Drawer
A floating panel that slides in from an edge of the viewport—filters, forms, and mobile-friendly sheets.
Installation
The component is exported from @by/experience-system. Add the package with your package manager:
pnpm add @by/experience-systemIn this monorepo, depend on the workspace package (for example via workspace:* or your catalog) so imports resolve to packages/experience-system.
Composition
Use the following composition to build a Drawer:
Drawer
├── DrawerTrigger (optional if controlled)
└── DrawerContent
├── DrawerHeader
│ ├── DrawerTitle
│ └── DrawerDescription (optional)
├── (body)
└── DrawerFooter (optional)
└── DrawerClose (optional)DrawerContent portals with overlay. Optional parts: DrawerHandle (drag affordance), DrawerPortal, DrawerOverlay when you need lower-level control. Built on Vaul; use direction, handleOnly, and data-vaul-no-drag per Vaul docs when content includes sliders or draggable regions.
Usage
import {
Button,
Drawer,
DrawerClose,
DrawerContent,
DrawerDescription,
DrawerFooter,
DrawerHeader,
DrawerTitle,
DrawerTrigger,
} from '@by/experience-system';Drawer and its parts are client components ('use client'). Wrap your tree in ThemeProvider so DrawerContent can resolve the portal container. Use it inside a Client Component or a dynamic import when using the Next.js App Router.
<Drawer>
<DrawerTrigger asChild>
<Button>Open</Button>
</DrawerTrigger>
<DrawerContent>
<DrawerHeader>
<DrawerTitle>Title</DrawerTitle>
<DrawerDescription>Optional description.</DrawerDescription>
</DrawerHeader>
<DrawerFooter>
<DrawerClose asChild>
<Button variant="outline">Close</Button>
</DrawerClose>
</DrawerFooter>
</DrawerContent>
</Drawer>When to use
- Providing access to locations within an application or MFE (for example application navigation).
- Context-sensitive actions and information for the page’s main content (for example filters, versioning).
When not to use
- Simple messages or actions—use Dialog instead.
Examples
The live preview at the top of this page uses drawer-usage (trigger + sheet shell).
Overview
A left drawer with handleOnly so dragging to dismiss is limited to the handle—useful when the sheet contains sliders or other draggable controls. Add data-vaul-no-drag on elements that must not initiate a sheet drag (see Vaul documentation).
Sides
Use the direction prop on Drawer for top, right, bottom (default), or left. Tune height on top/bottom sheets with className and the data-[vaul-drawer-direction=…] attributes when needed.
API Reference
Subsection titles name @by/experience-system exports and the Vaul part they wrap. Prop names, defaults, and roles follow the Vaul API Reference (Drawer here is Vaul’s Drawer.Root). For extra options on the root (for example shouldScaleBackground, closeThreshold, nested), see the DialogProps surface in the vaul package types.
Drawer
Root. Contains all parts of the drawer.
| Prop | Type | Default |
|---|---|---|
defaultOpen | boolean | — |
open | boolean | — |
onOpenChange | (open: boolean) => void | — |
modal | boolean | true |
container | HTMLElement | null | document.body |
direction | 'top' | 'right' | 'bottom' | 'left' | 'bottom' |
onAnimationEnd | (open: boolean) => void | — |
dismissible | boolean | true |
handleOnly | boolean | false |
repositionInputs | boolean | true |
Snap points
Additional Drawer props
| Prop | Type | Default |
|---|---|---|
snapPoints | (number | string)[] | — |
activeSnapPoint | number | string | null | — |
setActiveSnapPoint | (snapPoint: number | string | null) => void | — |
fadeFromIndex | number | — |
snapToSequentialPoint | boolean | false |
DrawerTrigger
The control that opens the drawer.
| Prop | Type | Default |
|---|---|---|
asChild | boolean | false |
DrawerPortal
When used, portals overlay and content into the target node. Forwards Radix Portal props (for example container, forceMount). In the default composition, DrawerContent already wraps DrawerPortal and sets container from ThemeProvider / useContainerElement() when present.
DrawerOverlay
Layer over the inert portion of the page while the drawer is open. Merges className with Experience System overlay styles. Included inside DrawerContent by default.
| Prop | Type | Default |
|---|---|---|
asChild | boolean | false |
DrawerContent
The sliding panel. Merges className. Composes DrawerPortal, DrawerOverlay, and Vaul Content—use lower-level DrawerPortal / DrawerOverlay only if you need custom portaling.
| Prop | Type | Default |
|---|---|---|
asChild | boolean | false |
DrawerClose
The control that closes the drawer.
| Prop | Type | Default |
|---|---|---|
asChild | boolean | false |
DrawerTitle
Optional accessible title announced when the drawer opens.
| Prop | Type | Default |
|---|---|---|
asChild | boolean | false |
DrawerDescription
Optional accessible description announced when the drawer opens.
| Prop | Type | Default |
|---|---|---|
asChild | boolean | false |
DrawerHandle
Optional drag affordance. Vaul’s public API lists no props; the underlying type may accept options such as preventCycle.
DrawerHeader
Title region; spacing and alignment adapt by direction (Experience System layout wrapper, not a Vaul primitive).
DrawerFooter
Sticky footer row (mt-auto flex column; Experience System layout wrapper).
Accessibility
- Keep
DrawerTitle(and usuallyDrawerDescription) inside the sheet for screen reader context. - Ensure
DrawerClosecontrols (or an explicit dismiss path) are keyboard-focusable and have visible labels. - For draggable sheets, use
handleOnlyordata-vaul-no-dragso nested sliders and inputs remain usable.
Source in the repo: packages/experience-system/src/components/Drawer/Drawer.tsx. Agent-oriented contracts: packages/experience-system/src/components/Drawer/Drawer_instructions.md.