Dialog

A modal dialog built on Radix UI that traps focus, locks scroll, and dismisses on backdrop click or Escape.

Default

tsx

import { Dialog, Button, CloseButton } from "@hummingbirdui/react";

export default function DialogDefault() {
  return (
    <Dialog>
      <Dialog.Trigger asChild>
        <Button variant="outline">Open dialog</Button>
      </Dialog.Trigger>
      <Dialog.Content>
        <Dialog.Header>
          <Dialog.Title>Modal title</Dialog.Title>
          <Dialog.Close asChild>
            <CloseButton />
          </Dialog.Close>
        </Dialog.Header>
        <Dialog.Body>
          <Dialog.Description>
            Click the backdrop, press Escape, or use a close button to dismiss
            it.
          </Dialog.Description>
        </Dialog.Body>
        <Dialog.Footer>
          <Dialog.Close asChild>
            <Button color="secondary" variant="subtle" className="me-2">
              Cancel
            </Button>
          </Dialog.Close>
          <Dialog.Close asChild>
            <Button>Save changes</Button>
          </Dialog.Close>
        </Dialog.Footer>
      </Dialog.Content>
    </Dialog>
  );
}

Centered

Setting centered on Dialog.Content vertically centers the dialog in the viewport.

tsx

import { Dialog, Button, CloseButton } from "@hummingbirdui/react";

export default function DialogCentered() {
  return (
    <Dialog>
      <Dialog.Trigger asChild>
        <Button variant="outline">Centered dialog</Button>
      </Dialog.Trigger>
      <Dialog.Content centered>
        <Dialog.Header>
          <Dialog.Title>Centered</Dialog.Title>
          <Dialog.Close asChild>
            <CloseButton />
          </Dialog.Close>
        </Dialog.Header>
        <Dialog.Body>
          <Dialog.Description>
            This dialog is centered vertically in the viewport.
          </Dialog.Description>
        </Dialog.Body>
      </Dialog.Content>
    </Dialog>
  );
}

Sizes

The size prop sets the dialog width across "sm", "md", "lg", and "xl".

tsx

import { Dialog, Button, CloseButton } from "@hummingbirdui/react";

const sizes = ["sm", "md", "lg", "xl"] as const;

export default function DialogSizes() {
  return (
    <div className="flex flex-wrap gap-2 justify-center">
      {sizes.map((size) => (
        <Dialog key={size}>
          <Dialog.Trigger asChild>
            <Button variant="outline">{size.toUpperCase()} dialog</Button>
          </Dialog.Trigger>
          <Dialog.Content size={size}>
            <Dialog.Header>
              <Dialog.Title>{size.toUpperCase()} dialog</Dialog.Title>
              <Dialog.Close asChild>
                <CloseButton />
              </Dialog.Close>
            </Dialog.Header>
            <Dialog.Body>
              <Dialog.Description>
                A dialog using <code>size=&quot;{size}&quot;</code>.
              </Dialog.Description>
            </Dialog.Body>
          </Dialog.Content>
        </Dialog>
      ))}
    </div>
  );
}

Scrollable

Setting scrollable keeps the header and footer fixed while a long body scrolls inside the dialog.

tsx

import { Dialog, Button, CloseButton } from "@hummingbirdui/react";

export default function DialogScrollable() {
  return (
    <Dialog>
      <Dialog.Trigger asChild>
        <Button variant="outline">Scrollable dialog</Button>
      </Dialog.Trigger>
      <Dialog.Content scrollable centered>
        <Dialog.Header>
          <Dialog.Title>Terms of service</Dialog.Title>
          <Dialog.Close asChild>
            <CloseButton />
          </Dialog.Close>
        </Dialog.Header>
        <Dialog.Body className="max-h-72">
          <p>Scroll to read all of the content below.</p>
          <p>
            Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum
            nec odio. Praesent libero. Sed cursus ante dapibus diam. Sed nisi.
            Nulla quis sem at nibh elementum imperdiet.
          </p>
          <p>
            Duis sagittis ipsum. Praesent mauris. Fusce nec tellus sed augue
            semper porta. Mauris massa. Vestibulum lacinia arcu eget nulla.
          </p>
          <p>
            Class aptent taciti sociosqu ad litora torquent per conubia nostra,
            per inceptos himenaeos. Curabitur sodales ligula in libero.
          </p>
          <p>
            Sed dignissim lacinia nunc. Curabitur tortor. Pellentesque nibh.
            Aenean quam. In scelerisque sem at dolor. Maecenas mattis.
          </p>
        </Dialog.Body>
        <Dialog.Footer>
          <Dialog.Close asChild>
            <Button>Got it</Button>
          </Dialog.Close>
        </Dialog.Footer>
      </Dialog.Content>
    </Dialog>
  );
}

