Drawer
A modal side sheet that slides in from an edge. Same behaviour and parts as Dialog, with side and size props on Content.
Import
import { Drawer } from "@merid/react";<Drawer.Root>
<Drawer.Trigger asChild>
<Button variant="secondary">Open right</Button>
</Drawer.Trigger>
<Drawer.Content side="right">
<Drawer.Title>Filters</Drawer.Title>
<Drawer.Description>Narrow the list of invoices.</Drawer.Description>
<Drawer.Footer>
<Drawer.Close>Reset</Drawer.Close>
<Drawer.Close asChild>
<Button variant="primary">Apply</Button>
</Drawer.Close>
</Drawer.Footer>
</Drawer.Content>
</Drawer.Root>Anatomy
<Drawer.Root>
<Drawer.Trigger />
<Drawer.Content side="right">
<Drawer.Title />
<Drawer.Description />
<Drawer.Footer />
</Drawer.Content>
</Drawer.Root>Root, Trigger, Title, Description, Close and Footer are the Dialog parts. The Footer is pushed to the bottom of the sheet.
Examples
Sides
<Drawer.Content side="left">…</Drawer.Content>
<Drawer.Content side="right">…</Drawer.Content>Sizes
size sets the sheet width: sm 320px, md 420px (default), lg 560px, always capped to the viewport minus 48px.
<Drawer.Content size="sm">…</Drawer.Content>
<Drawer.Content size="md">…</Drawer.Content>
<Drawer.Content size="lg">…</Drawer.Content>Controlled
const [open, setOpen] = useState(false);
<Drawer.Root open={open} onOpenChange={setOpen}>
<Drawer.Content>…</Drawer.Content>
</Drawer.Root>API reference
Drawer.Root
| Prop | Type | Default | Description |
|---|---|---|---|
open | boolean | – | Controlled open state. |
defaultOpen | boolean | false | Initial open state when uncontrolled. |
onOpenChange | (open: boolean) => void | – | Called when the open state should change. |
children | ReactNode | – | Trigger, Content and anything else sharing this drawer's state. |
Drawer.Content
Accepts every <div> attribute except role.
| Prop | Type | Default | Description |
|---|---|---|---|
side | "left" | "right" | "right" | Edge the sheet slides in from. |
size | DrawerSize | "md" | Sheet width: `sm` 320px, `md` 420px, `lg` 560px, capped to the viewport minus 48px. `DrawerSize` is `"sm" | "md" | "lg"`. |
closeOnOutsidePress | boolean | true | Close when the backdrop is pressed. |
closeOnEscape | boolean | true | Close on Escape. |
initialFocus | RefObject<HTMLElement | null> | – | Element to focus when opened; defaults to [data-autofocus], then the first tabbable element. |
container | Element | null | document.body | Portal target. `undefined` uses `document.body`; `null` renders nothing until the target exists. |
showClose | boolean | true | Render the standard top-right icon close button automatically. Skipped while you render your own icon `Close`. |
closeLabel | string | "Close" | Accessible name of the automatic close button. |
ref | Ref<HTMLDivElement> | – | Forwarded ref to the drawer element. |
Trigger, Title, Description, Close and Footer take the same props as in Dialog.
Styling
| Class | Element |
|---|---|
.mrd-drawer__backdrop | Fixed backdrop; aligns the sheet to its side |
.mrd-drawer | The sheet: 320 / 420 / 560px by data-size, capped at 100vw - 48px, full height |
Both carry data-side="left" | "right" and data-state="open"; the sheet also carries data-size. Title, description, footer and close use the .mrd-dialog__* classes. Motion is removed under prefers-reduced-motion.
Accessibility
Follows the WAI-ARIA Dialog (Modal) pattern: role="dialog", aria-modal="true", focus trap, scroll lock and focus return.
| Key | Action |
|---|---|
| SpaceEnter | On the trigger, opens the drawer. |
| Tab | Moves focus forward, wrapping inside the drawer. |
| ShiftTab | Moves focus backwards, wrapping inside the drawer. |
| Esc | Closes the drawer and returns focus to the trigger. |
Guidelines
Do
Avoid
Related
- Dialog
- SidebarNav for persistent navigation.