Stable in React
Drawer
A panel that slides in from the edge of the screen.
Install#
One package per framework. Drawer's parts come from it; the anatomy tab at the top shows how they nest.
bun add @▢▢▢▢/react15 parts. The example uses 10 of them; the data does not say which are required.
Drawer.RootGroups all parts of the drawer.Drawer.TriggerA button that opens the drawer.Drawer.PortalA portal element that moves the popup to a different part of the DOM.Drawer.BackdropAn overlay displayed beneath the popup.Drawer.ViewportA positioning container for the drawer popup that can be made scrollable.Drawer.PopupA container for the drawer contents.Drawer.ContentA container for the drawer contents.Drawer.TitleA heading that labels the drawer.Drawer.DescriptionA paragraph with additional information about the drawer.Drawer.CloseA button that closes the drawer.Drawer.IndentA wrapper element intended to contain your app's main UI. Not in the example.Drawer.IndentBackgroundAn element placed before <Drawer.Indent> to render a background layer that can be styled based on whether any drawer is open. Not in the example.Drawer.ProviderProvides a shared context for coordinating global Drawer UI, such as indent/background effects based on whether any Drawer is open. Not in the example.Drawer.SwipeAreaAn invisible area that listens for swipe gestures to open the drawer. Not in the example.Drawer.VirtualKeyboardProviderProvides keyboard-aware focus and scroll handling for bottom-sheet drawers with form fields. Not in the example.
Examples#
Common ways to put it to work. Each one runs live.
swipeDirection matches the side the drawer comes from.
DrawerPosition.tsxStyling and accessibility#
The parts ship no styles. Style their states with these attributes; the CSS tab at the top has a sheet. The same CSS works in all five frameworks.
[data-disabled]Present when the trigger is disabled. On Trigger, Close, SwipeArea.[data-popup-open]Present when the corresponding dialog is open. On Trigger.[data-open]Present when the drawer is open. On Backdrop, Viewport, Popup, SwipeArea.[data-closed]Present when the drawer is closed. On Backdrop, Viewport, Popup, SwipeArea.[data-starting-style]Present when the drawer begins animating in. On Backdrop, Viewport, Popup.[data-ending-style]Present when the drawer is animating out. On Backdrop, Viewport, Popup.--drawer-swipe-progressThe swipe progress of the drawer gesture. On Backdrop.[data-nested]Present when the drawer is nested within another drawer. On Viewport.--drawer-keyboard-insetThe software keyboard inset, measured from the bottom edge of the layout viewport. Present only when the drawer is wrapped inDrawer.VirtualKeyboardProvider. On Viewport.[data-expanded]Present when the drawer is at the expanded (full-height) snap point. On Popup.[data-nested-drawer-open]Present when a nested drawer is open. On Popup.[data-nested-drawer-swiping]Present when a nested drawer is being swiped. On Popup.[data-swipe-dismiss]Present when the drawer is dismissed by swiping. On Popup.[data-swipe-direction]Indicates the swipe direction. On Popup, SwipeArea.[data-swiping]Present when the drawer is being swiped. On Popup, SwipeArea.--nested-drawersThe number of nested drawers that are currently open. On Popup.--drawer-heightThe height of the drawer popup. On Popup.--drawer-frontmost-heightThe height of the frontmost open drawer in the current nested drawer stack. On Popup.--drawer-swipe-movement-xThe swipe movement on the X axis. On Popup.--drawer-swipe-movement-yThe swipe movement on the Y axis. On Popup.--drawer-snap-point-offsetThe snap point offset used for translating the drawer. On Popup.--drawer-swipe-strengthA scalar (0.1-1) used to scale the swipe release transition duration in CSS. On Popup.
Keyboard
Drawer takes Dialog's keys: Escape closes it, and Tab stays inside the popup while it is modal.
Set for you
a screen reader says the button opens a dialog and whether it is openTriggeraria-haspopup="dialog",aria-expanded,aria-controlsthe drawer is announced as a dialog with its heading and descriptionPopuprole="dialog",aria-labelledbyto Title,aria-describedbyto Descriptiona screen reader stays inside the drawerPopuparia-hidden="true"on the rest of the page whilemodalis onthe overlay is not readBackdroprole="presentation"the invisible gesture area is not read or reachedSwipeArearole="presentation",aria-hidden="true"
API reference#
31 props across 15 parts, in React syntax, and className, style, render once for every part. Open a row to try it.
Drawer · all partsclassNameCSS class applied to the element, or a function that
returns a class based on the component's state.string | ((state: State) => string | undefined)styleStyle applied to the element, or a function that
returns a style object based on the component's state.React.CSSProperties | ((state: State) => React.CSSProperties | undefined)renderAllows you to replace the component's HTML element
with a different tag, or compose it with another component.React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, State>Use it in React#
What differs from the React API. Switch the framework at the top of the page.
@▢▢▢▢/react is the React 19 package. Its parts, props, data attributes and error messages are upstream Base UI's, and upstream's own tests run against it.
- Parts
Each component is its own entry point and exports one namespace named after it.
import { Switch } from '@base-ui-next/react/switch' export function Example() { return ( <Switch.Root> <Switch.Thumb /> </Switch.Root> ) }A part renders the element its API reference names, such as a
<span>forSwitch.Root. - Props and events
Props and callbacks have upstream's names and signatures:
onCheckedChange(checked, details),onOpenChange(open, details),onValueChange(value, details).detailscarriesreason,event,cancel()andallowPropagation(). Native handlers are React's (onClick,onKeyDown), andevent.preventBaseUIHandler()stops a part's own handler. - Styling
classNametakes a string or a function(state) => string;styletakes an object or a function(state) => object. The state'sdata-*attributes are on the element for CSS. renderrendertakes an element (render={<a href="/docs" />}) or a function(props, state) => elementthat spreadspropson what it returns.- Refs
refreaches the part's element. Imperative handles are ref props, as upstream:inputRef, andactionsRef({ validate }on Field and Form,{ unmount, close }on Dialog). - Binding
A part is controlled when its value prop (
checked,open,value) is set, and uncontrolled with thedefaultform (defaultChecked,defaultOpen,defaultValue). React has no two-way binding: pair the value with its change callback. - From @base-ui/reactChange the import from
@base-ui/react/<component>to@▢▢▢▢/react/<component>. - From @base-ui/reactNothing else changes: props, parts, data attributes and errors are the same.