Skip to contents

Drawer

A panel that slides in from the edge of the screen.

View as Markdown

Note: Drawer is in preview for a temporary period and receives breaking changes in minor versions. DrawerPreview will be renamed to Drawer when it exits preview.

Anatomy

Import the component and assemble its parts:

Anatomy

Drawer supports swipe gestures to dismiss. Set swipeDirection to control which direction dismisses the drawer. <Drawer.Content> allows text selection of its children without swipe interference when using a mouse pointer.

Examples

State

By default, Drawer is an uncontrolled component that manages its own state.

Uncontrolled drawer

Use open and onOpenChange props if you need to access or control the state of the drawer.

Controlled drawer

Position

Positioning is handled by your styles. swipeDirection defaults to "down" for bottom sheets. Use "up", "left", or "right" for other drawer positions.

Swipe directions

Nested drawers

Use the [data-nested-drawer-open] selector and the --nested-drawers CSS variable to style drawers when a nested drawer is open.

This demo stacks nested drawers using a constant peek so the frontmost drawer stays anchored to the bottom while the ones behind it are scaled down and lifted. It also uses the --drawer-height and --drawer-frontmost-height CSS variables to handle varying drawer heights.

Snap points

Use snapPoints to snap a bottom sheet drawer to preset heights. Numbers between 0 and 1 represent fractions of the viewport height, and numbers greater than 1 are treated as pixel values. String values support px and rem units (for example, '148px' or '30rem').

Snap points

Apply the snap point offset in your styles when using vertical drawers:

Snap point offset

By default, the drawer can skip snap points when swiping quickly. Specify the snapToSequentialPoints prop to disable velocity-based skipping so the snap target is determined by drag distance (you can still drag past multiple points).

Indent effect

Scale the background down when any drawer opens by wrapping your app in <Drawer.Provider> and use <Drawer.IndentBackground> + <Drawer.Indent> at the top of your tree. Any <Drawer.Root> within the provider notifies it when it mounts, which activates the indent parts (they receive [data-active] state attributes).

Non-modal

Set modal={false} to opt out of focus trapping and disablePointerDismissal to keep the drawer open on outside clicks.

Mobile navigation

You can build a full-screen mobile navigation sheet using Drawer parts, including a flick-to-dismiss from the top gesture.

Action sheet with separate destructive action

This demo builds an action sheet with a grouped list of actions plus a separate destructive action button.

Detached triggers

A drawer can be controlled by a trigger located either inside or outside the <Drawer.Root> component. For simple, one-off interactions, place the <Drawer.Trigger> inside <Drawer.Root>.

However, if defining the drawer’s content next to its trigger is not practical, you can use a detached trigger. This involves placing the <Drawer.Trigger> outside of <Drawer.Root> and linking them with a handle created by the Drawer.createHandle() function.

Detached triggers

The drawer can render different content depending on which trigger opened it. This is achieved by passing a payload to the <Drawer.Trigger> and using the function-as-a-child pattern in <Drawer.Root>.

Detached triggers with payload

Stacking and animations

Use CSS transitions or animations to animate drawer opening, closing, swipe interactions, and nested stacking. The data-starting-style attribute is applied when a drawer starts to open, and data-ending-style is applied when it starts to close.

The --nested-drawers CSS variable can be used to determine stack depth. The frontmost drawer has index 0.

Stack depth

When stacked drawers have varying heights, use the --drawer-height and --drawer-frontmost-height variables to keep collapsed drawers aligned with the frontmost one.

Variable-height stacking

The data-nested-drawer-open attribute marks drawers behind the frontmost drawer. Use it with data-nested-drawer-swiping to dim or hide parent drawer content while keeping it visible during nested swipe interactions.

Nested content visibility

The --drawer-swipe-movement-x, --drawer-swipe-movement-y, and --drawer-snap-point-offset CSS variables can be used to create smooth drag and snap offsets:

Swipe and snap offset

The data-swipe-direction attribute can be used with data-ending-style to animate directional dismissal:

Swipe dismissal direction

Use --drawer-swipe-progress to fade the backdrop as the drawer is swiped, and --drawer-swipe-strength to scale release transition durations based on swipe velocity.

Backdrop and release timing

API reference

Provider

Provides a shared context for coordinating global Drawer UI, such as indent/background effects based on whether any Drawer is open.

childrenReactNode
Type
React.ReactNode

IndentBackground

An element placed before <Drawer.Indent> to render a background layer that can be styled based on whether any drawer is open.

classNamestring | function
Description

CSS class applied to the element, or a function that returns a class based on the component’s state.

Type
| string
| ((
    state: Drawer.IndentBackground.State,
  ) => string | undefined)