Fullscreen

Setting fullscreen expands the dialog to fill the entire viewport.

tsx

import { Dialog, Button, CloseButton } from "@hummingbirdui/react";

export default function DialogFullscreen() {
  return (
    <Dialog>
      <Dialog.Trigger asChild>
        <Button variant="outline">Fullscreen dialog</Button>
      </Dialog.Trigger>
      <Dialog.Content fullscreen>
        <Dialog.Header>
          <Dialog.Title>Fullscreen</Dialog.Title>
          <Dialog.Close asChild>
            <CloseButton />
          </Dialog.Close>
        </Dialog.Header>
        <Dialog.Body>
          <Dialog.Description>
            This dialog fills the entire viewport using <code>fullscreen</code>.
          </Dialog.Description>
        </Dialog.Body>
      </Dialog.Content>
    </Dialog>
  );
}

Controlled

Passing open and onOpenChange to the Dialog root drives its open state externally.

tsx

import * as React from "react";
import { Dialog, Button, CloseButton } from "@hummingbirdui/react";

export default function DialogControlled() {
  const [open, setOpen] = React.useState(false);

  return (
    <>
      <Button onClick={() => setOpen(true)} variant="outline">
        Open controlled dialog
      </Button>
      <Dialog open={open} onOpenChange={setOpen}>
        <Dialog.Content>
          <Dialog.Header>
            <Dialog.Title>Controlled dialog</Dialog.Title>
            <Dialog.Close asChild>
              <CloseButton />
            </Dialog.Close>
          </Dialog.Header>
          <Dialog.Body>
            <Dialog.Description>
              Open state is driven by <code>open</code> and{" "}
              <code>onOpenChange</code> on the <code>Dialog</code> root.
            </Dialog.Description>
          </Dialog.Body>
          <Dialog.Footer>
            <Button onClick={() => setOpen(false)}>Close</Button>
          </Dialog.Footer>
        </Dialog.Content>
      </Dialog>
    </>
  );
}

API Reference

Built on the Radix UI Dialog primitive. Each part forwards all props to its Radix counterpart, except Dialog.Header, Dialog.Body, and Dialog.Footer, which are Hummingbird-specific layout parts rendered as plain div elements.

Dialog

Contains all the parts of a dialog.

PropTypeDefault
defaultOpenboolean
openboolean
onOpenChangefunction
modalbooleantrue

Dialog.Trigger

The button that opens the dialog.

PropTypeDefault
asChildbooleanfalse
classNamestring

Data attributeValues
[data-state]"open" | "closed"

Dialog.Portal

When used, portals your overlay and content parts into the body. Dialog.Content renders this part automatically, so you rarely need to use it directly.

PropTypeDefault
forceMountboolean
containerHTMLElementdocument.body

Dialog.Overlay

A layer that covers the inert portion of the view when the dialog is open. Dialog.Content renders this part automatically; use its overlayProps prop to customize it.

PropTypeDefault
asChildbooleanfalse
forceMountboolean
classNamestring

Data attributeValues
[data-state]"open" | "closed"

Dialog.Content

Contains content to be rendered in the open dialog. Always renders its own Dialog.Portal and Dialog.Overlay, and wraps the content in the modal and modal-dialog elements.

PropTypeDefault
asChildbooleanfalse
forceMountboolean
onOpenAutoFocusfunction
onCloseAutoFocusfunction
onEscapeKeyDownfunction
onPointerDownOutsidefunction
onInteractOutsidefunction
sizeenum"md"
centeredbooleanfalse
scrollablebooleanfalse
fullscreenenumfalse
dialogClassNamestring
containerHTMLElementdocument.body
portalPropsobject
overlayPropsobject
classNamestring

Data attributeValues
[data-state]"open" | "closed"

Dialog.Header

Hummingbird-specific layout part. Renders a div with the modal-header class for the top area of the dialog.

Dialog.Title

An accessible title to be announced when the dialog is opened. Renders an h6 element by default.

PropTypeDefault
asChildbooleanfalse
classNamestring

Dialog.Description

An optional accessible description to be announced when the dialog is opened.

PropTypeDefault
asChildbooleanfalse
classNamestring

Dialog.Body

Hummingbird-specific layout part. Renders a div with the modal-body class for the main content of the dialog.

Dialog.Footer

Hummingbird-specific layout part. Renders a div with the modal-footer class, typically containing action buttons.

Dialog.Close

The button that closes the dialog.

PropTypeDefault
asChildbooleanfalse
classNamestring

Styling

Hummingbird React dialog is styled entirely through Hummingbird's utility classes and CSS variables.

  • See the full list of available dialog classes in the Class overview.
  • Visit the CSS Variables documentation to explore all available variables.