Skip to content

Overlays Pattern

Choose the right overlay (Dropdown, Dialog, Sheet) with production UX rules and accessible defaults.

Decision matrix (Dropdown vs Dialog vs Sheet)

Overlays are not “UI choices”. They are interaction contracts.

Use this matrix:

OverlayBest forAvoid when
DropdownMenuQuick actions on an existing item (row actions, small choices)Multi-step flows, forms, confirmation, long content
DialogFocused task that blocks the page (create/edit, confirm destructive, short forms)Advanced filtering, long scrollable content, navigation-like panels
SheetProgressive disclosure and side workflows (filters, details panel, navigation on mobile)Destructive confirmations, small 2-choice decisions

Rule of thumb

  • Dropdown = micro actions
  • Dialog = focused task
  • Sheet = side workflow

Anatomy & UX rules

Focus & keyboard

  • Trap focus in Dialog / Sheet.
  • Return focus to the trigger on close.
  • Support Escape to close (except critical confirmations where Escape is allowed but state remains safe).

Scroll lock

  • Dialog / Sheet should lock page scroll while open.
  • Content inside the overlay should scroll, not the page.

Stacking

  • Avoid nesting overlays (Dropdown → Dialog is OK, but keep it rare).
  • Ensure portal stacking is correct (Dropdown above tables, Dialog above everything).

Close semantics

  • Prefer explicit buttons: Cancel / Save
  • Don’t auto-close on submit errors.
  • Destructive actions require explicit confirmation (Dialog).

Common flows

“Create item” (Dialog)

Use Dialog when the user starts a task and must complete or cancel it.


“Filter + advanced options” (Sheet)

Use Sheet when filters are progressive and the user may tweak options while keeping context.


“Row actions” (Dropdown)

Use DropdownMenu for quick row actions. For destructive actions, open a Dialog from the menu.

Tip: destructive actions should open a confirmation Dialog.


Accessibility checklist

Before shipping overlays, verify:

  • Dialog / Sheet includes a Title (visible or VisuallyHidden)
  • Focus is trapped and restored correctly
  • Escape closes (and does not lose data silently)
  • Triggers have clear labels (aria-label when icon-only)
  • Dropdown items are fully keyboard reachable

Dialog example

<Dialog>
  <DialogTrigger asChild>
    <Button>Create</Button>
  </DialogTrigger>

  <DialogContent>
    <DialogHeader>
      <DialogTitle>Create project</DialogTitle>
      <DialogDescription>
        Give it a name and status.
      </DialogDescription>
    </DialogHeader>
    {/* form */}
  </DialogContent>
</Dialog>

Sheet example

<Sheet>
  <SheetTrigger asChild>
    <Button variant="outline">Filters</Button>
  </SheetTrigger>

  <SheetContent>
    <SheetHeader>
      <SheetTitle>Filters</SheetTitle>
      <SheetDescription>
        Narrow results and apply advanced options.
      </SheetDescription>
    </SheetHeader>
    {/* filters */}
  </SheetContent>
</Sheet>