Skip to content
Esc
navigateopen⌘Jpreview
On this page

Drawer

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

import { Button } from '@/components/ui/button';
import {
  Drawer,
  DrawerClose,
  DrawerContent,
  DrawerDescription,
  DrawerFooter,
  DrawerHeader,
  DrawerTitle,
  DrawerTrigger,
} from '@/components/ui/drawer';

export default function DrawerDemo() {
  return (
    <Drawer showSwipeHandle>
      <DrawerTrigger render={<Button variant="outline" />}>
        Open drawer
      </DrawerTrigger>
      <DrawerContent>
        <DrawerHeader>
          <DrawerTitle>Move goal</DrawerTitle>
          <DrawerDescription>Set your daily activity goal.</DrawerDescription>
        </DrawerHeader>
        <DrawerFooter>
          <DrawerClose render={<Button />}>Submit</DrawerClose>
          <DrawerClose render={<Button variant="outline" />}>Cancel</DrawerClose>
        </DrawerFooter>
      </DrawerContent>
    </Drawer>
  );
}

Install

npx shadcn@latest add @madeui/drawer

Usage

import {
  Drawer,
  DrawerClose,
  DrawerContent,
  DrawerDescription,
  DrawerFooter,
  DrawerHeader,
  DrawerTitle,
  DrawerTrigger,
} from '@/components/ui/drawer';

Composition

<Drawer>
  <DrawerTrigger />
  <DrawerContent>
    <DrawerHeader>
      <DrawerTitle />
      <DrawerDescription />
    </DrawerHeader>
    <DrawerFooter>
      <DrawerClose />
    </DrawerFooter>
  </DrawerContent>
</Drawer>

Directions

swipeDirection sets which edge the drawer rests on and swipes back toward.

import * as stylex from '@stylexjs/stylex';

import { Button } from '@/components/ui/button';
import {
  Drawer,
  DrawerContent,
  DrawerDescription,
  DrawerHeader,
  DrawerTitle,
  DrawerTrigger,
  type DrawerSwipeDirection,
} from '@/components/ui/drawer';
import { space } from '@/lib/constants.stylex';

const directions: DrawerSwipeDirection[] = ['up', 'right', 'down', 'left'];

export default function DrawerDirections() {
  return (
    <div {...stylex.props(styles.row)}>
      {directions.map((direction) => (
        <Drawer key={direction} swipeDirection={direction}>
          <DrawerTrigger render={<Button variant="outline" />}>
            {direction}
          </DrawerTrigger>
          <DrawerContent>
            <DrawerHeader>
              <DrawerTitle>Drawer from {direction}</DrawerTitle>
              <DrawerDescription>
                Rests on and swipes back toward the {direction} edge.
              </DrawerDescription>
            </DrawerHeader>
          </DrawerContent>
        </Drawer>
      ))}
    </div>
  );
}

const styles = stylex.create({
  row: {
    display: 'flex',
    flexWrap: 'wrap',
    gap: space.s2,
  },
});

API reference

Built on Base UI Drawer. Swipe-to-dismiss and snap points come from the primitive — Base UI drives the swipe offsets through CSS variables that the styles consume. All props are forwarded to the Base UI Root (open, snapPoints, modal, swipeDirection, …); see the Base UI Drawer API reference.

Unlike Sheet (a fixed side panel on the Dialog primitive), Drawer is gesture-driven — prefer it on touch-first surfaces.

Drawer

Prop Type Default Description
swipeDirection 'down' | 'up' | 'left' | 'right' 'down' Edge the drawer rests on and dismisses toward.
showSwipeHandle boolean false Renders a grab handle on the swipe edge.

Nested drawer stacking (scale/peek of the drawer behind) is not styled yet. Every part accepts a style prop (StyleXStyles, merged last).

Was this page helpful?