Drawer
A panel that slides in from the edge of the screen.
View as MarkdownAnatomy
Import the component and assemble its parts:
import { Drawer } from '@base-ui/react/drawer';
<Drawer.Provider>
<Drawer.IndentBackground />
<Drawer.Indent>
<Drawer.Root>
<Drawer.Trigger />
<Drawer.SwipeArea />
<Drawer.Portal>
<Drawer.Backdrop />
<Drawer.Viewport>
<Drawer.Popup>
<Drawer.Content>
<Drawer.Title />
<Drawer.Description />
<Drawer.Close />
</Drawer.Content>
</Drawer.Popup>
</Drawer.Viewport>
</Drawer.Portal>
</Drawer.Root>
</Drawer.Indent>
</Drawer.Provider>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.
<Drawer.Root>
<Drawer.Trigger>Open</Drawer.Trigger>
<Drawer.Portal>
<Drawer.Popup>
<Drawer.Content>
<Drawer.Title>Example drawer</Drawer.Title>
<Drawer.Close>Close</Drawer.Close>
</Drawer.Content>
</Drawer.Popup>
</Drawer.Portal>
</Drawer.Root>Use open and onOpenChange props if you need to access or control the state of the drawer.
const [open, setOpen] = React.useState(false);
return (
<Drawer.Root open={open} onOpenChange={setOpen}>
<Drawer.Trigger>Open</Drawer.Trigger>
<Drawer.Portal>
<Drawer.Popup>
<Drawer.Content>
<Drawer.Title>Example drawer</Drawer.Title>
<Drawer.Close>Close</Drawer.Close>
</Drawer.Content>
</Drawer.Popup>
</Drawer.Portal>
</Drawer.Root>
);Position
Positioning is handled by your styles. swipeDirection defaults to "down" for bottom sheets. Use "up", "left", or "right" for other drawer positions.
<Drawer.Root swipeDirection="top">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; strings are treated as CSS lengths (for example, '148px').
const snapPoints = ['148px', '355px', 1];
const [snapPoint, setSnapPoint] = React.useState<Drawer.Root.SnapPoint | null>(snapPoints[0]);
<Drawer.Root
swipeDirection="down"
snapPoints={snapPoints}
snapPoint={snapPoint}
onSnapPointChange={setSnapPoint}
>
{/* ... */}
</Drawer.Root>Apply the snap point offset in your styles when using vertical drawers:
.DrawerPopup {
transform: translateY(
calc(var(--drawer-snap-point-offset, 0px) + var(--drawer-swipe-movement-y))
);
}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.
Swipe to open
Place <Drawer.SwipeArea> along the edge of the viewport to enable swipe-to-open gestures. The drawer stays open on release once the swipe covers roughly half its size (minimum 40px).
Swipe from the right edge to open the drawer.
Animations
Use CSS transitions or animations to animate the drawer opening and closing. The data-starting-style attribute is applied when the drawer starts to open, and data-ending-style is applied when it starts to close.
The --drawer-swipe-movement-x, --drawer-swipe-movement-y, and --drawer-swipe-progress CSS variables can be used to create smooth drag interactions:
.DrawerPopup {
/* Apply swipe offset during drag */
transform: translateX(var(--drawer-swipe-movement-x));
transition: transform 525ms cubic-bezier(0.45, 1.005, 0, 1.005);
}
.DrawerPopup[data-starting-style],
.DrawerPopup[data-ending-style] {
transform: translateX(100%);
}
.DrawerPopup[data-ending-style] {
transition-duration: 180ms;
transition-timing-function: cubic-bezier(0.375, 0.015, 0.545, 0.455);
}Use --drawer-swipe-progress to fade the backdrop as the drawer is swiped:
.DrawerBackdrop {
--backdrop-opacity: 0.2;
opacity: calc(var(--backdrop-opacity) * (1 - var(--drawer-swipe-progress, 0)));
transition-duration: 450ms;
}
.DrawerBackdrop[data-swiping] {
transition-duration: 0ms;
}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—
- Name
- 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—
- Name
- 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
ReactElementor 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—
- Name
- 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
ReactElementor 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
- Name
- Description
Whether the drawer is initially open.
To render a controlled drawer, use the
openprop instead.- Type
boolean | undefined- Default
false
openboolean—
- Name
- Description
Whether the drawer is currently open.
- Type
boolean | undefined
onOpenChangefunction—
- Name
- Description
Event handler called when the drawer is opened or closed.
- Type
| (( open: boolean, eventDetails: Drawer.Root.ChangeEventDetails, ) => void) | undefined
snapPointsDrawerSnapPoint[]—
- Name
- Description
Snap points used to position the drawer. Use numbers between 0 and 1 to represent fractions of the viewport height, or CSS length strings (for example,
'148px').- Type
DrawerSnapPoint[] | undefined
defaultSnapPointDrawerSnapPoint | null—
- Name
- Description
The initial snap point value when uncontrolled.
- Type
number | string | null | undefined
snapPointDrawerSnapPoint | null—
- Name
- Description
The currently active snap point. Use with
onSnapPointChangeto 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>—
- Name
- Description
A ref to imperative actions.
unmount: When specified, the drawer will not be unmounted when closed. Instead, theunmountfunction 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> | undefined
disablePointerDismissalbooleanfalse
- Description
Determines whether the drawer should close on outside clicks.
- Type
boolean | undefined- Default
false
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'
- Name
- Description
The swipe direction used to dismiss the drawer.
- Type
'up' | 'down' | 'left' | 'right' | undefined- Default
'down'
childrenReactNode—
- Name
- Description
The content of the drawer.
- Type
React.ReactNode
Trigger
A button that opens the drawer.
Renders a <button> element.
nativeButtonbooleantrue
- Name
- Description
Whether the component renders a native
<button>element when replacing it via therenderprop. Set tofalseif the rendered element is not a button (e.g.<div>).- Type
boolean | undefined- Default
true
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 the dialogs in controlled mode (with the Dialog.Root
triggerIdprop).- Type
string | undefined
classNamestring | function—
- Name
- Description
CSS class applied to the element, or a function that returns a class based on the component’s state.
- Type
| string | ((state: AlertDialog.Trigger.State) => string | undefined)
styleReact.CSSProperties | function—
- Name
- Type
| React.CSSProperties | (( state: AlertDialog.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
ReactElementor a function that returns the element to render.- Type
| ReactElement | (( props: HTMLProps, state: AlertDialog.Trigger.State, ) => ReactElement)
SwipeArea
An invisible area that listens for swipe gestures to open the drawer.
Renders a <div> element.
swipeDirectionDrawerSwipeDirection—
- Name
- Description
The swipe direction that opens the drawer. Defaults to the opposite of
Drawer.RootswipeDirection.- Type
'up' | 'down' | 'left' | 'right' | undefined
disabledbooleanfalse
- Name
- Description
Whether the swipe area is disabled.
- Type
boolean | undefined- Default
false
classNamestring | function—
- Name
- Description
CSS class applied to the element, or a function that returns a class based on the component’s state.
- Type
| string | ((state: Drawer.SwipeArea.State) => string | undefined)
styleReact.CSSProperties | function—
- Name
- Type
| React.CSSProperties | (( state: Drawer.SwipeArea.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
ReactElementor a function that returns the element to render.- Type
| ReactElement | (( props: HTMLProps, state: Drawer.SwipeArea.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—
- Name
- Description
A parent element to render the portal element into.
- Type
| HTMLElement | ShadowRoot | React.RefObject<HTMLElement | ShadowRoot | null> | null | undefined
classNamestring | function—
- Name
- Description
CSS class applied to the element, or a function that returns a class based on the component’s state.
- Type
| string | ((state: Dialog.Portal.State) => string | undefined)
styleReact.CSSProperties | function—
- Name
- Type
| React.CSSProperties | (( state: Dialog.Portal.State, ) => CSSProperties | undefined) | undefined
keepMountedbooleanfalse
- Name
- 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
ReactElementor a function that returns the element to render.- Type
| ReactElement | (( props: HTMLProps, state: Dialog.Portal.State, ) => ReactElement)
Backdrop
An overlay displayed beneath the popup.
Renders a <div> element.
forceRenderbooleanfalse
- Name
- Description
Whether the backdrop is forced to render even when nested.
- Type
boolean | undefined- Default
false
classNamestring | function—
- Name
- 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
ReactElementor a function that returns the element to render.- Type
| ReactElement | (( props: HTMLProps, state: Drawer.Backdrop.State, ) => ReactElement)
--drawer-swipe-progress
Viewport
A positioning container for the drawer popup that can be made scrollable.
Renders a <div> element.
classNamestring | function—
- Name
- Description
CSS class applied to the element, or a function that returns a class based on the component’s state.
- Type
| string | ((state: AlertDialog.Viewport.State) => string | undefined)
styleReact.CSSProperties | function—
- Name
- Type
| React.CSSProperties | (( state: AlertDialog.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
ReactElementor a function that returns the element to render.- Type
| ReactElement | (( props: HTMLProps, state: AlertDialog.Viewport.State, ) => ReactElement)
Popup
A container for the drawer contents.
Renders a <div> element.
initialFocusUnion—
- Name
- 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, orkeyboard). Return an element to focus,trueto use the default behavior, orfalse/undefinedto do nothing.
- Type
| boolean | React.RefObject<HTMLElement | null> | (( openType: InteractionType, ) => boolean | void | HTMLElement | null) | undefined
finalFocusUnion—
- Name
- 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, orkeyboard). Return an element to focus,trueto use the default behavior, orfalse/undefinedto do nothing.
- Type
| boolean | React.RefObject<HTMLElement | null> | (( closeType: InteractionType, ) => boolean | void | HTMLElement | null) | undefined
classNamestring | function—
- Name
- 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
ReactElementor a function that returns the element to render.- Type
| ReactElement | (( props: HTMLProps, state: Drawer.Popup.State, ) => ReactElement)
data-expanded
data-nested-drawer-open
data-nested-drawer-swiping
data-swipe-direction
data-swipe-dismiss
data-swiping
--drawer-frontmost-height
--drawer-height
--drawer-snap-point-offset
--drawer-swipe-movement-x
--drawer-swipe-movement-y
--drawer-swipe-strength
--nested-drawers
Title
A heading that labels the drawer.
Renders an <h2> element.
classNamestring | function—
- Name
- Description
CSS class applied to the element, or a function that returns a class based on the component’s state.
- Type
| string | ((state: AlertDialog.Title.State) => string | undefined)
styleReact.CSSProperties | function—
- Name
- Type
| React.CSSProperties | ((state: AlertDialog.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
ReactElementor a function that returns the element to render.- Type
| ReactElement | (( props: HTMLProps, state: AlertDialog.Title.State, ) => ReactElement)
Description
A paragraph with additional information about the drawer.
Renders a <p> element.
classNamestring | function—
- Name
- Description
CSS class applied to the element, or a function that returns a class based on the component’s state.
- Type
| string | ((state: AlertDialog.Description.State) => string | undefined)
styleReact.CSSProperties | function—
- Name
- Type
| React.CSSProperties | (( state: AlertDialog.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
ReactElementor a function that returns the element to render.- Type
| ReactElement | (( props: HTMLProps, state: AlertDialog.Description.State, ) => ReactElement)
Close
A button that closes the drawer.
Renders a <button> element.
nativeButtonbooleantrue
- Name
- Description
Whether the component renders a native
<button>element when replacing it via therenderprop. Set tofalseif the rendered element is not a button (e.g.<div>).- Type
boolean | undefined- Default
true
classNamestring | function—
- Name
- Description
CSS class applied to the element, or a function that returns a class based on the component’s state.
- Type
| string | ((state: AlertDialog.Close.State) => string | undefined)
styleReact.CSSProperties | function—
- Name
- Type
| React.CSSProperties | ((state: AlertDialog.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
ReactElementor a function that returns the element to render.- Type
| ReactElement | (( props: HTMLProps, state: AlertDialog.Close.State, ) => ReactElement)