styleReact.CSSProperties | function
Name
Type
| React.CSSProperties
| ((
    state: Drawer.IndentBackground.State,
  ) => CSSProperties | undefined)
| undefined
renderReactElement | function
Name
Description

Allows you to replace the component’s HTML element with a different tag, or compose it with another component.

Accepts a ReactElement or a function that returns the element to render.

Type
| ReactElement
| ((
    props: HTMLProps,
    state: Drawer.IndentBackground.State,
  ) => ReactElement)

Indent

A wrapper element intended to contain your app's main UI. Applies data-active when any drawer within the nearest <Drawer.Provider> is open.

classNamestring | function
Description

CSS class applied to the element, or a function that returns a class based on the component’s state.

Type
| string
| ((state: Drawer.Indent.State) => string | undefined)
styleReact.CSSProperties | function
Name
Type
| React.CSSProperties
| ((
    state: Drawer.Indent.State,
  ) => CSSProperties | undefined)
| undefined
renderReactElement | function
Name
Description

Allows you to replace the component’s HTML element with a different tag, or compose it with another component.

Accepts a ReactElement or a function that returns the element to render.

Type
| ReactElement
| ((
    props: HTMLProps,
    state: Drawer.Indent.State,
  ) => ReactElement)

Root

Groups all parts of the drawer. Doesn't render its own HTML element.

defaultOpenbooleanfalse
Description

Whether the drawer is initially open.

To render a controlled drawer, use the open prop instead.

Type
boolean | undefined
Default
false
openboolean
Name
Description

Whether the drawer is currently open.

Type
boolean | undefined
onOpenChangefunction
Description

Event handler called when the drawer is opened or closed.

Type
| ((
    open: boolean,
    eventDetails: Drawer.Root.ChangeEventDetails,
  ) => void)
| undefined
snapPointsDrawerSnapPoint[]
Description

Snap points used to position the drawer. Use numbers between 0 and 1 to represent fractions of the viewport height, numbers greater than 1 as pixel values, or strings in px/rem units (for example, '148px' or '30rem').

Type
DrawerSnapPoint[] | undefined
defaultSnapPointDrawerSnapPoint | null
Description

The initial snap point value when uncontrolled.

Type
number | string | null | undefined
snapPointDrawerSnapPoint | null
Description

The currently active snap point. Use with onSnapPointChange to control the snap point.

Type
number | string | null | undefined
onSnapPointChangefunction
Description

Callback fired when the snap point changes.

Type
| ((
    snapPoint: DrawerSnapPoint | null,
    eventDetails: Drawer.Root.SnapPointChangeEventDetails,
  ) => void)
| undefined
actionsRefRefObject<Drawer.Root.Actions | null>
Description

A ref to imperative actions.

  • unmount: When specified, the drawer will not be unmounted when closed. Instead, the unmount function must be called to unmount the drawer manually. Useful when the drawer's animation is controlled by an external library.
  • close: Closes the drawer imperatively when called.
Type
| React.RefObject<Drawer.Root.Actions | null>
| undefined
defaultTriggerIdstring | null
Description

ID of the trigger that the drawer is associated with. This is useful in conjunction with the defaultOpen prop to create an initially open drawer.

Type
string | null | undefined
disablePointerDismissalbooleanfalse
Description

Determines whether the drawer should close on outside clicks.

Type
boolean | undefined
Default
false
handleDrawer.Handle<Payload>
Name
Description

A handle to associate the drawer with a trigger. If specified, allows detached triggers to control the drawer's open state. Can be created with the Drawer.createHandle() method.

Type
{} | undefined
modalboolean | 'trap-focus'true
Name
Description

Determines if the drawer enters a modal state when open.

  • true: user interaction is limited to just the drawer: focus is trapped, document page scroll is locked, and pointer interactions on outside elements are disabled.
  • false: user interaction with the rest of the document is allowed.
  • 'trap-focus': focus is trapped inside the drawer, but document page scroll is not locked and pointer interactions outside of it remain enabled.
Type
boolean | 'trap-focus' | undefined
Default
true
onOpenChangeCompletefunction
Description

Event handler called after any animations complete when the drawer is opened or closed.

Type
((open: boolean) => void) | undefined
snapToSequentialPointsbooleanfalse
Description

Disables velocity-based snap skipping so drag distance determines the next snap point.

Type
boolean | undefined
Default
false
swipeDirectionDrawerSwipeDirection'down'
Description

The swipe direction used to dismiss the drawer.

Type
'up' | 'down' | 'left' | 'right' | undefined
Default
'down'
triggerIdstring | null
Description

ID of the trigger that the drawer is associated with. This is useful in conjunction with the open prop to create a controlled drawer. There's no need to specify this prop when the drawer is uncontrolled (i.e. when the open prop is not set).

