Drawer
A panel that slides in from the edge of the screen, typically used for navigation or forms.
Anatomy
<Drawer.Root>
<Drawer.Trigger />
<Drawer.Backdrop />
<Drawer.Positioner>
<Drawer.Content>
<Drawer.Grabber>
<Drawer.GrabberIndicator />
</Drawer.Grabber>
<Drawer.Title />
<Drawer.Description />
<Drawer.CloseTrigger />
</Drawer.Content>
</Drawer.Positioner>
</Drawer.Root>
Examples
Swipe Direction
Use the swipeDirection prop to control which edge the drawer slides in from.
Snap Points
Use the snapPoints prop to define intermediate positions the drawer can snap to.
Modal
Set modal to false to allow interaction with the rest of the page while the drawer is open.
Controlled
Use the open and onOpenChange props to control the drawer state.
Scrollable
No Drag Area
Apply the data-no-drag attribute to any element inside the drawer to prevent dragging from starting on it.
Non Draggable
Set draggable to false to disable drag-to-dismiss entirely.
Indent Background
Use Drawer.IndentBackground to create a visual indent effect on the page behind the drawer.
Multiple Triggers
Use the value prop on Drawer.Trigger to share a single drawer across multiple trigger elements. The
onTriggerValueChange callback fires when a different trigger is activated.
Using the Root Provider
Use the useDrawer hook and Drawer.RootProvider to control the drawer from outside the component tree.
Guides
Styling by Swipe Direction
The Drawer.Content elements expose a data-swipe-direction attribute (up | down | left | right) that reflects
the resolved physical direction the drawer slides in from.
Target it to apply direction-aware styles — for example, rounded corners on the side facing the viewport:
[data-scope='drawer'][data-part='content'] {
/* Top drawer */
&[data-swipe-direction='up'] {
border-top-left-radius: 0;
border-top-right-radius: 0;
border-bottom-left-radius: 16px;
border-bottom-right-radius: 16px;
}
/* Bottom drawer */
&[data-swipe-direction='down'] {
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
border-top-left-radius: 16px;
border-top-right-radius: 16px;
}
/* Left drawer */
&[data-swipe-direction='left'] {
border-top-left-radius: 0;
border-bottom-right-radius: 16px;
border-top-left-radius: 16px;
border-top-right-radius: 0;
}
/* Right drawer */
&[data-swipe-direction='right'] {
border-top-right-radius: 0;
border-bottom-left-radius: 16px;
border-top-left-radius: 0;
border-top-right-radius: 16px;
}
}
Preventing Overdrag Gaps
When a user drags the drawer past its open position, a small gap can appear between the drawer content and the viewport
edge. To prevent this, extend the drawer's background beyond its visible bounds using a CSS ::after pseudo-element:
[data-scope='drawer'][data-part='content'] {
--bleed: 3rem;
position: relative;
/* Bleed effect */
&::after {
content: '';
position: absolute;
inset-inline: 0;
top: 100%;
height: var(--bleed);
background-color: inherit;
pointer-events: none;
}
}
For side drawers, adjust the pseudo-element to extend horizontally:
[data-scope='drawer'][data-part='content'] {
/* Right drawer */
&[data-swipe-direction='right']::after {
inset-inline: auto;
inset-block: 0;
top: 0;
left: 100%;
width: var(--bleed);
height: auto;
}
/* Left drawer */
&[data-swipe-direction='left']::after {
inset-inline: auto;
inset-block: 0;
top: 0;
right: 100%;
width: var(--bleed);
height: auto;
}
}
The ::after element inherits the drawer's background color and sits just outside the content bounds. During overdrag,
the dampened movement reveals this extension instead of an empty gap.
API Reference
Props
Root
| Prop | Default | Type |
|---|---|---|
closeOnEscape | true | booleanWhether to close the drawer when the escape key is pressed. |
closeOnInteractOutside | true | booleanWhether to close the drawer when the outside is clicked. |
closeThreshold | 0.25 | numberThe threshold distance for dismissing the drawer. |
defaultOpen | booleanThe initial open state of the drawer. | |
defaultSnapPoint | SnapPoint | |
finalFocusEl | () => MaybeElementElement to receive focus when the sheet is closed. | |
id | stringThe unique identifier of the machine. | |
ids | Partial<{
backdrop: string
positioner: string
content: string
title: string
header: string
trigger: string
grabber: string
grabberIndicator: string
closeTrigger: string
}>The ids of the elements in the drawer. Useful for composition. | |
immediate | booleanWhether to synchronize the present change immediately or defer it to the next frame | |
initialFocusEl | () => MaybeElementElement to receive focus when the sheet is opened. | |
lazyMount | false | booleanWhether to enable lazy mounting |
modal | true | booleanWhether to prevent pointer interaction outside the element and hide all content below it. |
onEscapeKeyDown | (event: KeyboardEvent) => voidFunction called when the escape key is pressed | |
onExitComplete | VoidFunctionFunction called when the animation ends in the closed state | |
onFocusOutside | (event: FocusOutsideEvent) => voidFunction called when the focus is moved outside the component | |
onInteractOutside | (event: InteractOutsideEvent) => voidFunction called when an interaction happens outside the component | |
onOpenChange | (details: OpenChangeDetails) => voidFunction called when the open state changes. | |
onPointerDownOutside | (event: PointerDownOutsideEvent) => voidFunction called when the pointer is pressed down outside the component | |
onRequestDismiss | (event: LayerDismissEvent) => voidFunction called when this layer is closed due to a parent layer being closed | |
onSnapPointChange | (details: SnapPointChangeDetails) => voidCallback fired when the snap point changes. | |
open | booleanWhether the drawer is open. | |
present | booleanWhether the node is present (controlled by the user) | |
preventDragOnScroll | true | booleanWhether to prevent dragging on scrollable elements. When enabled, the sheet will not start dragging if the user is interacting with a scrollable element. |
preventScroll | true | booleanWhether to prevent scrolling behind the sheet when it's opened |
restoreFocus | true | booleanWhether to restore focus to the element that had focus before the sheet was opened. |
role | 'dialog' | 'dialog' | 'alertdialog'The sheet's role |
skipAnimationOnMount | false | booleanWhether to allow the initial presence animation. |
snapPoint | SnapPointThe currently active snap point. | |
snapPoints | [1] | SnapPoint[]The snap points of the drawer. Array of numbers or strings representing the snap points. |
snapToSequentialPoints | false | booleanWhether the drawer should snap to sequential points when swiping. |
stack | DrawerStackOptional external store for coordinating app-level drawer stack visuals (e.g. indent and background layers). | |
swipeDirection | 'down' | SwipeDirectionThe direction in which the drawer can be swiped. |
swipeVelocityThreshold | 700 | numberThe threshold velocity (in pixels/s) for closing the drawer. |
trapFocus | true | booleanWhether to trap focus inside the sheet when it's opened. |
unmountOnExit | false | booleanWhether to unmount on exit. |
Backdrop
Renders a <div> element.
| Prop | Default | Type |
|---|---|---|
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
| Attribute | Description |
|---|---|
[data-scope] | drawer |
[data-part] | backdrop |
[data-state] | "open" | "closed" |
[data-swiping] |
| CSS Variable | Description |
|---|---|
--drawer-swipe-progress | The drawer swipe progress value for the Backdrop |
--drawer-swipe-strength | The drawer swipe strength value for the Backdrop |
--layer-index | The index of the dismissable in the layer stack |
CloseTrigger
Renders a <button> element.
| Prop | Default | Type |
|---|---|---|
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
Content
Renders a <div> element.
| Prop | Default | Type |
|---|---|---|
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. | |
draggable | true | booleanWhether the drawer content is draggable. If false, the drawer can only be dragged by the grabber. |
| Attribute | Description |
|---|---|
[data-scope] | drawer |
[data-part] | content |
[data-state] | "open" | "closed" |
[data-expanded] | Present when expanded |
[data-swipe-direction] | |
[data-swiping] | |
[data-dragging] | Present when in the dragging state |
| CSS Variable | Description |
|---|---|
--drawer-translate | The drawer translate value for the Content |
--drawer-translate-x | The drawer translate x value for the Content |
--drawer-translate-y | The drawer translate y value for the Content |
--drawer-snap-point-offset-x | The offset position for drawer snap point |
--drawer-snap-point-offset-y | The offset position for drawer snap point |
--drawer-swipe-movement-x | The drawer swipe movement x value for the Content |
--drawer-swipe-movement-y | The drawer swipe movement y value for the Content |
--drawer-swipe-strength | The drawer swipe strength value for the Content |
--layer-index | The index of the dismissable in the layer stack |
--nested-layer-count | The number of nested drawers |
GrabberIndicator
Renders a <div> element.
| Prop | Default | Type |
|---|---|---|
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
Grabber
Renders a <div> element.
| Prop | Default | Type |
|---|---|---|
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
IndentBackground
Renders a <div> element.
| Prop | Default | Type |
|---|---|---|
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
Indent
Renders a <div> element.
| Prop | Default | Type |
|---|---|---|
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
Positioner
Renders a <div> element.
| Prop | Default | Type |
|---|---|---|
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
| Attribute | Description |
|---|---|
[data-scope] | drawer |
[data-part] | positioner |
[data-state] | "open" | "closed" |
[data-swipe-direction] |
RootProvider
| Prop | Default | Type |
|---|---|---|
value | UseDrawerReturn | |
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. | |
immediate | booleanWhether to synchronize the present change immediately or defer it to the next frame | |
lazyMount | false | booleanWhether to enable lazy mounting |
onExitComplete | VoidFunctionFunction called when the animation ends in the closed state | |
present | booleanWhether the node is present (controlled by the user) | |
skipAnimationOnMount | false | booleanWhether to allow the initial presence animation. |
unmountOnExit | false | booleanWhether to unmount on exit. |
Title
Renders a <h2> element.
| Prop | Default | Type |
|---|---|---|
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
Trigger
Renders a <button> element.
| Prop | Default | Type |
|---|---|---|
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
| Attribute | Description |
|---|---|
[data-scope] | drawer |
[data-part] | trigger |
[data-value] | The value of the item |
[data-state] | "open" | "closed" |
[data-current] | Present when current |
Context
API
| Property | Type |
|---|---|
open | booleanWhether the drawer is open. |
dragging | booleanWhether the drawer is currently being dragged. |
triggerValue | string | nullThe value of the active trigger. |
setTriggerValue | (value: string | null) => voidSet the active trigger value. |
setOpen | (open: boolean) => voidFunction to open or close the menu. |
snapPoints | SnapPoint[]The snap points of the drawer. |
swipeDirection | SwipeDirectionThe swipe direction of the drawer. |
snapPoint | SnapPoint | nullThe currently active snap point. |
setSnapPoint | (snapPoint: SnapPoint | null) => voidFunction to set the active snap point. |
getOpenPercentage | () => numberGet the current open percentage of the drawer. |
getSnapPointIndex | () => numberGet the index of the currently active snap point. |
getContentSize | () => number | nullGet the current main-axis size of the drawer content. |
Accessibility
Complies with the Dialog WAI-ARIA design pattern.
| Key | Description |
|---|---|
Enter | When focus is on the trigger, opens the dialog. |
Tab | Moves focus to the next focusable element within the content. Focus is trapped within the dialog. |
Shift + Tab | Moves focus to the previous focusable element. Focus is trapped within the dialog. |
Esc | Closes the dialog and moves focus to trigger or the defined final focus element |