# Drawer

> A side drawer that follows the finger from the screen edge: the scrim darkens with its progress and, in the card variant, the screen behind shrinks, rounds and slides aside on the same progress. Release settles it with the finger's velocity; Escape, the back button or a tap on the scrim closes it.

- Type: Component (navigation)
- Access: Free, open source (MIT)
- Page: https://goodui.dev/native/components/drawer
- Markdown: https://goodui.dev/native/components/drawer.md
- Registry item: https://goodui.dev/native/r/drawer.json
- Dependencies: react-native-gesture-handler, react-native-reanimated
- Keywords: react native drawer, side menu, navigation drawer, hamburger menu, swipe from edge, slide out menu, filter drawer, expo drawer

## When to use

- App navigation with more destinations than a tab bar holds: mailboxes, workspaces, accounts.
- A filter or settings panel beside a list or map on a wide or busy screen.
- An account or workspace switcher that should stay one swipe away.

## When not to use

- Three to five top-level destinations: use tab-bar, which keeps them visible.
- A short list of actions for one item: use action-sheet or context-menu.
- Content that comes up from the bottom or needs detents: use bottom-sheet.
- Switching between views of the same content: use segmented-control or top-tabs.

## Installation

With the shadcn CLI (needs a components.json in the Expo app), then `npx expo install --fix` so native dependencies match your Expo SDK. The foundation comes along the first time.

```bash
npx shadcn@latest add https://goodui.dev/native/r/drawer.json
npx expo install --fix
```

The component lands in `components/goodui-native/drawer/` and imports the foundation from `components/goodui-native/foundation/` with relative paths; it also installs locale.

