Command Palette

Search for a command...

Dialog

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

import { styled } from "styled-system/jsx";
import { Button } from "@/components/ui/button";
import {
  Dialog,
  DialogClose,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
  DialogTrigger,
} from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";

export default function DialogDemo() {
  return (
    <Dialog>
      <form>
        <DialogTrigger asChild>
          <Button variant="outline">Open Dialog</Button>
        </DialogTrigger>
        <DialogContent css={{ sm: { maxW: "425px" } }}>
          <DialogHeader>
            <DialogTitle>Edit profile</DialogTitle>
            <DialogDescription>
              Make changes to your profile here. Click save when you&apos;re done.
            </DialogDescription>
          </DialogHeader>
          <styled.div css={{ display: "grid", gap: "4" }}>
            <styled.div css={{ display: "grid", gap: "3" }}>
              <Label htmlFor="name-1">Name</Label>
              <Input id="name-1" name="name" defaultValue="Pedro Duarte" />
            </styled.div>
            <styled.div css={{ display: "grid", gap: "3" }}>
              <Label htmlFor="username-1">Username</Label>
              <Input id="username-1" name="username" defaultValue="@peduarte" />
            </styled.div>
          </styled.div>
          <DialogFooter>
            <DialogClose asChild>
              <Button variant="outline">Cancel</Button>
            </DialogClose>
            <Button type="submit">Save changes</Button>
          </DialogFooter>
        </DialogContent>
      </form>
    </Dialog>
  );
}

Installation

npx nore-ui-cli@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

Dialog Custom Close Button

import { styled } from "styled-system/jsx";
import { Button } from "@/components/ui/button";
import {
  Dialog,
  DialogClose,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
  DialogTrigger,
} from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";

export default function DialogCloseButton() {
  return (
    <Dialog>
      <DialogTrigger asChild>
        <Button variant="outline">Share</Button>
      </DialogTrigger>
      <DialogContent css={{ sm: { maxW: "md" } }}>
        <DialogHeader>
          <DialogTitle>Share link</DialogTitle>
          <DialogDescription>Anyone who has this link will be able to view this.</DialogDescription>
        </DialogHeader>
        <styled.div css={{ display: "flex", alignItems: "center", gap: "2" }}>
          <styled.div css={{ display: "grid", flex: "1", gap: "2" }}>
            <Label htmlFor="link" css={{ srOnly: true }}>
              Link
            </Label>
            <Input id="link" defaultValue="https://ui.shadcn.com/docs/installation" readOnly />
          </styled.div>
        </styled.div>
        <DialogFooter css={{ sm: { justifyContent: "flex-start" } }}>
          <DialogClose asChild>
            <Button type="button" variant="secondary">
              Close
            </Button>
          </DialogClose>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  );
}

Responsive Dialog

You can make the dialog become a drawer on smaller screens by using this recipe.

preset/slot-recipes/dialog-responsive.ts
import { defineSlotRecipe } from "@pandacss/dev";

export const dialogSlotRecipe = defineSlotRecipe({
  className: "dialog",
  slots: [
    "root",
    "trigger",
    "portal",
    "overlay",
    "header",
    "title",
    "description",
    "content",
    "footer",
    "close",
  ],
  base: {
    overlay: {
      pos: "fixed",
      inset: "0",
      zIndex: "50",
      bg: "bg/80",
      _open: {
        animateIn: true,
        fadeIn: "0",
      },
      _closed: {
        animateOut: true,
        fadeOut: "0",
      },
    },

    header: {
      display: "flex",
      flexDir: "column",
      spaceY: "1.5",
    },

    title: {
      textStyle: "lg",
      fontWeight: "semibold",
      lineHeight: "none",
      letterSpacing: "tight",
    },

    description: {
      textStyle: "sm",
      color: "fg.muted",
    },

    content: {
      pos: "fixed",
      bottom: "0",
      left: "0",
      zIndex: "50",
      display: "grid",
      w: "full",
      maxW: "full",
      gap: "4",
      borderWidth: "1px",
      bg: "bg",
      p: "6",
      roundedTop: "md",
      shadow: "lg",
      _open: {
        animateIn: true,
        fadeIn: "0",
        slideInFromBottom: "100%",
      },
      _closed: {
        animateOut: true,
        fadeOut: "0",
        slideOutToBottom: "100%",
      },
      md: {
        maxW: "lg",
        top: "50%",
        left: "50%",
        transform: "translate(-50%, -50%)",
        bottom: "auto",
        rounded: "md",
        _open: {
          slideInFromBottom: "0",
          zoomIn: "95",
          slideInFromLeft: "50%",
          slideInFromTop: "48%",
        },
        _closed: {
          slideOutToBottom: "0",
          zoomOut: "95",
          slideOutToLeft: "50%",
          slideOutToTop: "48%",
        },
      },
    },

    footer: {
      display: "flex",
      flexDir: "column-reverse",
      sm: {
        flexDir: "row",
        justifyContent: "flex-end",
        gap: "2",
      },
    },
  },
});