Nexus DSdocs

Dialog

Installation

npm install @radix-ui/react-dialog@^1.1.15 @tabler/icons-react@^3.36.1 class-variance-authority@^0.7.1 clsx@^2.1.1 tailwind-merge@^3.4.0
components/overlay-layout/overlay-layout.ts
lib/icons.ts
lib/utils.ts
components/dialog/dialog.tsx
components/dialog/index.ts

The code below also uses:

npm install @radix-ui/react-slot@^1.2.4
components/button-group/button-group-context.ts
components/spinner/index.ts
components/spinner/spinner.tsx
components/button/button.tsx
components/button/index.ts

Code

'use client';

import { Button } from '@/components/button/button';
import {
  Dialog,
  DialogBody,
  DialogClose,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
  DialogTrigger,
} from '@/components/dialog/dialog';

export default function DialogDemo() {
  return (
    <Dialog>
      <DialogTrigger asChild>
        <Button variant="outline">Archive project</Button>
      </DialogTrigger>
      <DialogContent>
        <DialogHeader>
          <DialogTitle>Archive project?</DialogTitle>
          <DialogDescription>
            Archived projects are hidden from the sidebar and stop sending
            notifications.
          </DialogDescription>
        </DialogHeader>
        <DialogBody>
          <p className="nx:typography-body-default nx:text-foreground">
            You can restore the project from workspace settings at any time.
          </p>
        </DialogBody>
        <DialogFooter>
          <DialogClose asChild>
            <Button variant="outline">Cancel</Button>
          </DialogClose>
          <DialogClose asChild>
            <Button>Archive</Button>
          </DialogClose>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  );
}

Props

Dialog

Dialog adds no props of its own; it accepts the props of the element or primitive it wraps.

DialogBody

DialogBody adds no props of its own; it accepts the props of the element or primitive it wraps.

DialogClose

DialogClose adds no props of its own; it accepts the props of the element or primitive it wraps.

DialogContent

DialogContent props
PropTypeDefaultDescription
bodyReactNodeNo default documented

Optional body content rendered in a generated DialogBody before children. Ignored when a composed DialogBody child is present.

descriptionReactNodeNo default documented

Optional description for a prop-driven header, rendered before children. Ignored when a composed DialogHeader child is present.

showCloseButtonbooleantrue

Whether to show the close button in the top-right corner.

titleReactNodeNo default documented

Optional title for a prop-driven header, rendered before children. Ignored when a composed DialogHeader child is present. Detection covers a direct child or a Fragment-wrapped header; a header nested inside another element is not detected.

DialogDescription

DialogDescription adds no props of its own; it accepts the props of the element or primitive it wraps.

DialogFooter adds no props of its own; it accepts the props of the element or primitive it wraps.

DialogHeader

DialogHeader adds no props of its own; it accepts the props of the element or primitive it wraps.

DialogOverlay

DialogOverlay adds no props of its own; it accepts the props of the element or primitive it wraps.

DialogPortal

DialogPortal adds no props of its own; it accepts the props of the element or primitive it wraps.

DialogTitle

DialogTitle adds no props of its own; it accepts the props of the element or primitive it wraps.

DialogTrigger

DialogTrigger adds no props of its own; it accepts the props of the element or primitive it wraps.

Examples

With form

Display name: Ada Lovelace

This example also needs:

npm install @radix-ui/react-label@^2.1.7
lib/field-size.ts
components/input/index.ts
components/input/input.tsx
components/label/index.ts
components/label/label.tsx
'use client';

import { type FormEvent, useState } from 'react';

import { Button } from '@/components/button/button';
import {
  Dialog,
  DialogBody,
  DialogClose,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
  DialogTrigger,
} from '@/components/dialog/dialog';
import { Input } from '@/components/input/input';
import { Label } from '@/components/label/label';

