Dialog

Displays a modal window to capture user input or confirm actions.

import Image from "next/image";
 
import { Button } from "@/components/ui/button";
import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
  DialogTrigger,
} from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
 
export function DialogDemo() {
  return (
    <Dialog>
      <DialogTrigger render={<Button variant="outline">Open dialog</Button>} />
      <DialogContent className="sm:max-w-[425px]">
        <DialogHeader>
          <DialogTitle>Edit Content</DialogTitle>
          <DialogDescription>
            Update the details below and click save to apply changes.
          </DialogDescription>
        </DialogHeader>
        <div className="relative aspect-video w-full overflow-hidden">
          <Image
            src="/images/dialog-demo-image.jpg"
            alt="dialog-placeholder"
            priority
            className="rounded-md"
            objectFit="cover"
            fill
          />
        </div>
        <div className="flex flex-col gap-4">
          <div className="flex flex-col gap-3">
            <Label htmlFor="title">Title</Label>
            <Input id="title" value="Sample Title" className="col-span-3" />
          </div>
          <div className="flex flex-col gap-3">
            <Label htmlFor="description">Description</Label>
            <Input
              id="description"
              value="Sample Description"
              className="col-span-3"
            />
          </div>
        </div>
        <DialogFooter>
          <Button type="submit">Save changes</Button>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  );
}

Installation

npx shadcn@latest add "https://eo-n.vercel.app/r/dialog"

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

import { Button } from "@/components/ui/button";
import {
  Dialog,
  DialogClose,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
  DialogTrigger,
} from "@/components/ui/dialog";
 
export function DialogCustomCloseButton() {
  return (
    <Dialog>
      <DialogTrigger render={<Button variant="outline">Open dialog</Button>} />
      <DialogContent>
        <DialogHeader>
          <DialogTitle>Confirm Your Action</DialogTitle>
          <DialogDescription>
            This operation will update your settings and may affect your current
            configuration. Please confirm if you wish to proceed.
          </DialogDescription>
        </DialogHeader>
        <DialogFooter>
          <DialogClose render={<Button variant="outline">Cancel</Button>} />
          <Button variant="default">Confirm</Button>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  );
}

Without Close Icon

import Link from "next/link";
import { Radius } from "lucide-react";
 
import { Button } from "@/components/ui/button";
import { Checkbox } from "@/components/ui/checkbox";
import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
  DialogTrigger,
} from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
 
export function DialogWithNoXButton() {
  return (
    <Dialog>
      <DialogTrigger render={<Button variant="default">Sign in</Button>} />
      <DialogContent hideCloseIcon className="sm:max-w-[415px]">
        <DialogHeader className="items-center px-10">
          <div className="mb-2 grid place-items-center rounded-full border-2 p-3">
            <Radius />
          </div>
          <DialogTitle>Welcome back</DialogTitle>
          <DialogDescription className="text-center">
            Enter your credentials to login to your account.
          </DialogDescription>
        </DialogHeader>
        <div className="flex flex-col gap-4">
          <div className="flex flex-col gap-3">
            <Label htmlFor="email">Email</Label>
            <Input
              id="email"
              autoComplete="off"
              placeholder="eo-n@gmail.com"
              className="col-span-3"
            />
          </div>
          <div className="flex flex-col gap-3">
            <Label htmlFor="password">Password</Label>
            <Input
              id="password"
              autoComplete="off"
              placeholder="*********"
              className="col-span-3"
            />
          </div>
          <div className="flex items-center justify-between">
            <div className="flex items-center space-x-2">
              <Checkbox id="remember-me" />
              <label
                htmlFor="remember-me"
                className="text-sm leading-none font-medium peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
              >
                Remember me
              </label>
            </div>
            <Link
              prefetch
              href="/"
              className="text-sm text-blue-400 underline underline-offset-4"
            >
              Forgot password?
            </Link>
          </div>
        </div>
        <DialogFooter className="gap-3 sm:flex-col">
          <Button variant="default" className="w-full">
            Sign in
          </Button>
          <div className="before:bg-border after:bg-border flex items-center gap-3 before:h-px before:flex-1 after:h-px after:flex-1">
            <span className="text-muted-foreground text-xs">Or</span>
          </div>
          <Button variant="outline" className="w-full [&_svg]:size-4">
            <svg
              xmlns="http://www.w3.org/2000/svg"
              viewBox="0 0 24 24"
              className="size-2"
            >
              <path
                d="M12.48 10.92v3.28h7.84c-.24 1.84-.853 3.187-1.787 4.133-1.147 1.147-2.933 2.4-6.053 2.4-4.827 0-8.6-3.893-8.6-8.72s3.773-8.72 8.6-8.72c2.6 0 4.507 1.027 5.907 2.347l2.307-2.307C18.747 1.44 16.133 0 12.48 0 5.867 0 .307 5.387.307 12s5.56 12 12.173 12c3.573 0 6.267-1.173 8.373-3.36 2.16-2.16 2.84-5.213 2.84-7.667 0-.76-.053-1.467-.173-2.053H12.48z"
                fill="currentColor"
              />
            </svg>
            Login with Google
          </Button>
          <div className="text-muted-foreground text-center text-sm">
            Don&apos;t have an account?{" "}
            <Link
              prefetch
              href="/"
              className="text-blue-400 underline underline-offset-4"
            >
              Sign up
            </Link>
          </div>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  );
}

Nested Dialogs

import { Button } from "@/components/ui/button";
import {
  Dialog,
  DialogClose,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
  DialogTrigger,
} from "@/components/ui/dialog";
 
export function DialogNested() {
  return (
    <Dialog>
      <DialogTrigger render={<Button variant="outline">Settings</Button>} />
      <DialogContent className="sm:max-w-[425px]">
        <DialogHeader>
          <DialogTitle>Account Settings</DialogTitle>
          <DialogDescription>
            Manage your account preferences.
          </DialogDescription>
        </DialogHeader>
        <div className="py-2">
          <div className="flex items-center justify-between">
            <span>Email notifications</span>
            <span>Enabled</span>
          </div>
        </div>
        <DialogFooter>
          <Dialog>
            <DialogTrigger render={<Button variant="ghost">Advanced</Button>} />
            <DialogContent className="sm:max-w-[425px]">
              <DialogHeader>
                <DialogTitle>Advanced Settings</DialogTitle>
                <DialogDescription>
                  Configure advanced options.
                </DialogDescription>
              </DialogHeader>
              <div className="py-2">
                <div className="flex items-center justify-between">
                  <span>Developer mode</span>
                  <span>Disabled</span>
                </div>
              </div>
              <DialogFooter>
                <DialogClose render={<Button>Save</Button>} />
              </DialogFooter>
            </DialogContent>
          </Dialog>
          <DialogClose render={<Button>Save</Button>} />
        </DialogFooter>
      </DialogContent>
    </Dialog>
  );
}

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.

PropTypeDefault
hideCloseIcon?
boolean
false