Drawer

Slide-over panel for contextual content and actions

Installation

pnpm add @wandercom/design-system-web

Usage

The Drawer component provides both a high-level API and low-level primitives for advanced composition. On mobile, left and right drawers automatically slide from the bottom. A built-in close button is rendered in the top-right corner of the content area.

High-level API

Use the simplified Drawer component for most use cases:

import { Drawer } from '@wandercom/design-system-web/ui/drawer';
import { Button } from '@wandercom/design-system-web/ui/button';

export function Example() {
  return (
    <Drawer
      trigger={<Button>Open</Button>}
      title="Account settings"
      description="Manage your account preferences"
      primaryAction={{ label: "Save", action: () => console.log("saved") }}
      secondaryAction={{ label: "Cancel", action: () => console.log("cancelled") }}
    >
      <div>Content here</div>
    </Drawer>
  );
}

Primitive API

For advanced composition, use the individual primitives. Note that DrawerClose renders a pre-styled icon button in the top-right corner and is not intended for use as a labeled cancel button.

import {
  DrawerRoot,
  DrawerTrigger,
  DrawerContent,
  DrawerHeader,
  DrawerFooter,
  DrawerTitle,
  DrawerDescription,
  DrawerClose,
} from '@wandercom/design-system-web/ui/drawer';
import { Button } from '@wandercom/design-system-web/ui/button';

export function Example() {
  return (
    <DrawerRoot>
      <DrawerTrigger render={<Button variant="outline">Open</Button>} />
      <DrawerContent>
        <DrawerHeader>
          <DrawerTitle>Account settings</DrawerTitle>
          <DrawerDescription>Manage your account preferences</DrawerDescription>
        </DrawerHeader>
        <div>Content here</div>
        <DrawerFooter>
          <Button variant="outline">Cancel</Button>
          <Button>Save</Button>
        </DrawerFooter>
        <DrawerClose />
      </DrawerContent>
    </DrawerRoot>
  );
}

Examples

Loading example...

Nested drawers

Stack drawers by nesting DrawerRoot components. The inner drawer opens on top of the outer one, and each manages its own open/close state independently.

Loading example...
Loading example...
Loading example...

Props

Drawer

trigger?

React.ReactNode
Element that triggers the drawer to open.

title?

React.ReactNode
Drawer title content.

description?

React.ReactNode
Drawer description content.

direction?

'bottom' | 'left' | 'right'
Direction the drawer slides from. On mobile, left and right are overridden to bottom. Defaults to "bottom".

primaryAction?

{ label: string; action: () => void }
Configuration for the primary action button.

secondaryAction?

{ label: string; action: () => void }
Configuration for the secondary action button.

variant?

'default' | 'destructive'
Visual variant affecting primary button style. Defaults to "default".

formId?

string
Form ID to associate with the primary action button. When set, the primary button renders as a submit button for the given form.

children?

React.ReactNode
Main drawer content.

open?

boolean
Controlled open state.

onOpenChange?

(open: boolean) => void
Callback when the open state changes.

defaultOpen?

boolean
Default open state for uncontrolled usage.

onOpenAutoFocus?

(event: Event) => void
Callback when focus moves into the drawer on open.

onCloseAutoFocus?

(event: Event) => void
Callback when focus returns to the trigger on close.

slots?

DrawerSlots
Slot-based className overrides for subcomponents (trigger, overlay, content, header, footer, title, description).

className?

string
Additional CSS classes for the content container.

DrawerRoot

direction?

'bottom' | 'top' | 'left' | 'right'
Direction the drawer slides from. Supports all four directions. Defaults to "bottom".

open?

boolean
Controlled open state.

onOpenChange?

(open: boolean) => void
Callback when the open state changes.

defaultOpen?

boolean
Default open state for uncontrolled usage.

DrawerTrigger

render?

ReactElement
Replaces the default element with the provided element, merging props.

DrawerContent

className?

string
Additional CSS classes for the content container.

overlayClassName?

string
Additional CSS classes for the overlay backdrop.

onOpenAutoFocus?

(event: Event) => void
Callback when focus moves into the drawer on open.

onCloseAutoFocus?

(event: Event) => void
Callback when focus returns to the trigger on close.

DrawerHeader

className?

string
Additional CSS classes for the header container.

DrawerFooter

className?

string
Additional CSS classes for the footer container.

DrawerTitle

className?

string
Additional CSS classes for the title element. Renders as a Heading with the headline variant.

DrawerDescription

className?

string
Additional CSS classes for the description element.

DrawerClose

Renders a pre-styled ghost icon button with an X icon, absolutely positioned in the top-right corner of the drawer.

className?

string
Additional CSS classes for the close button.

Accessibility

  • Built on Base UI's drawer primitive, which follows WAI-ARIA dialog patterns
  • Closes on escape key press and overlay click when modal
  • Links title and description via aria-labelledby / aria-describedby
  • Returns focus to the trigger element on close
  • Supports swipe-to-dismiss gestures matching the drawer direction
Drawer