Drawer
Drawer slides in from any screen edge (top, bottom, left, right). It's powered by the Vaul library and supports drag-to-dismiss on mobile.
Interactive example#
Drawer · Live example
12 tasks per week
Open the drawer, change the weekly goal, then close it by dragging or using Done.
The code uses shared demo presentation helpers. Their complete source and styling are included in the LLM build context.
Import#
TSX
import {
Drawer,
DrawerTrigger,
DrawerContent,
DrawerHeader,
DrawerTitle,
DrawerDescription,
DrawerFooter,
DrawerClose,
} from '@clidey/ux';
Basic drawer (bottom, default)#
TSX
<Drawer>
<DrawerTrigger>
<Button variant="outline">Open drawer</Button>
</DrawerTrigger>
<DrawerContent>
<DrawerHeader>
<DrawerTitle>Move Goal</DrawerTitle>
<DrawerDescription>
Set your daily activity goal.
</DrawerDescription>
</DrawerHeader>
<div className="p-4">
{/* content */}
</div>
<DrawerFooter>
<Button>Submit</Button>
<DrawerClose>
<Button variant="outline">Cancel</Button>
</DrawerClose>
</DrawerFooter>
</DrawerContent>
</Drawer>
Directional variants#
Change the direction prop on the Drawer component to slide in from a different edge:
TSX
{/* From the right — good for detail panels */}
<Drawer direction="right">
<DrawerTrigger>
<Button>Open right panel</Button>
</DrawerTrigger>
<DrawerContent className="h-full w-80">
<DrawerHeader>
<DrawerTitle>Details</DrawerTitle>
</DrawerHeader>
<div className="p-4">{/* content */}</div>
</DrawerContent>
</Drawer>
{/* From the top */}
<Drawer direction="top">
<DrawerTrigger>
<Button>Open top panel</Button>
</DrawerTrigger>
<DrawerContent>
<DrawerHeader>
<DrawerTitle>Notification center</DrawerTitle>
</DrawerHeader>
</DrawerContent>
</Drawer>
Mobile bottom sheet#
The default bottom-positioned drawer works great as a mobile action sheet:
TSX
<Drawer>
<DrawerTrigger>
<Button size="icon" variant="ghost">
<MoreVertical className="size-4" />
</Button>
</DrawerTrigger>
<DrawerContent>
<DrawerHeader>
<DrawerTitle>Actions</DrawerTitle>
</DrawerHeader>
<div className="p-4 space-y-2">
<Button variant="ghost" className="w-full justify-start">
<Edit className="size-4 mr-2" /> Edit
</Button>
<Button variant="ghost" className="w-full justify-start">
<Share className="size-4 mr-2" /> Share
</Button>
<Button variant="ghost" className="w-full justify-start text-destructive">
<Trash className="size-4 mr-2" /> Delete
</Button>
</div>
</DrawerContent>
</Drawer>
Sub-components#
| Component | Description |
|---|---|
Drawer | Root. Accepts direction, open, onOpenChange. |
DrawerTrigger | Opens the drawer. Renders as its child automatically for ux components (Button, Badge, ...) and intrinsic elements, so no asChild is needed; pass asChild only for other ref-forwarding elements. See Triggers and asChild. |
DrawerPortal | Renders drawer outside the DOM tree. Used internally. |
DrawerOverlay | Backdrop layer using the --backdrop CSS token. Used internally. |
DrawerContent | The sliding panel. |
DrawerHeader | Top section with title/description. |
DrawerTitle | Accessible heading. |
DrawerDescription | Supporting text. |
DrawerFooter | Bottom section for actions. |
DrawerClose | Closes the drawer. Use asChild. |
Drawer Props
| Prop | Type | Default | Description |
|---|---|---|---|
direction | "top" | "bottom" | "left" | "right" | "bottom" | Side the drawer slides in from. |
open | boolean | — | Controlled open state. |
onOpenChange | (open: boolean) => void | — | Called when open state changes. |
shouldScaleBackground | boolean | false | Scales down the page background when open. |
Was this page helpful?
