Drawer
A temporary side surface for focused work.
overlaysurface
Preview
Drawer preview
Live · keyboard ready
When to use
- Side panels for filters, settings, or record details that keep the page in context.
- Forms that are too long for a dialog but should not leave the current view.
- Panels from any edge, via side, with drag-to-dismiss on the header.
When not to use
- Use dialog for short decisions and confirmations.
- Use bottom-sheet for mobile-first sheets with snap points.
- Use popover for small anchored content that does not need a modal overlay.
shadcn CLI
Also installs (1)
- manicat-foundation
Manual install(+)
registry.jsonjson
{
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
"name": "drawer",
"type": "registry:ui",
"title": "Drawer",
"description": "A temporary side surface for focused work.",
"categories": [
"disclosure"
],
"dependencies": [
"@radix-ui/react-dialog",
"lucide-react",
"motion"
],
"registryDependencies": [
"https://manicat.viraza.net/r/manicat-foundation.json"
],
"files": [
{
"path": "registry/components/drawer/drawer.tsx",
"type": "registry:component",
"target": "@components/manicat/drawer/drawer.tsx"
},
{
"path": "registry/components/drawer/drawer.module.css",
"type": "registry:component",
"target": "@components/manicat/drawer/drawer.module.css"
}
],
"docs": "Docs and live preview: https://uiarc.dev/components/drawer",
"meta": {
"tier": "free",
"kind": "component",
"docs": "https://manicat.viraza.net/components/drawer",
"markdown": "https://uiarc.dev/components/drawer/markdown",
"tags": [
"overlay",
"surface"
]
}
}Usage
Install the component, then copy this into your app. Change the content and the props to fit your screen.
example.tsxtsx
"use client";
import { Drawer, DrawerClose, DrawerContent, DrawerTrigger } from "@/components/manicat/drawer/drawer";
import { Button } from "@/components/manicat/button/button";
export function DrawerExample() {
return (
<Drawer>
<DrawerTrigger asChild>
<Button variant="secondary">Open drawer</Button>
</DrawerTrigger>
<DrawerContent title="Filters" description="Narrow the list without leaving the page.">
<p className="text-sm text-(color:--text-muted)">
A drag on the edge closes the panel, handing its velocity to the exit spring.
</p>
<DrawerClose asChild>
<Button>Show results</Button>
</DrawerClose>
</DrawerContent>
</Drawer>
);
}
Examples
Real screens rather than isolated props. Switch to Code to copy a whole example into your project.
Filter drawer A side panel for secondary controls that keeps the list in view.
Filter drawer
A side panel for secondary controls that keeps the list in view. preview
Live · keyboard ready
filter-drawer-a-side-panel-for-secondary-controls-that-keeps-the-list-in-view-.tsxtsx
"use client";
import { Button } from "@/components/manicat/button/button";
import { Drawer, DrawerClose, DrawerContent, DrawerTrigger } from "@/components/manicat/drawer/drawer";
export function DrawerFiltersExample() {
return (
<Drawer>
<DrawerTrigger asChild>
<Button variant="secondary">Filters</Button>
</DrawerTrigger>
<DrawerContent title="Filters" description="Narrow the list without leaving the page.">
<p className="text-sm text-(color:--text-muted)">Status, owner and date range go here.</p>
<DrawerClose asChild>
<Button>Show results</Button>
</DrawerClose>
</DrawerContent>
</Drawer>
);
}
Keyboard interactions
| Keys | Action |
|---|---|
| Escape | Closes the drawer and returns focus to the trigger. |
| Tab / Shift+Tab | Cycles focus within the panel while it is open. |
Accessibility
- Radix Dialog provides role="dialog", aria-modal, focus trapping, and focus return.
- title and description are wired to Dialog.Title and Dialog.Description, so the panel is always named.
- The header close button is labelled "Close drawer"; dragging is optional and never the only way to close.
Motion
- The panel springs in from its edge and leaves faster on a tween; a drag past a third of the panel or a quick flick closes it and keeps the release velocity.
- Dragging the header away from the edge rubber-bands; the overlay fades in and out.
- Reduced motion disables dragging and replaces the slide with a short opacity fade.
Responsive behavior
- Left and right panels are min(30rem, 100vw minus a gutter) wide; below 40rem they grow to nearly full width with tighter padding.
- Top and bottom panels span the full width and cap at min(32rem, 100dvh).
- The header is the drag handle with touch-action none, so a touch drag moves the panel while the body still scrolls normally.
Performance
- The overlay uses a 4px backdrop blur, which can cost frames on low-end devices over busy pages.
- Drag runs on motion pan handlers with no React re-render per frame; the panel stays mounted only while sliding out.
Notes for AI
- Use for side panels with forms, filters, settings, or detail views that keep the page in context. Use dialog for short decisions and bottom-sheet for mobile-first sheets.
- Always compose Drawer as the root; under a bare Radix Dialog root the panel falls back to CSS keyframes and loses drag.
- Control open when the drawer must close after an async submit.