export default function DialogWithForm() {
  const [open, setOpen] = useState(false);
  const [name, setName] = useState('Ada Lovelace');

  function handleSubmit(event: FormEvent<HTMLFormElement>) {
    event.preventDefault();
    const data = new FormData(event.currentTarget);
    setName(String(data.get('name')));
    setOpen(false);
  }

  return (
    <div className="nx:flex nx:flex-col nx:items-center nx:gap-3">
      <Dialog open={open} onOpenChange={setOpen}>
        <DialogTrigger asChild>
          <Button variant="outline">Edit profile</Button>
        </DialogTrigger>
        <DialogContent>
          <DialogHeader>
            <DialogTitle>Edit profile</DialogTitle>
            <DialogDescription>
              Change how your name appears to teammates.
            </DialogDescription>
          </DialogHeader>
          <DialogBody>
            <form
              id="dialog-with-form"
              className="nx:grid nx:gap-1.5"
              onSubmit={handleSubmit}
            >
              <Label htmlFor="dialog-with-form-name">Display name</Label>
              <Input
                id="dialog-with-form-name"
                name="name"
                defaultValue={name}
                required
              />
            </form>
          </DialogBody>
          <DialogFooter>
            <DialogClose asChild>
              <Button variant="outline">Cancel</Button>
            </DialogClose>
            <Button type="submit" form="dialog-with-form">
              Save changes
            </Button>
          </DialogFooter>
        </DialogContent>
      </Dialog>
      <p className="nx:typography-body-default nx:text-muted-foreground">
        Display name: {name}
      </p>
    </div>
  );
}

Prop driven

'use client';

import { Button } from '@/components/button/button';
import {
  Dialog,
  DialogClose,
  DialogContent,
  DialogFooter,
  DialogTrigger,
} from '@/components/dialog/dialog';

export default function DialogPropDriven() {
  return (
    <Dialog>
      <DialogTrigger asChild>
        <Button variant="outline">Invite teammate</Button>
      </DialogTrigger>
      <DialogContent
        title="Invite teammate"
        description="Send an invitation and assign their first workspace role."
        body={
          <p className="nx:typography-body-default nx:text-foreground">
            The invitation expires in seven days and can be revoked from team
            settings.
          </p>
        }
      >
        <DialogFooter>
          <DialogClose asChild>
            <Button variant="outline">Cancel</Button>
          </DialogClose>
          <DialogClose asChild>
            <Button>Send invite</Button>
          </DialogClose>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  );
}

Scrollable body

'use client';

import { Button } from '@/components/button/button';
import {
  Dialog,
  DialogBody,
  DialogClose,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
  DialogTrigger,
} from '@/components/dialog/dialog';

const SECTIONS = [
  'Your workspace data stays in the region you chose at sign-up.',
  'Admins can export or delete workspace data from settings at any time.',
  'Integrations only receive the scopes you approve when connecting them.',
  'Audit logs record every sign-in, permission change and export.',
  'Deleted projects are kept for thirty days before permanent removal.',
  'Billing contacts receive invoices and usage alerts by email.',
  'Single sign-on is enforced for every member once an admin enables it.',
  'Guests can only see the projects they are explicitly invited to.',
];

export default function DialogScrollableBody() {
  return (
    <Dialog>
      <DialogTrigger asChild>
        <Button variant="outline">Review terms</Button>
      </DialogTrigger>
      <DialogContent className="nx:h-80">
        <DialogHeader>
          <DialogTitle>Workspace terms</DialogTitle>
          <DialogDescription>
            The body scrolls while the header and footer stay in place.
          </DialogDescription>
        </DialogHeader>
        <DialogBody className="nx:space-y-4 nx:typography-body-default nx:text-foreground">
          {SECTIONS.map((section) => (
            <p key={section}>{section}</p>
          ))}
        </DialogBody>
        <DialogFooter>
          <DialogClose asChild>
            <Button variant="outline">Decline</Button>
          </DialogClose>
          <DialogClose asChild>
            <Button>Accept</Button>
          </DialogClose>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  );
}