# Drawer

> A modal panel that slides in from a screen edge for details, filters and secondary tasks.

Category: Overlays · Status: stable · Package: @bindshq/stance-react@0.1.1 · Docs: https://stanceui.com/components/drawer

Drawer keeps the page in context while showing longer content than a Dialog comfortably holds, such as a user’s profile or a filter panel. It shares Dialog’s behaviour: focus trap, scroll lock, Escape to close and focus return. Side panels never exceed the viewport width on phones.

## Import

```tsx
import { Drawer, DrawerTrigger, DrawerContent, DrawerHeader, DrawerTitle, DrawerDescription, DrawerBody, DrawerFooter, DrawerClose } from '@bindshq/stance-react';
```

Styles are imported once per app: `@bindshq/stance-tokens/css` and `@bindshq/stance-react/styles.css`.

## Examples

### Basic

```tsx
import { Button, Drawer, DrawerBody, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerTitle, DrawerTrigger, FormField, Input } from '@bindshq/stance-react';

export function Basic() {
  return (
    <Drawer>
      <DrawerTrigger asChild>
        <Button variant="outline">Edit user</Button>
      </DrawerTrigger>
      <DrawerContent>
        <DrawerHeader>
          <DrawerTitle>Ada Lovelace</DrawerTitle>
          <DrawerDescription>Head of Operations · London</DrawerDescription>
        </DrawerHeader>
        <DrawerBody>
          <div style={{ display: 'grid', gap: 16 }}>
            <FormField label="Job title">
              <Input defaultValue="Head of Operations" />
            </FormField>
            <FormField label="Direct phone" description="Shown in the signature.">
              <Input type="tel" defaultValue="+44 20 7946 0958" />
            </FormField>
          </div>
        </DrawerBody>
        <DrawerFooter>
          <DrawerClose asChild>
            <Button variant="outline">Cancel</Button>
          </DrawerClose>
          <DrawerClose asChild>
            <Button>Save</Button>
          </DrawerClose>
        </DrawerFooter>
      </DrawerContent>
    </Drawer>
  );
}
```

### Sides

Slide from any edge. Use left for navigation, right for details.

```tsx
import { Button, Drawer, DrawerBody, DrawerContent, DrawerHeader, DrawerTitle, DrawerTrigger } from '@bindshq/stance-react';

export function Sides() {
  return (
    <div style={{ display: 'flex', flexWrap: 'wrap', gap: 12 }}>
      {(['left', 'right', 'top', 'bottom'] as const).map((side) => (
        <Drawer key={side}>
          <DrawerTrigger asChild>
            <Button variant="outline">From {side}</Button>
          </DrawerTrigger>
          <DrawerContent side={side}>
            <DrawerHeader>
              <DrawerTitle>Drawer from the {side}</DrawerTitle>
            </DrawerHeader>
            <DrawerBody>Press Escape or click outside to close.</DrawerBody>
          </DrawerContent>
        </Drawer>
      ))}
    </div>
  );
}
```

### Sizes

```tsx
import { Button, Drawer, DrawerBody, DrawerContent, DrawerHeader, DrawerTitle, DrawerTrigger } from '@bindshq/stance-react';

export function Sizes() {
  return (
    <div style={{ display: 'flex', flexWrap: 'wrap', gap: 12 }}>
      {(['sm', 'md', 'lg'] as const).map((size) => (
        <Drawer key={size}>
          <DrawerTrigger asChild>
            <Button variant="secondary">Size {size}</Button>
          </DrawerTrigger>
          <DrawerContent size={size}>
            <DrawerHeader>
              <DrawerTitle>Size “{size}”</DrawerTitle>
            </DrawerHeader>
            <DrawerBody>Side drawers never exceed the viewport width.</DrawerBody>
          </DrawerContent>
        </Drawer>
      ))}
    </div>
  );
}
```

## API reference

| Component | Prop | Type | Default | Description |
| --- | --- | --- | --- | --- |
| Drawer | `open / defaultOpen / onOpenChange` | `boolean / boolean / (open: boolean) => void` | None | Same as Dialog. |
| DrawerTrigger | `asChild` | `boolean` | `false` | Render your own Button as the trigger. |
| DrawerContent | `side` | `'left' \| 'right' \| 'top' \| 'bottom'` | `'right'` | Edge the drawer slides from. |
| DrawerContent | `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Width (left/right: 320, 416, 640px) or height (top/bottom: 33%, 50%, 85%). |
| DrawerContent | `showClose / closeLabel` | `boolean / string` | `true / 'Close'` | Close button. |
| DrawerContent | `dismissOnOutsideClick` | `boolean` | `true` | Close on backdrop click. |
| DrawerContent | `initialFocus` | `RefObject<HTMLElement>` | None | Element focused on open. |
| DrawerTitle | `level` | `1 \| 2 \| 3 \| 4` | `2` | Heading level; the accessible name. |
| DrawerHeader / DrawerBody / DrawerFooter / DrawerDescription | `...props` | `HTMLAttributes` | None | Layout parts shared with Dialog. The body scrolls. |
| DrawerClose | `asChild` | `boolean` | `false` | Closes the drawer. |

## Accessibility

- A modal dialog (role="dialog", aria-modal) named by DrawerTitle.
- Focus is trapped inside and returned to the trigger on close.
- Respects prefers-reduced-motion: the slide animation is removed.

## Keyboard

| Keys | Action |
| --- | --- |
| `Tab` `Shift+Tab` | Cycle focus inside the drawer. |
| `Esc` | Closes the drawer. |

## Guidelines

**Do**

- Use for detail views that benefit from keeping the list visible behind.
- Put persistent actions in DrawerFooter.

**Don’t**

- Use as primary app navigation on desktop; use Sidebar.

## Related

- https://stanceui.com/components/dialog
- https://stanceui.com/components/sheet
- https://stanceui.com/components/sidebar
