Components
Component
productdevbook/ui

Stable in React

Drawer

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

Loading React 19.3.1…
Hover a part or use it. The code follows. · Loading React…
579Tests passing in React · 0 failing
45.5 KiBSize
2.00 msOpen
1.90 msClose
0.78 MiBMemory
productdevbook and 13 upstream authors8 commits · last

Install#

One package per framework. Drawer's parts come from it; the anatomy tab at the top shows how they nest.

bun add @▢▢▢▢/react

15 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.tsx

Styling 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 in Drawer.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

  • aria-haspopup="dialog", aria-expanded, aria-controlsa screen reader says the button opens a dialog and whether it is openTrigger
  • role="dialog", aria-labelledby to Title, aria-describedby to Descriptionthe drawer is announced as a dialog with its heading and descriptionPopup
  • aria-hidden="true" on the rest of the page while modal is ona screen reader stays inside the drawerPopup
  • role="presentation"the overlay is not readBackdrop
  • role="presentation", aria-hidden="true"the invisible gesture area is not read or reachedSwipeArea

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 parts
Shared propsEvery part that renders an element takes these.
classNameCSS 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> for Switch.Root.

  • Props and events

    Props and callbacks have upstream's names and signatures: onCheckedChange(checked, details), onOpenChange(open, details), onValueChange(value, details). details carries reason, event, cancel() and allowPropagation(). Native handlers are React's (onClick, onKeyDown), and event.preventBaseUIHandler() stops a part's own handler.

  • Styling

    className takes a string or a function (state) => string; style takes an object or a function (state) => object. The state's data-* attributes are on the element for CSS.

  • render

    render takes an element (render={<a href="/docs" />}) or a function (props, state) => element that spreads props on what it returns.

  • Refs

    ref reaches the part's element. Imperative handles are ref props, as upstream: inputRef, and actionsRef ({ 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 the default form (defaultChecked, defaultOpen, defaultValue). React has no two-way binding: pair the value with its change callback.

  • From @base-ui/react
    Change the import from @base-ui/react/<component> to @▢▢▢▢/react/<component>.
  • From @base-ui/react
    Nothing else changes: props, parts, data attributes and errors are the same.