Skip to content
SevenUI
Esc
navigateopen⌘Jpreview
Sign in
On this page

Dialog

A window overlaid on either the primary window or another dialog window, rendering the content underneath inert, built on the Base UI Dialog primitive.

"use client";

import { Button } from "@/registry/base/ui/button";
import {
  Dialog,
  DialogClose,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
  DialogTrigger,
} from "@/registry/base/ui/dialog";
import { Input } from "@/registry/base/ui/input";
import { Label } from "@/registry/base/ui/label";

export default function DialogDemo() {
  return (
    <Dialog>
      <DialogTrigger render={<Button variant="outline">Edit profile</Button>} />
      <DialogContent className="sm:max-w-106">
        <DialogHeader>
          <DialogTitle>Edit profile</DialogTitle>
          <DialogDescription>
            Make changes to your profile here. Click save when you&apos;re
            done.
          </DialogDescription>
        </DialogHeader>
        <div className="grid gap-4">
          <div className="grid gap-2">
            <Label htmlFor="name">Name</Label>
            <Input id="name" defaultValue="Pedro Duarte" />
          </div>
          <div className="grid gap-2">
            <Label htmlFor="username">Username</Label>
            <Input id="username" defaultValue="@peduarte" />
          </div>
        </div>
        <DialogFooter>
          <DialogClose render={<Button variant="outline">Cancel</Button>} />
          <Button type="submit">Save changes</Button>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  );
}

Installation

npx shadcn@latest add https://sevenui.dev/r/dialog.json

Usage

"use client";

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>
      <DialogTrigger render={<Button variant="outline">Edit profile</Button>} />
      <DialogContent className="sm:max-w-106">
        <DialogHeader>
          <DialogTitle>Edit profile</DialogTitle>
          <DialogDescription>
            Make changes to your profile here. Click save when you're done.
          </DialogDescription>
        </DialogHeader>
        <div className="grid gap-4">
          <div className="grid gap-2">
            <Label htmlFor="name">Name</Label>
            <Input id="name" defaultValue="Pedro Duarte" />
          </div>
          <div className="grid gap-2">
            <Label htmlFor="username">Username</Label>
            <Input id="username" defaultValue="@peduarte" />
          </div>
        </div>
        <DialogFooter>
          <DialogClose render={<Button variant="outline">Cancel</Button>} />
          <Button type="submit">Save changes</Button>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  );
}

API reference

Dialog

Extends the Base UI Dialog Root — all its props apply. Base UI has no dismissible prop; use disablePointerDismissal instead.

Prop Type Default
open / defaultOpen boolean false
onOpenChange (open, eventDetails) => void
onOpenChangeComplete fires after animations finish
modal true | false | "trap-focus" true
disablePointerDismissal boolean — outside press keeps it open false
actionsRef ref with imperative close()/unmount()
handle Dialog.Handle — pairs with detached triggers
triggerId / defaultTriggerId string | null — which trigger opened it

Detached triggers (createHandle)

The primitive’s Dialog.createHandle() connects triggers and a dialog that are not co-located in the tree: create a handle, pass it to any number of DialogTriggers (optionally with a typed payload and id) and to the Dialog root. The root’s children can be a render function receiving { payload }, so one dialog renders trigger-specific content. Our wrappers pass these props straight through:

import { Dialog as DialogPrimitive } from "@base-ui/react/dialog";

const editDialog = DialogPrimitive.createHandle<{ id: string }>();

<DialogTrigger handle={editDialog} payload={{ id: "42" }} render={<Button>Edit</Button>} />
// ...anywhere else in the app:
<Dialog handle={editDialog}>{/* content */}</Dialog>

DialogContent

Content is centered by a Dialog.Viewport — long content scrolls the viewport, not the page. A dialog with another dialog open inside it exposes data-nested-dialog-open on its Popup and the --nested-dialogs CSS variable, useful for dimming or scaling background dialogs.

Prop Type Default
showCloseButton boolean — built-in ✕ true
initialFocus / finalFocus focus target on open/close

DialogFooter

Extends div — the muted footer bar.

Prop Type Default
showCloseButton boolean — appends an outline Close button false

DialogTrigger, DialogClose, DialogHeader, DialogTitle, DialogDescription, DialogOverlay, DialogPortal

Styled Base UI parts. Trigger (a button; also takes handle / payload / id for detached use) and Close accept the render prop for custom elements (e.g. render={<Button />}). DialogPortal takes container and keepMounted (default false); DialogOverlay is the Backdrop — nested dialogs hide the parent’s backdrop automatically (forceRender on the primitive overrides that). Title renders an <h2>, Description a <p> — keep both for accessible labeling, or render them sr-only.

Was this page helpful?