A React drawer that slides in from any edge with a Liquid Glass surface and swipe dismissal.
Drawer is an edge-aligned panel with swipe dismissal. It renders the portal, backdrop, popup, title, description, optional body, and a custom action area internally, with optional snap points.
Unlike the centered Dialog, Drawer slides in from an edge of the screen, which makes it a good fit for mobile settings panels, filters, and side navigation.
The drawer opens from the bottom by default. open controls visibility and footer holds your own buttons; buttons do not close the drawer automatically, so update open yourself as the example does.
direction accepts four values: down slides in from the bottom, up from the top, and left and right from the matching side. It defaults to down, and the component adjusts the popup position, corner radii, enter animation, and swipe dismissal direction together.
On touch screens, drag the drawer in the direction set by direction to dismiss it. On desktop, the backdrop, Escape key, and action buttons remain available.
Open the drawer, then swipe it downward on a touch screen.
snapPoints, defaultSnapPoint, snapPoint, onSnapPointChange, and snapToSequentialPoints configure snap heights and can make a bottom or top drawer settle between preset heights.
disablePointerDismissal prevents the backdrop from closing the drawer while Escape and the action buttons still work. Use it for flows that may contain unsaved work.
Drawer uses the same dialog semantics as Dialog: focus moves into the panel, stays trapped while it is open, and returns to the trigger when it closes. title names the panel and description describes it.
Escape, the backdrop, and the swipe gesture close it; use disablePointerDismissal and a visible cancel action when dismissal could lose work. Keep swipe gestures away from horizontally scrollable content. See the accessibility guide for overlay focus rules.
| Prop | Type | Default | Description |
|---|---|---|---|
title | ReactNode | — | Drawer heading. Required. |
description | ReactNode | — | Supporting copy under the title. |
children | ReactNode | — | Body content between the description and the action area. |
footer | ReactNode | — | Bottom action area that can hold any number of controls. |
direction | "down" | "up" | "left" | "right" | "down" | Edge the drawer slides in from, also used as the swipe dismissal direction. |
open | boolean | — | Controlled open state. |
defaultOpen | boolean | false | Initial open state when uncontrolled. |
onOpenChange | (open: boolean, details) => void | — | Called when the drawer opens or closes. |
onOpenChangeComplete | (open: boolean) => void | — | Called after the open or close animation finishes. |
modal | boolean | "trap-focus" | true | Controls focus trapping and page scroll locking. |
disablePointerDismissal | boolean | false | Prevents the backdrop from closing the drawer. |
actionsRef | RefObject<Actions | null> | — | Imperative close and unmount actions. |
handle | DrawerHandle | — | Associates the drawer with external triggers. |
triggerId | string | null | — | ID of the trigger associated with the drawer. |
defaultTriggerId | string | null | — | ID of the trigger used for an initially open drawer. |
snapPoints | DrawerSnapPoint[] | — | Snap heights, as viewport fractions, pixel numbers, or px/rem strings. |
snapToSequentialPoints | boolean | false | Uses drag distance instead of velocity to choose the next snap point. |
snapPoint | DrawerSnapPoint | null | — | Controlled active snap point. |
defaultSnapPoint | DrawerSnapPoint | null | — | Initial snap point when uncontrolled. |
onSnapPointChange | (snapPoint: DrawerSnapPoint | null, details) => void | — | Called when the active snap point changes. |
className | string | — | Tailwind classes merged into the popup. |
style | CSSProperties | — | Inline styles merged into the same element as className. |
ui | { backdrop?: ClassValue; viewport?: ClassValue; popup?: ClassValue; glass?: ClassValue; overlay?: ClassValue; content?: ClassValue; title?: ClassValue; description?: ClassValue; body?: ClassValue; footer?: ClassValue } | — | Tailwind classes for internal slots, keyed by data-slot. |
| Attribute | Description |
|---|---|
data-open | Present when the drawer is open. |
data-closed | Present when the drawer is closed. |
data-starting-style | Present when the drawer begins animating in. |
data-ending-style | Present when the drawer is animating out. |
data-expanded | Present when the drawer is at the expanded snap point. |
data-nested | Present when the drawer is nested within another drawer. |
data-nested-drawer-open | Present when another drawer is open inside this one. |
data-nested-drawer-swiping | Present while a nested drawer is being swiped. |
data-swipe-dismiss | Present when the drawer is dismissed by a swipe. |
data-swipe-direction | Indicates the swipe direction (up, down, left, or right). |
data-swiping | Present while the drawer is being swiped. |
data-slot | Marks internal parts (backdrop, viewport, popup, glass, overlay, content, title, description, body, footer) for styling. |
The component uses the shared glass material. Tune shared --ui-glass-* tokens on a parent, use className for layout, and use ui for internal slots keyed by data-slot. Component-specific values live in src/theme/drawer.ts. See the theming guide for token layers and presets.