Drawer
A sheet that slides in from an edge, wrapping the Base UI Drawer. Everything the Dialog does (modal, focus trapped, Escape closes) plus swipe dismissal and a grab handle. Right and left are side panels; bottom is the mobile sheet.
Playground
Every control maps to a prop. On a touch screen, drag the handle down to dismiss; the Code tab always shows the markup for exactly what you've configured.
Variants
One prop, three shapes: right and left are full-height panels capped at 24rem wide, bottom is a full-width sheet capped at 85dvh, and the grab handle and dismiss swipe follow the edge.
States
Closed, opening, open, swiping, and closing: the trigger carries data-popup-open while the sheet is up, a finger on the handle puts the popup into data-swiping and pauses its transition so it tracks the drag, and letting go past the threshold plays the exit; hover and focus are the trigger button's own.
Styling states
The popup carries data-side always and data-swiping while a finger drags it, plus the usual data-starting-style/data-ending-style transitions. The swipe offset arrives through --drawer-swipe-movement-y (or -x for side panels), which the shipped transform already reads, so restyling a side keeps the swipe intact:
.ub-drawer-popup[data-side="right"] {
width: min(28rem, calc(100vw - 3rem));
}
.ub-drawer-popup[data-swiping]::before {
background: var(--ub-accent);
}Props
The Dialog's parts and props, plus side and the swipe settings on the root. The essentials:
| Prop | Type | Default | Description |
|---|---|---|---|
DrawerRoot.open | boolean | โ | Controlled state. Pair with onOpenChange (also defaultOpen). |
DrawerRoot.modal | boolean | "trap-focus" | true | Dims and inerts the page behind, like the Dialog. |
DrawerRoot.side | "right" | "left" | "bottom" | "right" | Which edge the sheet slides in from; the dismiss swipe matches. |
DrawerRoot.disablePointerDismissal | boolean | false | Ignore outside clicks, for sheets that shouldn't be lost. |
DrawerTrigger.render | ReactElement | โ | The real control that opens the drawer, usually a Bones Button. |
DrawerClose.render | ReactElement | โ | A button that closes the sheet, usually a Bones Button. |
One quirk: the dismiss swipe follows side (a right panel swipes right). Pass swipeDirection to override it.
Agent instructions
Working with a coding agent? Paste this into its context, or point it at llms.txt for the whole library.
DrawerRoot, DrawerTrigger, DrawerContent, DrawerTitle, DrawerDescription, DrawerClose, from @usebones/react.
- Structure: DrawerRoot wraps DrawerTrigger + DrawerContent; put DrawerTitle (names the sheet), DrawerDescription, and the body inside. DrawerClose closes it.
- Attach trigger and close buttons to real controls via render={<Button ... />}.
- side on the root: "right" (default) | "left" (full-height panels up to 24rem wide, shrinking on narrow screens) | "bottom" (full-width mobile sheet, scrolls past 85dvh). The dismiss swipe matches the side (swipeDirection overrides); modal with focus trap and Escape everywhere.
- Restyle in CSS via .ub-drawer-popup, [data-side], .ub-drawer-backdrop, [data-swiping], [data-starting-style]/[data-ending-style], [data-popup-open] on the trigger. Tokens only.