Skip to content

Dialog ​

A modal dialog, as a compound component on Base UI's dialog. It is for a question or a form of a few fields. For a single question with an answer typed in, use PromptDialog, which already has the layout.

tsx
import { Dialog } from '@basmilius/react-ui';
tsx
import { useState } from 'react';
import { Button, Dialog } from '@basmilius/react-ui';

export default function DialogDemo() {
    const [open, setOpen] = useState(false);

    return (
        <Dialog.Root open={open} onOpenChange={setOpen}>
            <Dialog.Trigger render={<Button variant="danger-outline" />}>Remove branch</Dialog.Trigger>
            <Dialog.Popup size="sm">
                <Dialog.Title>Remove the branch?</Dialog.Title>
                <Dialog.Description className="mt-1">It has two commits nothing else has.</Dialog.Description>
                <Dialog.Text size="xs" className="mt-2">
                    A removed branch can be restored from the reflog for 90 days.
                </Dialog.Text>
                <Dialog.Footer>
                    <Dialog.Close render={<Button />}>Cancel</Dialog.Close>
                    <Button variant="danger" onClick={() => setOpen(false)}>
                        Remove
                    </Button>
                </Dialog.Footer>
            </Dialog.Popup>
        </Dialog.Root>
    );
}

Parts ​

PartWhat it is
Dialog.RootHolds the open state: open and onOpenChange, or defaultOpen.
Dialog.TriggerThe button that opens it.
Dialog.PopupThe portal, the backdrop and the popup in one part.
Dialog.TitleThe heading, which is also the dialog's accessible name. size="lg" for a dialog with a header of its own rather than a question.
Dialog.DescriptionThe sentence under the title, which a screen reader reads as the dialog's description. It carries no margin.
Dialog.TextA further line in the description's style that is not the description: a warning, a note under a choice.
Dialog.FooterThe buttons at the foot, with the main action last and so on the right.
Dialog.CloseA button that closes the dialog. Hand it a Button through render.

Size ​

size="sm" is 420 pixels wide with 20 pixels of padding, the one width every question dialog shares. Without a size the popup is as wide as its className says, and brings no padding.

A dialog over a dialog ​

A dialog that opens while another is up stacks over it by itself. Its backdrop dims the first dialog too, and the first one stops taking clicks. nested forces the stacked layers for a dialog that always opens over another. Menus and selects inside either dialog still float above both.

tsx
import { Button, Dialog } from '@basmilius/react-ui';

export default function DialogNested() {
    return (
        <Dialog.Root>
            <Dialog.Trigger render={<Button variant="secondary" />}>Edit profile</Dialog.Trigger>
            <Dialog.Popup className="w-[480px] p-5">
                <Dialog.Title size="lg">Profile</Dialog.Title>
                <Dialog.Description className="mt-1">A dialog opened from inside another one stacks over it and dims it.</Dialog.Description>
                <Dialog.Footer>
                    <Dialog.Root>
                        <Dialog.Trigger render={<Button variant="danger-outline" />}>Delete account</Dialog.Trigger>
                        <Dialog.Popup size="sm">
                            <Dialog.Title>Delete the account?</Dialog.Title>
                            <Dialog.Description className="mt-1">Everything in it goes with it.</Dialog.Description>
                            <Dialog.Footer>
                                <Dialog.Close render={<Button />}>Keep it</Dialog.Close>
                                <Dialog.Close render={<Button variant="danger" />}>Delete</Dialog.Close>
                            </Dialog.Footer>
                        </Dialog.Popup>
                    </Dialog.Root>
                    <Dialog.Close render={<Button variant="primary" />}>Done</Dialog.Close>
                </Dialog.Footer>
            </Dialog.Popup>
        </Dialog.Root>
    );
}

Dialog.Popup takes Base UI's popup props, plus:

PropTypeDefault
size'sm'
nestedbooleanfalseAlways over another dialog.
backdropbooleantrueThe dim behind the dialog.
backdropClassNamestringSuch as lightbox-backdrop, the heavier dim a picture needs.
keepMountedbooleanKeeps the popup in the document while it is closed.

Dialog.Title and Dialog.Description take a size, and Dialog.Text and Dialog.Footer take render.

Keyboard and focus ​

Opening the dialog moves focus into it, and Tab stays inside until it closes. Escape and a click on the backdrop close it through onOpenChange(false). Closing returns focus to the trigger.

Types ​

DialogPopupProps, DialogTitleProps, DialogDescriptionProps, DialogTextProps and DialogFooterProps are exported types.