Dialog

Loading...

Installation

Install the component via the CLI in one command.

npx shadcn@latest add @intentui/dialog

Composed components

When you install this component via the CLI, it automatically loads all composed components, so you don’t need to add them individually.

Manual installation

Use this approach if you prefer to install and wire up the component yourself instead of using the CLI.

npm install react-aria-components @heroicons/react cn

Anatomy

import { Button } from "@/components/ui/button"
import { Dialog, DialogBody, DialogClose, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog"
<DialogTrigger>
  <Button>Open Dialog</Button>
  <Dialog>
    <DialogHeader>
      <DialogTitle>Dialog Title</DialogTitle>
      <DialogDescription>Dialog Description</DialogDescription>
    </DialogHeader>
    <DialogBody>Dialog Body</DialogBody>
    <DialogFooter>
      <DialogClose>Close</DialogClose>
      <Button>Confirm</Button>
    </DialogFooter>
  </Dialog>
</DialogTrigger>

Examples

Alert dialog

Use role="alertdialog" for a critical message that requires an explicit response.

Loading...

Alert dialogs cannot be dismissed by clicking outside by default, and the close icon is hidden. Provide an explicit action such as Cancel.

<Dialog role="alertdialog" />

Controlled

You can control the dialog programmatically.

Loading...

Sizes

The dialog defaults to md. Available sizes are 2xs, xs, sm, md, lg, xl, 2xl, 3xl, 4xl, 5xl, and fullscreen.

Loading...

Dialog overlay styles

The overlay uses a subtle 1px blur by default. You can increase it or disable it entirely.

<DialogTrigger>
  <Button intent="outline">Rename</Button>
  <Dialog overlay={{ className: "backdrop-blur-2xl" }}>

If you want to turn it off, use backdrop-blur-none. If you do not need any blur at all, the recommended approach is to remove the blur class from the component defaults.

-"bg-background/15 backdrop-blur-[1px] motion-reduce:backdrop-blur-none",
+"bg-background/15",

Or, you can override it per instance from the component usage:

<DialogTrigger>
  <Button intent="outline">Rename</Button>
  <Dialog overlay={{ className: "backdrop-blur-none" }}>

Sticky

Use DialogBody for scrollable content while the header and footer stay visible.

Loading...

Nested

You can also nest dialogs. Try open it and confirm!

Loading...

This setup’s super flexible. If you skip adding DialogTitle and just drop a string as its child, it'll auto-render as the title. Like this:

<DialogHeader>
  Title
</DialogHeader>

Wanna customize more? Throw in props like title and description for a tailored header:

<DialogHeader title='Title' description='Description' />

Triggered by menu

You can also trigger the dialog by clicking on a menu item.

Loading...

It might be a good idea to extract the dialog into a separate component for better organization.

interface DialogActionProps {
  state: string | null
  onOpenChange: () => void
  actionType: { description: string; action: () => void; confirmText: string; title: string }
  disabled: boolean
}

const DialogAction = (props: DialogActionProps) => (
  <Dialog isOpen={props.state !== null} onOpenChange={props.onOpenChange}>
    <DialogHeader>
      <DialogTitle>{props.actionType?.title}</DialogTitle>
      <DialogDescription>{props.actionType?.description}</DialogDescription>
    </DialogHeader>
    <DialogFooter>
      <DialogClose>Cancel</DialogClose>
      <Button
        intent={props.state === "ban" ? "danger" : "primary"}
        className="min-w-24"
        isDisabled={props.disabled}
        onPress={props.actionType?.action}
      >
        {props.disabled ? <Loader variant="spin" /> : props.actionType?.confirmText}
      </Button>
    </DialogFooter>
  </Dialog>
)

Then you can use it like this.

<DialogAction
  state={state}
  onOpenChange={closeDialog}
  actionType={actionType(state)}
  disabled={loading}
/>

With that, now we can modify the actionType function to return the initial state.

const actionType = (t: string | null) => {
  const initialsState = {
    title: '',
    description: '',
    confirmText: '',
    action: () => {}
  }

  switch (t) {
    case 'delete': ...
    case 'ban': ...
    case 'restore': ...
    default:
      return initialsState
  }
}

Component API

DialogTrigger

DialogTrigger extends the React Aria Components <DialogTrigger> component.

Dialog

Dialog extends the React Aria Components <ModalOverlay> component.

classNamestring
isOpenboolean
onOpenChange(isOpen: boolean) => void
isDismissableboolean
Default: true, except for alertdialog
role"dialog" | "alertdialog"
Default: dialog
closeButtonboolean
Default: true
overlayPick<ModalOverlayProps, "className">
sizekeyof typeof sizes
Default: md

DialogBody

DialogBody extends the native <div> element.

classNamestring

DialogCloseIcon

DialogCloseIcon renders a React Aria Button with the close slot. Dialog renders it automatically when closeButton and isDismissable are enabled.

classNamestring
isDismissableboolean | undefined

DialogClose

DialogClose extends the Intent UI <Button> component.

classNamestring
intent"primary" | "secondary" | "warning" | "danger" | "success" | "outline" | "plain"
Default: plain

DialogDescription

DialogDescription renders a native <p> element.

classNamestring

DialogFooter

DialogFooter extends the native <div> element.

classNamestring

DialogHeader

DialogHeader extends the native <div> element.

classNamestring
descriptionstring
titlestring

DialogTitle

DialogTitle extends the React Aria Components <Heading> component.

classNamestring
Design