Installation
CLI
npx shadcn@latest add "https://eo-n.vercel.app/r/dialog"
Manual
Copy and paste the following code into your project.
"use client";
import * as React from "react";
import { Dialog as DialogPrimitive } from "@base-ui-components/react/dialog";
import { XIcon } from "lucide-react";
import { cn } from "@/lib/utils";
function Dialog({
...props
}: React.ComponentProps<typeof DialogPrimitive.Root>) {
return <DialogPrimitive.Root data-slot="dialog" {...props} />;
}
function DialogTrigger({
...props
}: React.ComponentProps<typeof DialogPrimitive.Trigger>) {
return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />;
}
function DialogPortal({
...props
}: React.ComponentProps<typeof DialogPrimitive.Portal>) {
return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />;
}
function DialogBackdrop({
className,
...props
}: React.ComponentProps<typeof DialogPrimitive.Backdrop>) {
return (
<DialogPrimitive.Backdrop
data-slot="dialog-backdrop"
className={cn(
"fixed inset-0 z-50 bg-black/50 backdrop-blur-[1.5px] transition-opacity duration-150 ease-out data-[ending-style]:opacity-0 data-[starting-style]:opacity-0",
className
)}
{...props}
/>
);
}
interface DialogContentProps
extends React.ComponentProps<typeof DialogPrimitive.Popup> {
hideCloseIcon?: boolean;
}
function DialogContent({
className,
children,
hideCloseIcon = false,
...props
}: DialogContentProps) {
return (
<DialogPortal>
<DialogBackdrop />
<DialogPrimitive.Popup
data-slot="dialog-content"
className={cn(
"bg-background fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg transition-all duration-150 ease-out outline-none sm:max-w-lg",
"top-[calc(50%+1rem*var(--nested-dialogs))] scale-[calc(1-0.05*var(--nested-dialogs))] data-[ending-style]:top-[50.25%] data-[ending-style]:scale-95 data-[ending-style]:opacity-0 data-[starting-style]:top-[50.25%] data-[starting-style]:scale-95 data-[starting-style]:opacity-0",
"data-[nested-dialog-open]:after:absolute data-[nested-dialog-open]:after:inset-0 data-[nested-dialog-open]:after:rounded-[inherit] data-[nested-dialog-open]:after:bg-black/5 data-[nested-dialog-open]:after:backdrop-blur-[1.5px]",
className
)}
{...props}
>
{children}
{!hideCloseIcon && (
<DialogPrimitive.Close className="ring-offset-background focus:ring-ring absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4">
<XIcon />
<span className="sr-only">Close</span>
</DialogPrimitive.Close>
)}
</DialogPrimitive.Popup>
</DialogPortal>
);
}
function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="dialog-header"
className={cn("flex flex-col gap-2 text-center sm:text-left", className)}
{...props}
/>
);
}
function DialogFooter({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="dialog-footer"
className={cn(
"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
className
)}
{...props}
/>
);
}
function DialogTitle({
className,
...props
}: React.ComponentProps<typeof DialogPrimitive.Title>) {
return (
<DialogPrimitive.Title
data-slot="dialog-title"
className={cn("text-lg leading-none font-semibold", className)}
{...props}
/>
);
}
function DialogDescription({
className,
...props
}: React.ComponentProps<typeof DialogPrimitive.Description>) {
return (
<DialogPrimitive.Description
data-slot="dialog-description"
className={cn("text-muted-foreground text-sm", className)}
{...props}
/>
);
}
function DialogClose({
...props
}: React.ComponentProps<typeof DialogPrimitive.Close>) {
return <DialogPrimitive.Close data-slot="dialog-close" {...props} />;
}
export {
Dialog,
DialogClose,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogBackdrop,
DialogPortal,
DialogTitle,
DialogTrigger,
};
Update the import paths to match your project setup.
Usage
Import all parts and piece them together.
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
<Dialog>
<DialogTrigger>Open</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Are you sure you want to proceed?</DialogTitle>
<DialogDescription>
This action may have permanent effects. Please confirm if you want to
continue.
</DialogDescription>
</DialogHeader>
</DialogContent>
</Dialog>
Examples
With Custom Close Button
Without Close Icon
Nested Dialogs
Open On Menu
In order to open a dialog using a menu, control the dialog state and open it imperatively using the onClick
handler on the menu item.
Return Focus Properly
Make sure to also use the dialog’s finalFocus
prop to return focus back
to the menu trigger.
import * as React from "react";
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "../ui/dialog";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuPositioner,
DropdownMenuTrigger,
} from "../ui/dropdown-menu";
export default function DropdownMenuRadio() {
const menuTriggerRef = React.useRef<HTMLButtonElement>(null);
const [dialogOpen, setDialogOpen] = React.useState(false);
return (
<React.Fragment>
<DropdownMenu>
{/* Set the trigger ref */}
<DropdownMenuTrigger ref={menuTriggerRef}>Open</DropdownMenuTrigger>
<DropdownMenuPositioner>
<DropdownMenuContent>
{/* Open the dialog when the menu item is clicked */}
<DropdownMenuItem onClick={() => setDialogOpen(true)}>
Open dialog
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenuPositioner>
</DropdownMenu>
{/* Control the dialog state */}
<Dialog open={dialogOpen} onOpenChange={setDialogOpen}>
{/* Return focus to the menu trigger when the dialog is closed */}
<DialogContent finalFocus={menuTriggerRef}>
<DialogHeader>
<DialogTitle>Are you sure you want to proceed?</DialogTitle>
<DialogDescription>
This action may have permanent effects. Please confirm if you want
to continue.
</DialogDescription>
</DialogHeader>
</DialogContent>
</Dialog>
</React.Fragment>
);
}
API Reference
Content
Contains content to be rendered in the open dialog.
Prop | Type | Default |
---|---|---|
hideCloseIcon | boolean | false |