For AI agents: the complete documentation index is available at https://wallarm.github.io/design-system/llms.txt, the full documentation bundle is available at https://wallarm.github.io/design-system/llms-full.txt, and this page is available as Markdown at https://wallarm.github.io/design-system/components/drawer.md.

Drawer

A side panel for work the reader does alongside the page — its differentiator is that it can run non-modal, so with modal={false} they can keep using what is behind it. Reach for Dialog when the panel is a decision to resolve now, since that is the same panel with the exit closed off.

Source · Storybook

Usage

import { Button } from '@wallarm-org/design-system/Button';
import { Drawer, DrawerBody, DrawerContent, DrawerHeader, DrawerTitle, DrawerTrigger } from '@wallarm-org/design-system/Drawer';

Examples

DrawerFooter for the actions that commit the work. It stays put while the body scrolls.

With description

DrawerDescription under the title, wired to aria-describedby so it is read with the panel rather than after it. DrawerHeader stacks the two, keeping the close button right-aligned.

A left-aligned secondary action, separated from the committing pair on the right.

Sizes

The standard widths. A drawer earns more width than a dialog because the reader is working in it rather than answering it.

Custom sizes

Explicit width bounds for content the standard steps do not fit.

Resizable

DrawerResizeHandle lets the reader set the width themselves — the part Dialog deliberately does not export, because a decision does not need resizing.

Resizable with overflow list

Resizing with content that reflows, which is the case worth testing: the panel is only useful at its new width if what is inside adapts.

Scrollable

The body scrolls between a fixed header and footer, so the actions never scroll away from the work.

Controlled

Owning open, for a panel something outside it opens or closes.

No closable on esc

Escape disabled, for a panel holding unsaved work where a reflex keystroke would lose it.

No overlay

Without the backdrop, which is how a drawer reads as non-blocking. Pair it with modal={false} if the page behind should also stay usable.

With nested

A drawer from a drawer: same family, so the outer one steps back and the depth is legible.

With nested dialog

A dialog opened from a drawer — different family, so the drawer stays put rather than scaling. That separation is what kind exists for.

With nested select

A Select inside a drawer, checking its menu escapes the panel instead of being clipped.

With tabs

Tabs inside a drawer, which suits a panel with several facets of one subject.

Props

PropTypeDefaultDescription
kindDrawerKind | undefineddrawerWhich overlay family this root belongs to. Drives the same-kind nesting counter behind the pushed-back animation: a Drawer only pushes back for a nested Drawer, a Dialog only for a nested Dialog. Set to 'dialog' by the Dialog component — consumers should not need to touch this.
openboolean | undefinedControlled open state
onOpenChange((open: boolean) => void) | undefinedControlled open change handler
closeOnEscapeboolean | undefinedtrueWhether ESC key closes the drawer (default: true)
closeOnOutsideClickboolean | undefinedtrueWhether clicking outside closes the drawer (default: true)
overlayboolean | undefinedtrueOverlay visibility (default: true)
modalboolean | undefinedtrueWhether the drawer is modal — blocks interaction with the rest of the page (default: true)
widthstring | number | undefinedWidth - number for pixels, string for percentage (e.g., "50%")
minWidthnumber | undefinedMinimum width in pixels
maxWidthnumber | undefinedMaximum width in pixels
onInteractOutsidecomplexFired when the user interacts outside the drawer (e.g. clicks the backdrop).
onEscapeKeyDown((event: KeyboardEvent) => void) | undefinedFired when the user presses the Escape key while the drawer is open.
data-testidstring | undefined

DrawerClose

PropTypeDefaultDescription
asChildboolean | undefinedfalseRender as child component
titlestring | undefined
defaultCheckedboolean | undefined
defaultValuestring | number | readonly string[] | undefined
autoFocusboolean | undefined
idstring | undefined
tabIndexnumber | undefined
relstring | undefined
onChangeChangeEventHandler<T, Element> | undefined
type"button" | "submit" | "reset" | undefined
disabledboolean | undefined
namestring | undefined
valuestring | number | readonly string[] | undefined
data-testidstring | undefined

DrawerContent

PropTypeDefaultDescription
asChildboolean | undefined

DrawerFooterControls

PropTypeDefaultDescription
placement{ left: string; right: string; }right

DrawerPositioner

PropTypeDefaultDescription
isResizing *boolean

DrawerTrigger

PropTypeDefaultDescription
asChildboolean | undefinedfalseRender as child component
valuestring | undefinedNative button value attribute (string only, matching Ark UI's Dialog.Trigger)
titlestring | undefined
defaultCheckedboolean | undefined
defaultValuestring | number | readonly string[] | undefined
autoFocusboolean | undefined
idstring | undefined
tabIndexnumber | undefined
relstring | undefined
onChangeChangeEventHandler<T, Element> | undefined
type"button" | "submit" | "reset" | undefined
disabledboolean | undefined
namestring | undefined
data-testidstring | undefined

Explorer