Skip to content
Highlight

ComponentsUpdated Sep 9, 2026

Dialog

A modal window. Content bundles the portal, backdrop, popup, and close button; Header, Body, and Footer lay out the inside. Trigger and Close are Buttons, so they take the same variants.

Import

Source: packages/ui/src/components/dialog

tsx
import { Dialog } from '@highlight/ui'
tsx
import { Dialog } from '@highlight/ui'

export default function DialogBasic() {
  return (
    <Dialog>
      <Dialog.Trigger>View notifications</Dialog.Trigger>
      <Dialog.Content>
        <Dialog.Header>
          <Dialog.Title>Notifications</Dialog.Title>
          <Dialog.Description>You are all caught up.</Dialog.Description>
        </Dialog.Header>
        <Dialog.Body>
          <p className="text-foreground/70 text-sm">Every notification has been reviewed. New ones will appear here.</p>
        </Dialog.Body>
        <Dialog.Footer>
          <Dialog.Close>Done</Dialog.Close>
        </Dialog.Footer>
      </Dialog.Content>
    </Dialog>
  )
}

Controlled

open and onOpenChange, with the trigger elsewhere.

tsx
import { useState } from 'react'
import { Button, Dialog } from '@highlight/ui'

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

  return (
    <div className="flex items-center gap-3">
      <Button variant="secondary" onClick={() => setOpen(true)}>
        Open from outside
      </Button>
      <Dialog open={open} onOpenChange={setOpen}>
        <Dialog.Content>
          <Dialog.Header>
            <Dialog.Title>Controlled</Dialog.Title>
            <Dialog.Description>Open state lives in React.</Dialog.Description>
          </Dialog.Header>
          <Dialog.Footer>
            <Button variant="secondary" onClick={() => setOpen(false)}>
              Close programmatically
            </Button>
          </Dialog.Footer>
        </Dialog.Content>
      </Dialog>
    </div>
  )
}

Destructive and blocking

showClose hides the corner button; disableEscape keeps Escape and the backdrop from closing.

tsx
import { Dialog } from '@highlight/ui'

export default function DialogVariants() {
  return (
    <div className="flex flex-wrap items-center justify-center gap-3">
      <Dialog>
        <Dialog.Trigger variant="danger">Delete thread</Dialog.Trigger>
        <Dialog.Content showClose={false}>
          <Dialog.Header>
            <Dialog.Title>Delete this thread?</Dialog.Title>
            <Dialog.Description>Messages and attachments are removed for everyone.</Dialog.Description>
          </Dialog.Header>
          <Dialog.Footer>
            <Dialog.Close variant="secondary">Cancel</Dialog.Close>
            <Dialog.Close variant="danger">Delete</Dialog.Close>
          </Dialog.Footer>
        </Dialog.Content>
      </Dialog>

      <Dialog disableEscape>
        <Dialog.Trigger variant="bold">Finish setup</Dialog.Trigger>
        <Dialog.Content showClose={false}>
          <Dialog.Header>
            <Dialog.Title>One more step</Dialog.Title>
            <Dialog.Description>Escape and the backdrop are disabled; pick an action to continue.</Dialog.Description>
          </Dialog.Header>
          <Dialog.Footer>
            <Dialog.Close variant="secondary">Later</Dialog.Close>
            <Dialog.Close variant="primary">Connect calendar</Dialog.Close>
          </Dialog.Footer>
        </Dialog.Content>
      </Dialog>
    </div>
  )
}
Open from a text link
tsx
import { Dialog } from '@highlight/ui'

export default function DialogCustomTrigger() {
  return (
    <Dialog>
      <Dialog.Trigger
        nativeButton={false}
        render={
          <span className="text-foreground/70 hover:text-foreground cursor-pointer text-sm underline underline-offset-4">
            Open from a text link
          </span>
        }
      />
      <Dialog.Content>
        <Dialog.Header>
          <Dialog.Title>Custom trigger</Dialog.Title>
          <Dialog.Description>
            Pass render to make any element the trigger. Set nativeButton to false when it is not a button.
          </Dialog.Description>
        </Dialog.Header>
      </Dialog.Content>
    </Dialog>
  )
}

Parts

Compound sub-components. Every part accepts className and forwards the rest of its props to Base UI.

Dialog.Trigger
A Button that opens the dialog. Takes variant, size, shape, or a render element.
Dialog.Content
Portal + Backdrop + Popup + close button. showClose, backdropClassName, initialFocus.
Dialog.Header / Body / Footer
Layout slots. Body scrolls when content overflows.
Dialog.Title / Description
Accessible name and description.
Dialog.Close
A Button that closes the dialog.
Dialog.Portal / Backdrop / Popup
Low-level parts when Content is not enough.

Props

Props added on top of the underlying element or Base UI part.

PropTypeDefaultDescription
disableEscapebooleanfalseOnly explicit Close clicks dismiss the dialog.