default.tsx
1'use client';23import { Button } from '@/components/ui/button';4import { Drawer } from '@/components/ui/drawer';56const notifications = [7 { from: 'Jane', message: 'Your report is ready for download.' },8 { from: 'System Alert', message: 'Scheduled maintenance at 3:00 AM UTC.' },9 { from: 'John', message: 'Please review the updated project plan.' },10] as const;1112export function Default() {13 return (14 <Drawer>15 <Drawer.Trigger variant="outline">Open Drawer</Drawer.Trigger>16 <Drawer.Content side="right">17 <div className="flex flex-col gap-4">18 <h2 className="text-lg font-semibold">Notifications</h2>19 <p className="text-muted-foreground text-sm">20 You have 3 new messages and 1 system alert. Review them below.21 </p>2223 <div className="space-y-2 text-sm">24 {notifications.map((n) => (25 <div key={n.from} className="bg-accent/10 rounded-md border p-3">26 <strong>27 {n.from === 'System Alert' ? 'System Alert:' : `Message from ${n.from}:`}28 </strong>{' '}29 {n.message}30 </div>31 ))}32 </div>3334 <div className="mt-4 flex justify-end gap-2">35 <Drawer.Close>Dismiss All</Drawer.Close>36 <Button>View Details</Button>37 </div>38 </div>39 </Drawer.Content>40 </Drawer>41 );42}
Installation
pnpm dlx nachui add drawerAnatomy
1import { Drawer } from '@/components/ui/drawer';
1<Drawer>2 <Drawer.Trigger>3 <Button>Open Drawer</Button>4 </Drawer.Trigger>5 <Drawer.Content>6 <Drawer.Header>7 <Drawer.Title>Drawer Title</Drawer.Title>8 <Drawer.Description>This is a drawer component.</Drawer.Description>9 </Drawer.Header>10 <div>Your content here</div>11 </Drawer.Content>12</Drawer>
Features
- Multiple sides - Slide from bottom, top, left, or right
- Drag to close - Swipe to dismiss in any direction
- Responsive - Adapts to different screen sizes
- Smooth animations - Spring-based slide transitions
- Focus trap - Keyboard navigation stays within the drawer
- Body scroll lock - Prevents background scrolling when open
Variants
Positions
positions.tsx
1'use client';23import { Drawer } from '@/components/ui/drawer';45const DRAWER_SIDES = ['top', 'right', 'bottom', 'left'] as const;67export function Positions() {8 return (9 <div className="grid grid-cols-2 gap-4">10 {DRAWER_SIDES.map((side) => (11 <Drawer key={side}>12 <Drawer.Trigger className="w-full capitalize" variant="outline">13 {side}14 </Drawer.Trigger>15 <Drawer.Content side={side}>16 <Drawer.Header>17 <Drawer.Title>Edit profile</Drawer.Title>18 <Drawer.Description>19 Make changes to your profile here. Click save when you're done.20 </Drawer.Description>21 </Drawer.Header>22 <div className="py-4">23 <p className="text-muted-foreground text-sm">24 This drawer is appearing from the {side}.25 </p>26 </div>27 <div className="flex justify-end">28 <Drawer.Close>Save changes</Drawer.Close>29 </div>30 </Drawer.Content>31 </Drawer>32 ))}33 </div>34 );35}
Form
form.tsx
1'use client';23import { Drawer } from '@/components/ui/drawer';4import { Input } from '@/components/ui/input';5import { Label } from '@/components/ui/label';67const fields = [8 { id: 'name', label: 'Name', defaultValue: 'Ignacio Figueroa', type: 'text' },9 { id: 'username', label: 'Username', defaultValue: '@figueroaignacio', type: 'text' },10] as const;1112export function Form() {13 return (14 <Drawer>15 <Drawer.Trigger variant="outline">Edit Profile</Drawer.Trigger>16 <Drawer.Content side="bottom">17 <Drawer.Header>18 <Drawer.Title>Edit profile</Drawer.Title>19 <Drawer.Description>20 Make changes to your profile here. Click save when you're done.21 </Drawer.Description>22 </Drawer.Header>23 <div className="grid gap-4 py-4">24 {fields.map((field) => (25 <div key={field.id} className="grid grid-cols-4 items-center gap-4">26 <Label htmlFor={field.id} className="text-right">27 {field.label}28 </Label>29 <Input30 id={field.id}31 type={field.type}32 defaultValue={field.defaultValue}33 className="col-span-3"34 />35 </div>36 ))}37 </div>38 <div className="flex justify-end">39 <Drawer.Close>Save changes</Drawer.Close>40 </div>41 </Drawer.Content>42 </Drawer>43 );44}
API Reference
Drawer
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | - | Drawer trigger and content |
defaultOpen | boolean | false | Initial open state |
open | boolean | - | Controlled open state |
onOpenChange | (open: boolean) => void | - | Callback when open changes |
Drawer.Trigger
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | - | Trigger button content |
className | string | - | Additional CSS classes |
variant | ButtonProps['variant'] | - | Button variant |
size | ButtonProps['size'] | - | Button size |
Drawer.Content
| Prop | Type | Default | Description |
|---|---|---|---|
side | 'bottom' | 'top' | 'left' | 'right' | 'bottom' | Side to slide from |
showDragHandle | boolean | true | Show drag handle for closing |
className | string | - | Additional CSS classes |
Drawer.Header
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | - | Additional CSS classes |
Drawer.Title
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | - | Additional CSS classes |
Drawer.Description
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | - | Additional CSS classes |
Drawer.Close
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | - | Clickable content |
Found something to improve?
Notice a bug, typo, or missing detail on this page? Help us make the documentation better by opening a GitHub issue.