Drawer
A sidebar panel that slides in from any edge of the viewport for navigation, forms, or supplementary content.
Installation
npx vayu-ui-cli@latest add drawerUsage
Drawer Example
Anatomy
import { Drawer } from 'vayu-ui';
<Drawer side="right">
<Drawer.Trigger asChild>
<Button>Open</Button>
</Drawer.Trigger>
<Drawer.Overlay />
<Drawer.Content>
<Drawer.Header>
<Drawer.Title>Title</Drawer.Title>
<Drawer.Description>Description goes here.</Drawer.Description>
</Drawer.Header>
<div className="py-4">{/* Content */}</div>
<Drawer.Footer>
<Drawer.Close asChild>
<Button>Close</Button>
</Drawer.Close>
</Drawer.Footer>
</Drawer.Content>
</Drawer>;- Drawer — Root container. Manages open/close state and provides context to all subcomponents. Supports controlled and uncontrolled usage.
- Drawer.Trigger — Button that opens the drawer. Supports
asChildto merge props onto a custom element. - Drawer.Overlay — Backdrop behind the drawer. Clicking it dismisses the drawer by default.
- Drawer.Content — The slide-in panel with focus trapping and keyboard navigation. Use Drawer.Header for an in-flow close button.
- Drawer.Header — Container for
Drawer.TitleandDrawer.Description. - Drawer.Title — Accessible heading rendered as an
<h2>. Linked to the dialog viaaria-labelledby. - Drawer.Description — Accessible description rendered as a
<p>. Linked to the dialog viaaria-describedby. - Drawer.Footer — Container for action buttons, pinned to the bottom of the drawer.
- Drawer.Close — Button that closes the drawer. Supports
asChildfor custom elements. - Drawer.Portal — Portal wrapper for rendering the drawer outside the DOM tree.
Accessibility
- Keyboard Support:
Escape— Closes the drawer.Tab— Moves focus between interactive elements inside the drawer.Shift + Tab— Moves focus backwards through interactive elements.- Focus is trapped within the drawer when open (circular tab navigation).
- ARIA Attributes:
role="dialog"onDrawer.Content.aria-modal="true"onDrawer.Content.aria-labelledbyonDrawer.Contentlinks toDrawer.Title.aria-describedbyonDrawer.Contentlinks toDrawer.Description.aria-expandedonDrawer.Triggerreflects the open/closed state.aria-haspopup="dialog"onDrawer.Trigger.aria-hidden="true"onDrawer.Overlay.- Built-in close button has
aria-label="Close drawer".
- Focus Behavior:
- When the drawer opens, focus moves to the first interactive element inside
Drawer.Content. - Focus is trapped within the drawer until it closes.
- When the drawer closes, focus returns to the trigger element.
- Body scroll is locked while a modal drawer is open.
- When the drawer opens, focus moves to the first interactive element inside
Screen reader behavior
When a drawer opens, the screen reader announces the dialog (via role="dialog" and aria-modal="true") and reads the title (linked by aria-labelledby) and description (linked by aria-describedby). The overlay is hidden from the assistive technology tree (aria-hidden="true"). While the drawer is open, only content inside it is accessible to screen readers. When the drawer closes, focus returns to the trigger button and the previously visible content becomes accessible again.
Component Folder Structure
Drawer/
├── Drawer.tsx # Root component with context provider and state management
├── DrawerTrigger.tsx # Trigger button that opens the drawer
├── DrawerOverlay.tsx # Modal backdrop with dismiss on click
├── DrawerContent.tsx # Slide-in panel with focus trapping and keyboard navigation
├── DrawerHeader.tsx # Container for title and description
├── DrawerFooter.tsx # Container for action buttons
├── DrawerTitle.tsx # Accessible heading linked to dialog
├── DrawerDescription.tsx # Accessible description linked to dialog
├── DrawerClose.tsx # Button that closes the drawer
├── DrawerPortal.tsx # Portal wrapper for DOM rendering
├── types.ts # TypeScript type definitions
├── index.ts # Re-exports all components and types
└── README.md # Component usage referenceProps
Drawer (Root)
| Prop | Type | Default | Description |
|---|---|---|---|
open | boolean | — | Controlled open state. |
onOpenChange | (open: boolean) => void | — | Callback when open state changes. |
defaultOpen | boolean | false | Default open state for uncontrolled usage. |
side | "left" | "right" | "top" | "bottom" | "right" | Direction the drawer slides in from. |
modal | boolean | true | Whether to lock body scroll and show overlay. |
children | ReactNode | — | Drawer subcomponents. |
className | string | — | Additional CSS classes. |
Drawer.Trigger
| Prop | Type | Default | Description |
|---|---|---|---|
asChild | boolean | false | Merge props onto child element instead of rendering a button. |
className | string | — | Additional CSS classes. |
Drawer.Overlay
| Prop | Type | Default | Description |
|---|---|---|---|
dismissible | boolean | true | Whether clicking the overlay closes the drawer. |
className | string | — | Additional CSS classes. |
Drawer.Content
| Prop | Type | Default | Description |
|---|---|---|---|
trapFocus | boolean | true | Whether to trap focus within the drawer when open. |
className | string | — | Additional CSS classes. |
Drawer.Header
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | — | Additional CSS classes. |
Drawer.Footer
| 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 |
|---|---|---|---|
asChild | boolean | false | Merge props onto child element instead of rendering a button. |
className | string | — | Additional CSS classes. |
Drawer.Portal
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | — | Content to render inside the portal. |
Header close button
Drawer.Header includes an accessible X control in the header flex row. The old absolutely positioned close button in Drawer.Content has moved into the header. Include a header or compose Drawer.Close explicitly when using a custom layout.
<Drawer.Content>
<Drawer.Header closeLabel="Close navigation">
<Drawer.Title>Navigation</Drawer.Title>
<Drawer.Description>Choose a destination.</Drawer.Description>
</Drawer.Header>
</Drawer.Content>Use showClose={false} to hide the default header control. Drawer.Close asChild preserves the child's click handler and respects event.preventDefault().