Drawer
A bottom sheet with native drag-to-dismiss support. Built on vaul — designed for mobile-first, touch-friendly workflows. Automatically includes a drag handle and supports velocity-based snap behaviour.
Anatomy
DrawerContent automatically renders a drag handle bar at the top. DrawerHeader centers text on mobile and left-aligns on desktop. DrawerClose wraps any element to dismiss the drawer. DrawerFooter stacks buttons vertically on mobile and rows them on desktop.
drag handle auto-renderedvaul velocity-based dismissDrawerClose to dismiss
Examples
Basic info
Action sheet
Form drawer
Drawer vs Sheet
Both are panel overlays, but they serve different interaction models.
Drawer
- Drag-to-dismiss (vaul)
- Mobile-first, touch-optimised
- Bottom only (primary use case)
- No side directions built-in
Sheet
- All four sides (top/right/bottom/left)
- Desktop navigation and settings panels
- More predictable dismiss behaviour
- No drag handle or snap behaviour
Design Guidelines
Do
- Use for mobile-first flows and action sheets. Vaul's drag-to-dismiss matches native mobile patterns users already know.
- Keep content lightweight. Drawers work best for focused tasks — a handful of actions or a short form.
- Stack footer buttons vertically on mobile. DrawerFooter handles responsive stacking automatically.
Don't
- Don't use as a desktop navigation panel. Sheet's
side="left"is better suited for that — Drawer is optimised for bottom/mobile. - Don't fill with heavily scrollable content. Users expect drawers to be lightweight — long content should live on a dedicated page.
- Don't rely on programmatic open alone. Always include a visible dismiss path (
DrawerCloseor drag handle) so users aren't stranded.
Developer Reference
Accessibility
- Vaul manages
role="dialog", focus trapping, and ESC dismissal internally. - Drag handle is a visual affordance — include a
DrawerClosebutton for keyboard and screen reader users. - Velocity-based dismiss: a quick downward drag closes the drawer regardless of distance travelled.
DrawerHeadertext-aligns to center on mobile (sm:text-lefton desktop).
Usage
import {
Drawer,
DrawerTrigger,
DrawerContent,
DrawerHeader,
DrawerFooter,
DrawerTitle,
DrawerDescription,
DrawerClose,
} from "@/components/ui/drawer"
// Basic drawer
<Drawer>
<DrawerTrigger asChild>
<Button>Open</Button>
</DrawerTrigger>
<DrawerContent>
<DrawerHeader>
<DrawerTitle>Title</DrawerTitle>
<DrawerDescription>Supporting description.</DrawerDescription>
</DrawerHeader>
<div className="px-4 pb-2">{/* content */}</div>
<DrawerFooter>
<Button>Confirm</Button>
<DrawerClose asChild>
<Button variant="outline">Cancel</Button>
</DrawerClose>
</DrawerFooter>
</DrawerContent>
</Drawer>
// Controlled
const [open, setOpen] = useState(false)
<Drawer open={open} onOpenChange={setOpen}>...</Drawer>