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>
</>