Component

A headless drawer primitive for React with modal and non-modal modes, side-aware placement, focus management, portaling, and drag-to-close handles.

preview
React
'use client' import Drawer from '@radui/ui/Drawer' export default function DrawerExample() { return ( <Drawer.Root swipeDirection="right"> <Drawer.Trigger>Open drawer</Drawer.Trigger> <Drawer.Portal> <Drawer.Overlay /> <Drawer.Content> <Drawer.Handle /> <Drawer.Title>Project settings</Drawer.Title> <Drawer.Description> Update workspace details without leaving the current page. </Drawer.Description> <div style={{ padding: '0 1.25rem 1.25rem' }}> <Drawer.Close>Close</Drawer.Close> </div> </Drawer.Content> </Drawer.Portal> </Drawer.Root> ) }

Import all parts of the component and piece them together

React
import Drawer from '@radui/ui/Drawer' export default () => { return ( <Drawer.Root> <Drawer.Trigger>Open drawer</Drawer.Trigger> <Drawer.Portal> <Drawer.Overlay /> <Drawer.Content> <Drawer.Handle /> <Drawer.Title>Drawer title</Drawer.Title> <Drawer.Description>Drawer description</Drawer.Description> <Drawer.Close>Close</Drawer.Close> </Drawer.Content> </Drawer.Portal> </Drawer.Root> ) }

The root component that owns drawer state, modal behavior, snap point state, and child drawer nesting.

PropTypeDefault
children
ReactNode
--
defaultOpen
boolean
false
open
boolean
--
onOpenChange
(open: boolean) => void
--
onOpenChangeComplete
(open: boolean) => void
--
modal
boolean | 'trap-focus'
true
swipeDirection
'left' | 'right' | 'top' | 'bottom'
'right'
disablePointerDismissal
boolean
false
snapPoints
Array<number | string>
[]
snapPoint
number | string | null
--
onSnapPointChange
(snapPoint: number | string | null) => void
--
actionsRef
RefObject<DrawerRootActions | null>
--
customRootClass
string
''

A button-like part that opens the drawer.

PropTypeDefault
children
ReactNode
--
asChild
boolean
false
className
string
--

Portals drawer overlay and content outside the normal DOM flow.

PropTypeDefault
children
ReactNode
--

The backdrop layer that can lock scroll and dismiss the drawer.

PropTypeDefault
forceMount
boolean
false
className
string
--

The positioned drawer panel.

PropTypeDefault
children
ReactNode
--
forceMount
boolean
false
asChild
boolean
false
className
string
--

A heading part for naming the drawer content.

PropTypeDefault
children
ReactNode
--
className
string
--

A text part for describing the drawer content.

PropTypeDefault
children
ReactNode
--
className
string
--

A button-like part that closes the drawer intentionally.

PropTypeDefault
children
ReactNode
--
asChild
boolean
false
className
string
--

An optional draggable handle that lets users dismiss the drawer by dragging outward.

PropTypeDefault
closeThreshold
number
80
className
string
--
ShortcutDescription
Tab

Moves focus through tabbable controls inside the drawer. Modal drawers keep focus contained until closed.

Shift + Tab

Moves focus backward through tabbable controls inside the drawer. Modal drawers keep focus contained until closed.

Enter

Activates Drawer.Trigger or Drawer.Close when either control has focus.

Space

Activates Drawer.Trigger or Drawer.Close when either control has focus.

Escape

Closes the drawer unless outside dismissal is disabled by the root configuration.

ReferenceWhat Rad UI follows

Drawer.Content uses dialog semantics and sets aria-modal from the Drawer.Root modal behavior.

Label Drawer.Content with Drawer.Title and describe it with Drawer.Description using aria-labelledby and aria-describedby.

Examples

preview
React
'use client' import Drawer from '@radui/ui/Drawer' const modes = [ { label: 'Modal', value: true }, { label: 'Non-modal', value: false }, { label: 'Trap focus', value: 'trap-focus' }, ] as const export default function DrawerModesExample() { return ( <div style={{ display: 'flex', gap: '0.75rem', flexWrap: 'wrap' }}> {modes.map((mode) => ( <Drawer.Root key={mode.label} modal={mode.value} swipeDirection="right"> <Drawer.Trigger>{mode.label}</Drawer.Trigger> <Drawer.Portal> <Drawer.Overlay /> <Drawer.Content> <Drawer.Title>{mode.label} drawer</Drawer.Title> <Drawer.Description> Use this mode to tune focus, scroll, and outside interaction behavior. </Drawer.Description> <div style={{ padding: '0 1.25rem 1.25rem' }}> <Drawer.Close>Close</Drawer.Close> </div> </Drawer.Content> </Drawer.Portal> </Drawer.Root> ))} </div> ) }
preview
React
'use client' import Drawer from '@radui/ui/Drawer' const directions = ['right', 'left', 'top', 'bottom'] as const export default function DrawerDirectionsExample() { return ( <div style={{ display: 'flex', gap: '0.75rem', flexWrap: 'wrap' }}> {directions.map((direction) => ( <Drawer.Root key={direction} swipeDirection={direction}> <Drawer.Trigger>From {direction}</Drawer.Trigger> <Drawer.Portal> <Drawer.Overlay /> <Drawer.Content> <Drawer.Handle /> <Drawer.Title>Drawer from {direction}</Drawer.Title> <Drawer.Description> The same composition supports each placement direction. </Drawer.Description> <div style={{ padding: '0 1.25rem 1.25rem' }}> <Drawer.Close>Close</Drawer.Close> </div> </Drawer.Content> </Drawer.Portal> </Drawer.Root> ))} </div> ) }