Components/Drawer

Drawer

Bottom-anchored panel with a drag-to-dismiss gesture. Designed for mobile-first interactions where a centered modal would be cramped. The grab handle at the top is a real touch target — drag it down past 120px (configurable) to dismiss, or release to snap back.


Installation

npx ajaxui add drawer

Action sheet

Native-feeling list of options. Each row is a button with an icon + label. Cancel sits in the footer.

Form drawer

Long inline forms benefit from the full viewport width compared to a centered modal.

Notes

Anatomy

<Drawer>                  // open/close state owner
  <DrawerTrigger/>        // any clickable becomes the trigger via asChild
  <DrawerContent>         // the sheet — captures the drag gesture
    <DrawerHeader>
      <DrawerTitle/>
      <DrawerDescription/>
    </DrawerHeader>
    <DrawerBody/>         // scrollable region
    <DrawerFooter/>       // action row
  </DrawerContent>
</Drawer>

Drag mechanics

  • Pointer-down anywhere in the top grab region captures the gesture.
  • Drag down follows the cursor 1:1 and fades the backdrop proportionally.
  • Drag up is rubber-banded (capped at −16px) — only dismissal is committed.
  • Release past the threshold dismisses; otherwise snaps back with a 200ms ease.
  • Tap-outside still closes, and Escape works via the focus trap.

Props

PropTypeDefaultDescription
openbooleanControlled open state.
defaultOpenbooleanfalseUncontrolled initial state.
onOpenChange(open: boolean) => voidFires whenever the drawer opens or closes.
noDragboolean (on Content)falseDisable the drag-to-dismiss gesture.
dismissThresholdnumber (on Content)120Pixels of downward drag past which release dismisses.
handleReactNode (on Content)Custom handle in place of the default pill.