Drawer
A panel that slides in from any edge of the screen, with swipe-to-dismiss support.
CMS Demo
See how content editors configure drawers in a CMS:
Basic Usage
Installation
pnpm add @corew500/uiUsage
import {
Drawer,
DrawerTrigger,
DrawerContent,
DrawerHeader,
DrawerTitle,
DrawerDescription,
DrawerFooter,
DrawerClose,
} from "@mordecai-design-system/ui"<Drawer>
<DrawerTrigger>Open Drawer</DrawerTrigger>
<DrawerContent>
<DrawerHeader>
<DrawerTitle>Drawer Title</DrawerTitle>
<DrawerDescription>Description here</DrawerDescription>
</DrawerHeader>
<p>Content</p>
<DrawerFooter>
<Button>Submit</Button>
<DrawerClose>Cancel</DrawerClose>
</DrawerFooter>
</DrawerContent>
</Drawer>Examples
Directions
// Bottom (default)
<Drawer>
<DrawerContent>Slides from bottom</DrawerContent>
</Drawer>
// Right
<Drawer direction="right">
<DrawerContent>Slides from right</DrawerContent>
</Drawer>
// Left
<Drawer direction="left">
<DrawerContent>Slides from left</DrawerContent>
</Drawer>
// Top
<Drawer direction="top">
<DrawerContent>Slides from top</DrawerContent>
</Drawer>Controlled
const [open, setOpen] = useState(false)
<Drawer open={open} onOpenChange={setOpen}>
<DrawerTrigger>Open</DrawerTrigger>
<DrawerContent>
<Button onClick={() => setOpen(false)}>Close</Button>
</DrawerContent>
</Drawer>With Form
<DrawerContent>
<DrawerHeader>
<DrawerTitle>Edit Settings</DrawerTitle>
</DrawerHeader>
<form className="p-4 space-y-4">
<Input placeholder="Name" />
<Input type="email" placeholder="Email" />
</form>
<DrawerFooter>
<Button>Save</Button>
<DrawerClose>Cancel</DrawerClose>
</DrawerFooter>
</DrawerContent>Sub-components
| Component | Description |
|-----------|-------------|
| Drawer | Root component with direction control |
| DrawerTrigger | Button that opens the drawer |
| DrawerContent | The sliding panel |
| DrawerHeader | Header area |
| DrawerTitle | Main heading |
| DrawerDescription | Supporting text |
| DrawerFooter | Footer with actions |
| DrawerClose | Button to close |
| DrawerOverlay | Background overlay |
Props
Drawer
| Prop | Type | Default | Description |
|---|---|---|---|
| activeSnapPoint | enum | — | — |
| setActiveSnapPoint | (snapPoint: string | number) => void | — | — |
| open | enum | — | — |
| closeThreshold | number | 0.25 | Number between 0 and 1 that determines when the drawer should be closed. Example: threshold of 0.5 would close the drawer if the user swiped for 50% of the height of the drawer or more. |
| noBodyStyles | enum | — | When `true` the `body` doesn't get any styles assigned from Vaul |
| onOpenChange | (open: boolean) => void | — | — |
| shouldScaleBackground | enum | — | — |
| setBackgroundColorOnScale | enum | true | When `false` we don't change body's background color when the drawer is open. |
| scrollLockTimeout | number | 500ms | Duration for which the drawer is not draggable after scrolling content inside of the drawer. |
| fixed | enum | — | When `true`, don't move the drawer upwards if there's space, but rather only change it's height so it's fully scrollable when the keyboard is open |
| handleOnly | enum | false | When `true` only allows the drawer to be dragged by the `<Drawer.Handle />` component. |
| dismissible | enum | true | When `false` dragging, clicking outside, pressing esc, etc. will not close the drawer. Use this in comination with the `open` prop, otherwise you won't be able to open/close the drawer. |
| onDrag | (event: PointerEvent<HTMLDivElement>, percentageDragged: number) => void | — | — |
| onRelease | (event: PointerEvent<HTMLDivElement>, open: boolean) => void | — | — |
| modal | enum | true | When `false` it allows to interact with elements outside of the drawer without closing it. |
| nested | enum | — | — |
| onClose | () => void | — | — |
| direction | enum | 'bottom' | Direction of the drawer. Can be `top` or `bottom`, `left`, `right`. |
| defaultOpen | enum | false | Opened by default, skips initial enter animation. Still reacts to `open` state changes |
| disablePreventScroll | enum | false | When set to `true` prevents scrolling on the document body on mount, and restores it on unmount. |
| repositionInputs | enum | true when {@link snapPoints } is defined | When `true` Vaul will reposition inputs rather than scroll then into view if the keyboard is in the way. Setting it to `false` will fall back to the default browser behavior. |
| snapToSequentialPoint | enum | false | Disabled velocity based swiping for snap points. This means that a snap point won't be skipped even if the velocity is high enough. Useful if each snap point in a drawer is equally important. |
| container | HTMLElement | — | — |
| onAnimationEnd | (open: boolean) => void | — | Gets triggered after the open or close animation ends, it receives an `open` argument with the `open` state of the drawer by the time the function was triggered. Useful to revert any state changes for example. |
| preventScrollRestoration | enum | — | — |
| autoFocus | enum | — | — |
| snapPoints | (string | number)[] | — | Array of numbers from 0 to 100 that corresponds to % of the screen a given snap point should take up. Should go from least visible. Example `[0.2, 0.5, 0.8]`. You can also use px values, which doesn't take screen height into account. |
| fadeFromIndex | number | — | Index of a `snapPoint` from which the overlay fade should be applied. Defaults to the last snap point. |
DrawerContent
Inherits Vaul drawer content props.
Features
- Swipe to dismiss: Natural gesture support on touch devices
- Directional: Slides from any edge
- Accessible: Full keyboard and screen reader support
- Responsive: Great for mobile navigation patterns