# Family Drawer

> A React bottom drawer that floats above the page and morphs its height between stacked views, inspired by the Family app.

- Type: React component (Overlays)
- Access: Free (the source is open to everyone)
- Version: 1.0.1
- Install: `npx wingo-ui@latest add family-drawer`
- Docs: https://wingo-ui.com/components/family-drawer

## Interaction

Press the trigger to open a floating bottom drawer with an Options menu. Pick an option and the drawer morphs into that view: its height eases to fit the new content while the old view fades and shrinks away and the new one scales in, with the crossfade timed to how far the height has to travel. Cancel, Reveal or Continue step back to the menu. Drag the drawer down, tap the backdrop, press Escape or use the close button to dismiss it. Buttons press in on tap.

## Installation

```bash
npx wingo-ui@latest add family-drawer
```

The CLI adds the files, their dependencies and a version record for updates.

With shadcn: `npx shadcn@latest add https://wingo-ui.com/r/family-drawer.json`

Files:

- `components/ui/family-drawer.tsx`

npm dependencies: `npm i clsx motion react-use-measure vaul`

Wingo UI dependencies (installed automatically):

- [UIProvider](https://wingo-ui.com/components/ui-config) ([Markdown](https://wingo-ui.com/components/ui-config.md))

## Usage

```tsx
import FamilyDrawer from "@/components/ui/family-drawer"

export function Demo() {
  return <FamilyDrawer triggerLabel="Wallet options" />
}

// your own views: each one can morph to another view or close the drawer
// <FamilyDrawer
//   triggerLabel="Delete project"
//   views={{
//     default: ({ setView }) => <button onClick={() => setView("confirm")}>Delete</button>,
//     confirm: ({ close }) => <button onClick={close}>Yes, delete it</button>,
//   }}
// />
```

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `triggerLabel?` | `string` | `"Try it out"` | Text on the built-in trigger button. Ignored when you pass your own trigger. |
| `title?` | `string` | `"Options"` | Heading of the built-in menu view, also the drawer's accessible name. |
| `closeLabel?` | `string` | `"Close"` | Accessible name of the round close button. |
| `defaultView?` | `string` | `"default"` | The view shown first. The built-in views are default, key, phrase and remove; with your own views, any key of views. |
| `size?` | `"sm" \| "md" \| "lg"` | `"md"` | Maximum width of the drawer: 320px (sm), 361px (md) or 420px (lg). It always keeps a 16px margin on phones. |
| `radius?` | `number` | `36` | Corner radius of the drawer in pixels. |
| `accent?` | `string` | `"#4DAFFF"` | Fill of the primary action in the built-in views (Reveal). |
| `danger?` | `string` | `"#FF3F40"` | Any CSS color for the destructive action in the built-in views: the Remove Wallet row (tinted to 12%) and the Continue button. |
| `surface?` | `string` | `""` | Any CSS color for the drawer panel. Empty uses #FEFFFE light and the popover token dark. The built-in views keep the theme's text and button colors, so pick a surface that suits the theme (or restyle your own views). |
| `showCloseButton?` | `boolean` | `true` | Shows the round close button in the top right corner (32px, with a 44px hit area on touch screens). |
| `dismissible?` | `boolean` | `true` | Lets a downward drag, a backdrop tap or Escape close the drawer. Turn it off to require the close button or an action inside a view. |
| `defaultOpen?` | `boolean` | `false` | Starts open when uncontrolled. Ignored when open is set. |
| `open?` | `boolean` |  | Controlled open state. Pass it with onOpenChange to drive the drawer yourself. |
| `onOpenChange?` | `(open: boolean) => void` |  | Called with the next open state from the trigger, the close button, a drag, the backdrop or Escape. |
| `trigger?` | `ReactNode` |  | Your own trigger element, rendered as the drawer trigger through asChild. Replaces the built-in button. |
| `views?` | `Record<string, ReactNode \| ((api: { view, setView, close }) => ReactNode)>` |  | Your own views keyed by name, replacing the built-in wallet views. A function receives setView to morph to another view and close to dismiss the drawer. |
| `onViewChange?` | `(view: string) => void` |  | Called with the name of the view the drawer morphs to. |
| `className?` | `string` |  | Extra classes merged onto the drawer panel, for example a different surface color. |

## Parts

### DefaultView (component)

The built-in Options menu: a title row and three option buttons that switch to the key, phrase and remove views.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `setView` | `(view: string) => void` |  | Switches the drawer to another view. |
| `title?` | `string` | `"Options"` | The heading. |
| `danger?` | `string` | `"#FF3F40"` | Color of the Remove Wallet row. |

### Key (component)

The built-in Private Key view: header, three safety notes, Cancel and Reveal.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `setView` | `(view: string) => void` |  | Both buttons switch back to the default view. |
| `accent?` | `string` | `"#4DAFFF"` | Fill of the Reveal button. |

### Phrase (component)

The built-in Secret Recovery Phrase view, laid out like Key.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `setView` | `(view: string) => void` |  | Both buttons switch back to the default view. |
| `accent?` | `string` | `"#4DAFFF"` | Fill of the Reveal button. |

### RemoveWallet (component)

The built-in confirmation view: a warning header with Cancel and Continue.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `setView` | `(view: string) => void` |  | Both buttons switch back to the default view. |
| `danger?` | `string` | `"#FF3F40"` | Fill of the Continue button. |

### Header (component)

The icon, title and description block at the top of a detail view. Use it in your own views to match.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `icon` | `ReactNode` |  | The 48px icon above the title. |
| `title` | `ReactNode` |  | The 22px heading. |
| `description` | `ReactNode` |  | The muted paragraph under it. |

### Button (component)

A 48px gray menu row with an icon and a label, as in the Options view.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `children` | `ReactNode` |  | An icon and the label. |
| `onClick?` | `() => void` |  | Called on press. |

### SecondaryButton (component)

A 48px pill button for the actions at the bottom of a view; color it with className or style.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `children` | `ReactNode` |  | The label, optionally with an icon. |
| `onClick?` | `() => void` |  | Called on press. |
| `className?` | `string` |  | Extra classes, e.g. the fill and text color. |
| `style?` | `CSSProperties` |  | Inline styles, e.g. a backgroundColor. |

### LockIcon (component)

The 24px lock glyph of the View Private Key row. It takes no props, like the other stock glyphs below.

### PhraseIcon (component)

The 24px glyph of the View Recovery Phrase row.

### WarningIcon (component)

The 21px warning glyph of the Remove Wallet row.

### RecoveryPhraseIcon (component)

The 48px header icon of the Private Key and Recovery Phrase views.

### DangerIcon (component)

The 48px red header icon of the Remove Wallet confirmation.

### ShieldIcon (component)

The 24px shield glyph of the first safety note in the detail views.

### PassIcon (component)

The 24px glyph of the second safety note in the detail views.

### BannedIcon (component)

The 24px glyph of the third safety note in the detail views.

### FaceIDIcon (component)

The 20px Face ID glyph inside the Reveal button.

### CloseIcon (component)

The 12px cross inside the round close button.

### CrossIcon (component)

A 12px cross glyph, a spare for your own dismiss buttons.

### FamilyDrawerApi (type)

What a view function receives: { view, setView, close }.

### FamilyDrawerView (type)

A view: plain content, or (api: FamilyDrawerApi) => ReactNode to switch views or close the drawer.

## Examples

- **Confirm a destructive action**: Your own views with Header, DangerIcon and SecondaryButton: Delete morphs into a done state, Cancel closes.
- **Share menu with your own trigger**: trigger replaces the built-in button; the stock Button rows make a share menu that morphs into a confirmation.
- **Controlled**: open and onOpenChange drive it from your state; onViewChange reports each view, here with a green accent and the lg width.

## See also

- [Drawer](https://wingo-ui.com/components/drawer) ([Markdown](https://wingo-ui.com/components/drawer.md))
- [Sheet](https://wingo-ui.com/components/sheet) ([Markdown](https://wingo-ui.com/components/sheet.md))
- [Action Sheet](https://wingo-ui.com/components/action-sheet) ([Markdown](https://wingo-ui.com/components/action-sheet.md))
- [Alert Dialog](https://wingo-ui.com/components/alert-dialog) ([Markdown](https://wingo-ui.com/components/alert-dialog.md))
- [Dialog](https://wingo-ui.com/components/dialog) ([Markdown](https://wingo-ui.com/components/dialog.md))

## Credits

- Inspired by the Family app (family.co)

---

Canonical: https://wingo-ui.com/components/family-drawer
More components: https://wingo-ui.com/components · Index for agents: https://wingo-ui.com/llms.txt
