Drawer

Bottom-sheet drawer built on vaul. Mobile-native feel: drag-to-dismiss, snap points, body-scale-on-open. Use for quick mobile actions, filters, pickers.

Installation

pnpm
pnpm dlx @hex-core/cli add drawer

API Reference

PropTypeDefaultDescription
open
booleanControlled open state
defaultOpen
booleanfalseDefault open state
onOpenChange
functionCallback when open state changes: (open: boolean) => void
shouldScaleBackground
booleantrueScale the <body> element when the drawer opens (creates depth)
snapPoints
objectArray of snap positions ('40%', 400, '100%') — defines resting heights the user can snap to
activeSnapPoint
objectControlled active snap point value (matches one entry in snapPoints)
closeThreshold
number0.25Fraction of height the user must drag down to close (0..1)
dismissible
booleantrueAllow drag-to-dismiss and outside-click-to-dismiss

AI Guidance

When to use

Use on mobile-first or mobile-primary UX when a native app-like bottom sheet matters. Good for filters, quick pickers, confirm-then-do flows, or anywhere a user expects drag-to-dismiss.

When not to use

Don't use on desktop-primary UIs (use Dialog or Sheet). Don't use for side navigation (use Sheet). Don't use for transient info (use Popover or Tooltip). Don't use when you must prevent dismissal — drawers invite drag-down.

Common mistakes

  • Forgetting DrawerTitle — vaul/Radix warn and screen readers announce an unnamed dialog
  • Placing long forms inside a drawer without snap points — content gets cramped
  • Disabling shouldScaleBackground when the background context-cue matters for UX
  • Wrapping DrawerContent in Portal yourself — DrawerContent already portals via DrawerPortal

Accessibility

vaul delegates to Radix Dialog: focus trap, Escape to close, aria-labelledby/describedby wired to DrawerTitle/Description. The top handle is decorative (aria-hidden).

Related components

Token budget: 700