Type
string | null | undefined
childrenReactNode | PayloadChildRenderFunction<Payload>
Description

The content of the drawer.

Type
| React.ReactNode
| ((arg: { payload: Payload | undefined }) => ReactNode)

Trigger

A button that opens the drawer. Renders a <button> element.

handleDrawerHandle<Payload>
Name
Description

A handle to associate the trigger with a drawer. Can be created with the Drawer.createHandle() method.

Type
{} | undefined
nativeButtonbooleantrue
Description

Whether the component renders a native <button> element when replacing it via the render prop. Set to false if the rendered element is not a button (e.g. <div>).

Type
boolean | undefined
Default
true
payloadPayload
Description

A payload to pass to the drawer when it is opened.

Type
Payload | undefined
idstring
Name
Description

ID of the trigger. In addition to being forwarded to the rendered element, it is also used to specify the active trigger for drawers in controlled mode (with the Drawer.Root triggerId prop).

Type
string | undefined
classNamestring | function
Description

CSS class applied to the element, or a function that returns a class based on the component’s state.

Type
| string
| ((state: Drawer.Trigger.State) => string | undefined)
styleReact.CSSProperties | function
Name
Type
| React.CSSProperties
| ((
    state: Drawer.Trigger.State,
  ) => CSSProperties | undefined)
| undefined
renderReactElement | function
Name
Description

Allows you to replace the component’s HTML element with a different tag, or compose it with another component.

Accepts a ReactElement or a function that returns the element to render.

Type
| ReactElement
| ((
    props: HTMLProps,
    state: Drawer.Trigger.State,
  ) => ReactElement)

Portal

A portal element that moves the popup to a different part of the DOM. By default, the portal element is appended to <body>. Renders a <div> element.

containerUnion
Description

A parent element to render the portal element into.

Type
| HTMLElement
| ShadowRoot
| React.RefObject<HTMLElement | ShadowRoot | null>
| null
| undefined
classNamestring | function
Description

CSS class applied to the element, or a function that returns a class based on the component’s state.

Type
| string
| ((state: Drawer.Portal.State) => string | undefined)
styleReact.CSSProperties | function
Name
Type
| React.CSSProperties
| ((
    state: Drawer.Portal.State,
  ) => CSSProperties | undefined)
| undefined
keepMountedbooleanfalse
Description

Whether to keep the portal mounted in the DOM while the popup is hidden.

Type
boolean | undefined
Default
false
renderReactElement | function
Name
Description

Allows you to replace the component’s HTML element with a different tag, or compose it with another component.

Accepts a ReactElement or a function that returns the element to render.

Type
| ReactElement
| ((
    props: HTMLProps,
    state: Drawer.Portal.State,
  ) => ReactElement)

Backdrop

An overlay displayed beneath the popup. Renders a <div> element.

forceRenderbooleanfalse
Description

Whether the backdrop is forced to render even when nested.

Type
boolean | undefined
Default
false
classNamestring | function
Description

CSS class applied to the element, or a function that returns a class based on the component’s state.

Type
| string
| ((state: Drawer.Backdrop.State) => string | undefined)
styleReact.CSSProperties | function
Name
Type
| React.CSSProperties
| ((
    state: Drawer.Backdrop.State,
  ) => CSSProperties | undefined)
| undefined
renderReactElement | function
Name
Description

Allows you to replace the component’s HTML element with a different tag, or compose it with another component.

Accepts a ReactElement or a function that returns the element to render.

Type
| ReactElement
| ((
    props: HTMLProps,
    state: Drawer.Backdrop.State,
  ) => ReactElement)
--drawer-swipe-progress
The swipe progress of the drawer gesture.

Viewport

A positioning container for the drawer popup that can be made scrollable. Renders a <div> element.

classNamestring | function
Description

CSS class applied to the element, or a function that returns a class based on the component’s state.

Type
| string
| ((state: Drawer.Viewport.State) => string | undefined)
styleReact.CSSProperties | function
Name
Type
| React.CSSProperties
| ((
    state: Drawer.Viewport.State,
  ) => CSSProperties | undefined)
| undefined
renderReactElement | function
Name
Description

Allows you to replace the component’s HTML element with a different tag, or compose it with another component.

Accepts a ReactElement or a function that returns the element to render.

Type
| ReactElement
| ((
    props: HTMLProps,
    state: Drawer.Viewport.State,
  ) => ReactElement)

A container for the drawer contents. Renders a <div> element.

initialFocusUnion
Description

