Marble UI
← Components

Drawer

Preview: Bottom

Edge-anchored, swipeable panel built on React Aria Sheet (v1.22+). position: bottom (default) | top | left | right | center. Supports snapPoints.

Import

import { Drawer, DrawerTitle, DrawerTrigger } from '@marbleui/ui'

Props

<Drawer>

PropTypeDefaultDescription
childrenReactNode—

Also accepts React Aria props (85).

<DrawerTitle>

PropTypeDefaultDescription
children *requiredReactNode—
descriptionReactNode—

<DrawerTrigger>alias of SheetTrigger

Re-exported from React Aria SheetTrigger; see its documentation for props.

Accessibility

Keyboard interaction, focus management and ARIA semantics come from the underlying primitives: React Aria Heading · React Aria Sheet · React Aria SheetBackdrop · React Aria SheetContent · React Aria SheetOverlay · React Aria SheetTrigger. Follow the primitive's docs for the full keyboard map.

Usage notes

Swipeable, edge-anchored panel (React Aria Sheet). Bottom sheets on mobile, side panels on desktop.

<DrawerTrigger>
  <Button>Filters</Button>
  <Drawer snapPoints={['40%', '90%']}>
    {({ close }) => (<><DrawerTitle>Filters</DrawerTitle>…<Button onPress={close}>Apply</Button></>)}
  </Drawer>
</DrawerTrigger>
  • position: bottom (default), top, left, right or center.
  • Always render a DrawerTitle; it names the dialog.
  • Users can dismiss by swiping, pressing Escape or tapping the backdrop.
Example source · show
import { Button } from '../button'
import { Checkbox, CheckboxGroup } from '../checkbox'
import { Drawer, DrawerTitle, DrawerTrigger } from './drawer'

export const styles = {
  Bottom: () => (
    <DrawerTrigger>
      <Button variant="outline">Open bottom drawer</Button>
      <Drawer>
        {({ close }) => (
          <div className="flex flex-col pt-2">
            <DrawerTitle description="Choose what shows up in your feed.">Filters</DrawerTitle>
            <CheckboxGroup aria-label="Filters" defaultValue={['open']}>
              <Checkbox value="open">Open issues</Checkbox>
              <Checkbox value="mine">Assigned to me</Checkbox>
              <Checkbox value="blocked">Blocked</Checkbox>
            </CheckboxGroup>
            <Button className="mt-6" onPress={close}>Apply</Button>
          </div>
        )}
      </Drawer>
    </DrawerTrigger>
  ),
  Right: () => (
    <DrawerTrigger>
      <Button variant="outline">Open side panel</Button>
      <Drawer position="right">
        {({ close }) => (
          <div className="flex h-full flex-col">
            <DrawerTitle description="ACME-142 · In progress">Fix login redirect</DrawerTitle>
            <p className="text-sm leading-relaxed text-fg-muted">
              After signing in from a deep link, users land on the dashboard instead of the page they asked for.
            </p>
            <Button className="mt-auto" variant="soft" onPress={close}>Close</Button>
          </div>
        )}
      </Drawer>
    </DrawerTrigger>
  ),
  Snap: () => (
    <DrawerTrigger>
      <Button variant="outline">Open with snap points</Button>
      <Drawer snapPoints={['40%', '90%']}>
        <div className="pt-2">
          <DrawerTitle description="Drag up to expand.">Comments</DrawerTitle>
          <div className="flex flex-col gap-3">
            {Array.from({ length: 12 }, (_, i) => (
              <div key={i} className="rounded-md bg-surface-muted p-3 text-sm text-fg-muted">
                Comment {i + 1}: looks good to me, ship it.
              </div>
            ))}
          </div>
        </div>
      </Drawer>
    </DrawerTrigger>
  ),
}

Source: packages/ui/src/drawer/drawer.tsx

Catalog

Category
overlays
Priority
Foundation
Primitive
react-aria
React Aria
Sheet
Base UI
Drawer