Docs
Dialog

Dialog

A window overlaid on either the primary window or another dialog window, rendering the content underneath inert.

Installation

npx shadcn@latest add dialog

Usage

import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogHeader,
  DialogTitle,
  DialogTrigger,
} from "@/components/ui/dialog"
<Dialog>
  <DialogTrigger>Open</DialogTrigger>
  <DialogContent>
    <DialogHeader>
      <DialogTitle>Are you absolutely sure?</DialogTitle>
      <DialogDescription>
        This action cannot be undone. This will permanently delete your account
        and remove your data from our servers.
      </DialogDescription>
    </DialogHeader>
  </DialogContent>
</Dialog>

Examples

Custom close button

Notes

To activate the Dialog component from within a Context Menu or Dropdown Menu, you must encase the Context Menu or Dropdown Menu component in the Dialog component. For more information, refer to the linked issue here.

<Dialog>
  <ContextMenu>
    <ContextMenuTrigger>Right click</ContextMenuTrigger>
    <ContextMenuContent>
      <ContextMenuItem>Open</ContextMenuItem>
      <ContextMenuItem>Download</ContextMenuItem>
      <DialogTrigger asChild>
        <ContextMenuItem>
          <span>Delete</span>
        </ContextMenuItem>
      </DialogTrigger>
    </ContextMenuContent>
  </ContextMenu>
  <DialogContent>
    <DialogHeader>
      <DialogTitle>Are you absolutely sure?</DialogTitle>
      <DialogDescription>
        This action cannot be undone. Are you sure you want to permanently
        delete this file from our servers?
      </DialogDescription>
    </DialogHeader>
    <DialogFooter>
      <Button type="submit">Confirm</Button>
    </DialogFooter>
  </DialogContent>
</Dialog>

This approach is not feasible if you plan to activate different types of dialogs from the same Context Menu or Dropdown Menu. In such cases, it is recommended to colocate the dialogs and control their visibility by making them controlled components. To achieve this, you can use the following utility hook, as suggested here:

"use client"
 
import { useRef, useState } from "react"
 
function useDialog<TriggerRef extends HTMLElement>() {
  const [isOpen, setIsOpen] = useState(false)
  const triggerRef = useRef<TriggerRef>(null)
 
  function trigger() {
    setIsOpen(true)
  }
 
  function dismiss() {
    setIsOpen(false)
    triggerRef.current?.focus()
  }
 
  return {
    triggerProps: {
      ref: triggerRef,
      onClick: trigger,
    },
    dialogProps: {
      open: isOpen,
      onOpenChange: (open: boolean) => {
        if (open) trigger()
        else dismiss()
      },
    },
    trigger,
    dismiss,
  }
}

The useDialog hook allows you to spread the necessary props to both the trigger, which toggles the Dialog, and the Dialog itself, making it a controlled component. Additionally, to ensure accessibility, the hook will attempt to focus the trigger (such as a button) when the Dialog closes.

const editDialog = useDialog<HTMLDivElement>()
const deleteDialog = useDialog<HTMLDivElement>()
 
<>
  <DropdownMenu>
    <DropdownMenuTrigger asChild>
      <Button variant="ghost" className="h-8 w-8 p-0">
        Actions
      </Button>
    </DropdownMenuTrigger>
    <DropdownMenuContent align="end">
      <DropdownMenuItem {...editDialog.triggerProps}>
        Edit
      </DropdownMenuItem>
      <DropdownMenuItem {...deleteDialog.triggerProps}>
        Delete
      </DropdownMenuItem>
    </DropdownMenuContent>
  </DropdownMenu>
  <Dialog {...editDialog.dialogProps}>
    {/* ... */}
  </Dialog>
  <Dialog {...deleteDialog.dialogProps}>
    {/* ... */}
  </Dialog>
</>