Determines the element to focus when the drawer is opened.

  • false: Do not move focus.
  • true: Move focus based on the default behavior (first tabbable element or popup).
  • RefObject: Move focus to the ref element.
  • function: Called with the interaction type (mouse, touch, pen, or keyboard). Return an element to focus, true to use the default behavior, or false/undefined to do nothing.
Type
| boolean
| React.RefObject<HTMLElement | null>
| ((
    openType: InteractionType,
  ) => boolean | void | HTMLElement | null)
| undefined
finalFocusUnion
Description

Determines the element to focus when the drawer is closed.

  • false: Do not move focus.
  • true: Move focus based on the default behavior (trigger or previously focused element).
  • RefObject: Move focus to the ref element.
  • function: Called with the interaction type (mouse, touch, pen, or keyboard). Return an element to focus, true to use the default behavior, or false/undefined to do nothing.
Type
| boolean
| React.RefObject<HTMLElement | null>
| ((
    closeType: InteractionType,
  ) => boolean | void | HTMLElement | null)
| undefined
classNamestring | function
Description

CSS class applied to the element, or a function that returns a class based on the component’s state.

Type
| string
| ((state: Drawer.Popup.State) => string | undefined)
styleReact.CSSProperties | function
Name
Type
| React.CSSProperties
| ((state: Drawer.Popup.State) => CSSProperties | undefined)
| undefined
renderReactElement | function
Name
Description

Allows you to replace the component’s HTML element with a different tag, or compose it with another component.

Accepts a ReactElement or a function that returns the element to render.

Type
| ReactElement
| ((
    props: HTMLProps,
    state: Drawer.Popup.State,
  ) => ReactElement)
data-expanded
Present when the drawer is at the expanded (full-height) snap point.
data-nested-drawer-open
Present when a nested drawer is open.
data-nested-drawer-swiping
Present when a nested drawer is being swiped.
data-swipe-direction
Indicates the swipe direction.
data-swipe-dismiss
Present when the drawer is dismissed by swiping.
data-swiping
Present when the drawer is being swiped.
--drawer-frontmost-height
The height of the frontmost open drawer in the current nested drawer stack.
--drawer-height
The height of the drawer popup.
--drawer-snap-point-offset
The snap point offset used for translating the drawer.
--drawer-swipe-movement-x
The swipe movement on the X axis.
--drawer-swipe-movement-y
The swipe movement on the Y axis.
--drawer-swipe-strength
A scalar (0.1-1) used to scale the swipe release transition duration in CSS.
--nested-drawers
The number of nested drawers that are currently open.

Title

A heading that labels the drawer. Renders an <h2> element.

classNamestring | function
Description

CSS class applied to the element, or a function that returns a class based on the component’s state.

Type
| string
| ((state: Drawer.Title.State) => string | undefined)
styleReact.CSSProperties | function
Name
Type
| React.CSSProperties
| ((state: Drawer.Title.State) => CSSProperties | undefined)
| undefined
renderReactElement | function
Name
Description

Allows you to replace the component’s HTML element with a different tag, or compose it with another component.

Accepts a ReactElement or a function that returns the element to render.

Type
| ReactElement
| ((
    props: HTMLProps,
    state: Drawer.Title.State,
  ) => ReactElement)

Description

A paragraph with additional information about the drawer. Renders a <p> element.

classNamestring | function
Description

CSS class applied to the element, or a function that returns a class based on the component’s state.

Type
| string
| ((state: Drawer.Description.State) => string | undefined)
styleReact.CSSProperties | function
Name
Type
| React.CSSProperties
| ((
    state: Drawer.Description.State,
  ) => CSSProperties | undefined)
| undefined
renderReactElement | function
Name
Description

Allows you to replace the component’s HTML element with a different tag, or compose it with another component.

Accepts a ReactElement or a function that returns the element to render.

Type
| ReactElement
| ((
    props: HTMLProps,
    state: Drawer.Description.State,
  ) => ReactElement)

Close

A button that closes the drawer. Renders a <button> element.

nativeButtonbooleantrue
Description

Whether the component renders a native <button> element when replacing it via the render prop. Set to false if the rendered element is not a button (e.g. <div>).

Type
boolean | undefined
Default
true
classNamestring | function
Description

CSS class applied to the element, or a function that returns a class based on the component’s state.

Type
| string
| ((state: Drawer.Close.State) => string | undefined)
styleReact.CSSProperties | function
Name
Type
| React.CSSProperties
| ((state: Drawer.Close.State) => CSSProperties | undefined)
| undefined
renderReactElement | function
Name
Description

Allows you to replace the component’s HTML element with a different tag, or compose it with another component.

Accepts a ReactElement or a function that returns the element to render.

Type
| ReactElement
| ((
    props: HTMLProps,
    state: Drawer.Close.State,
  ) => ReactElement)