Built-in strings follow `<GooduiLocaleProvider>` (one [language pack](https://goodui.dev/native/r/locale-tr.json) translates the whole kit); the `labels` prop overrides them for one instance.

## Usage

```tsx
import { Pressable, Text, View } from "react-native";
import { Drawer, useDrawer } from "@/components/goodui-native/drawer/drawer";

function Menu() {
  const { close } = useDrawer();
  return (
    <View style={{ padding: 16, gap: 12 }}>
      <Pressable accessibilityRole="button" onPress={close}>
        <Text>Inbox</Text>
      </Pressable>
    </View>
  );
}

function Inbox() {
  const { toggle } = useDrawer();
  return (
    <Pressable accessibilityRole="button" onPress={toggle}>
      <Text>Open menu</Text>
    </Pressable>
  );
}

export function MailScreen() {
  return (
    <Drawer variant="card" accessibilityLabel="Mailboxes" content={<Menu />}>
      <Inbox />
    </Drawer>
  );
}
```

## Examples

### Controlled filter drawer from the right

```tsx
const [open, setOpen] = useState(false);

<Drawer side="right" width={300} open={open} onOpenChange={setOpen} accessibilityLabel="Filters" content={<Filters onApply={() => setOpen(false)} />}>
  <Results onFilters={() => setOpen(true)} />
</Drawer>
```

### Embedded in part of the screen

```tsx
<View style={{ height: 480, borderRadius: 20, overflow: "hidden" }}>
  <Drawer insets={false} content={<Menu />}>
    <Preview />
  </Drawer>
</View>
```

## API reference

### Drawer

`import { Drawer } from "@/components/goodui-native/drawer/drawer";`

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `children` (required) | `ReactNode` | – | The screen the drawer opens over. Fills the Drawer, which fills its parent. |
| `content` (required) | `ReactNode` | – | What the drawer shows: navigation, filters, account switcher. It mounts when the drawer starts to open. |
| `open` | `boolean` | – | Open state (controlled). Pair it with `onOpenChange`. |
| `defaultOpen` | `boolean` | `false` | Open state on first render (uncontrolled). |
| `onOpenChange` | `(open: boolean) => void` | – | Called when a swipe, a tap on the scrim, Escape or the back button opens or closes it. |
| `side` | `"end" \| "left" \| "right" \| "start"` | `"start"` | Edge the drawer comes from. Default `start`: the left, or the right under RTL. |
| `variant` | `"overlay" \| "card"` | `"overlay"` | `overlay` slides the drawer over the screen above a fading scrim. `card` slides the screen aside as a shrinking, rounding card and reveals the drawer underneath. |
| `width` | `number` | – | Drawer width in points. Default 82 % of the Drawer's width, at most 320. |
| `edgeWidth` | `number` | `24` | Width of the strip at the edge that starts an open swipe. Default 24. `0` turns edge swipes off. |
| `insets` | `boolean` | `true` | Pad the drawer by the safe area. Turn off when the Drawer is not full screen. Default `true`. |
| `haptic` | `HapticName \| false` | `"tap"` | Haptic when a swipe or a tap commits open or closed. `false` turns it off. |
| `accessibilityLabel` | `string` | – | Screen reader name of the drawer. Same as `labels.menu`. |
| `labels` | `Partial<DrawerLabels>` | – | Overrides for the built-in strings; a GooduiLocaleProvider pack sets them app-wide. |
| `style` | `StyleProp<ViewStyle>` | – | Style of the outer container. |
| `panelStyle` | `StyleProp<ViewStyle>` | – | Style of the drawer surface, for example a different background. |
| `testID` | `string` | – | Identifier for end-to-end tests. |

## Accessibility

- The open drawer is a modal dialog named by accessibilityLabel (accessibilityViewIsModal on iOS, role dialog with aria-modal on the web); the screen behind is hidden from screen readers while it is open.
- The scrim is a button named 'Close <label>' with activate and escape actions; Escape closes on the web and the back button closes on Android.
- Opening and closing are announced ('Menu opened').
- The closed drawer's content is unmounted, so it is never reachable by keyboard or screen reader; the edge strip is hidden from them.

## Motion

- One progress value drives everything: the drawer's position (1:1 with the finger), the scrim's opacity and, in the card variant, the screen's scale (to 0.86), corner radius (to radius.xl) and slide.
- Release projects the velocity: past halfway (with a flick counted in) it opens, otherwise it closes, on the smooth spring carrying the finger's velocity.
- Overlay: the screen behind shifts 10 % of the drawer width as the drawer arrives. Card: the drawer underneath eases in from behind and fades up.
- Reduced motion: opening and closing from a button fade the drawer and scrim instead of sliding; a swipe still follows the finger and settles with a short timing; no parallax.

## Haptics

- tap when a swipe or a scrim tap commits open or closed (configurable with haptic); none for programmatic changes

## Performance

- All motion runs on the UI thread from a single shared value; React state changes only when the drawer commits or finishes closing.
- Drawer content mounts when the drawer starts opening and unmounts after it closes.

## Notes for AI

- Drawer wraps the screen: children is the screen, content is the drawer. It fills its parent (flex: 1); give an embedded one a height.
- Inside either side, call useDrawer() for open, setOpen, toggle and close. A menu item should close() after navigating.
- Pass insets={false} when the Drawer is not full screen, or the drawer gets safe-area padding it does not need.
- Drawer content sits on the background color (overlay) or a quiet surfaceMuted fill (card); put ListSection and ListRow in it for a native menu.
- Do not wrap the whole app's navigator in a Drawer just for one screen; scope it to the screens that have the menu.
- edgeWidth={0} turns off the edge swipe, for example on a screen with its own horizontal gestures at the edge.

## Related

- [Tab bar](https://goodui.dev/native/components/tab-bar.md): A bottom tab bar whose pill stretches toward the next tab and can be dragged to scrub between tabs; the selected icon bounces, badges pop, and it can hide while scrolling.
- [Bottom sheet](https://goodui.dev/native/components/bottom-sheet.md): A sheet that rests at detents, follows the finger 1:1, settles with velocity and hands drags off to its scroll view.
- [List row](https://goodui.dev/native/components/list-row.md): Settings and navigation cells with a token-colored icon tile, title, subtitle and a trailing value, switch, badge, check or chevron, grouped in inset sections with headers and footers. On touch down the highlight floods in, the tile sinks and the chevron leans forward; on release the highlight lingers and fades.
- [Top tabs](https://goodui.dev/native/components/top-tabs.md): A swipeable pager under a tab strip whose indicator tracks the swipe continuously: the leading edge runs ahead and the trailing edge follows, so it stretches between tabs and gathers as the page lands. Labels recolor with the swipe and a scrolling strip keeps the indicator centred.

## Guidance for AI tools

Drawer: A side drawer that follows the finger from the screen edge: the scrim darkens with its progress and, in the card variant, the screen behind shrinks, rounds and slides aside on the same progress. Release settles it with the finger's velocity; Escape, the back button or a tap on the scrim closes it. Use only the documented props. Keep accessibility, reduced motion and both color schemes intact when adapting it.

Full library index: https://goodui.dev/native/llms.txt
