Drawer
A panel that slides up from the bottom of a phone screen for a short task or a list of actions, within thumb reach, with the page still visible behind it. Also known as a bottom sheet.
Preview
Mirrored is the same English content laid out right to left: it separates a mirroring bug from a translation one.
Installation
npx shadcn@latest add @ritla/drawerDependencies it brings: @base-ui/react, @phosphor-icons/react
Usage
import { Drawer, DrawerBody, DrawerContent, DrawerHeader, DrawerMenu, DrawerMenuItem, DrawerTrigger } from "@/components/ui/drawer";<Drawer>
<DrawerTrigger render={<Button />}>Move email</DrawerTrigger>
<DrawerContent>
<DrawerHeader title="Move to" />
<DrawerBody>
<DrawerMenu>
<DrawerMenuItem leadingIcon={TrayIcon} onSelect={moveToInbox}>Inbox</DrawerMenuItem>
</DrawerMenu>
</DrawerBody>
</DrawerContent>
</Drawer>Examples
A short task
The content is one task (filters) ending with a secondary, then a primary button; the primary sits at the inline end. It holds choices, so a tap on the overlay doesn't close it (disablePointerDismissal).
A long list
The sheet stops at 90% of the screen and the content scrolls while the header stays. The subtitle sits beside the title.
Stacked actions
stacking="vertical" for long labels, primary on top. The buttons stay in reading order: secondary, then primary.
API reference
Drawer
| Prop | Type | Default | Description |
|---|---|---|---|
open / defaultOpen / onOpenChange | boolean | Controls whether it's open. | |
disablePointerDismissal | boolean | false | A tap on the overlay doesn't close it (sheets that hold input). |
DrawerContent
| Prop | Type | Default | Description |
|---|---|---|---|
handle | boolean | true | The drag handle, a hint only. |
initialFocus | RefObject<HTMLElement> | Where focus starts. |
DrawerHeader
| Prop | Type | Default | Description |
|---|---|---|---|
title | ReactNode | Figma's Title: what the sheet is for; it names the sheet. | |
subtitle | ReactNode | Figma's Subtitle, beside the title. | |
description | ReactNode | Figma's Description: one short line of context. | |
badge | ReactNode | Figma's Show badge: a count or short text. | |
closeButton / closeLabel | boolean / string | true / "Close" | The close button and its name. Keep it. |
DrawerMenuItem
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | The row's text. | |
leadingIcon / description / trailing | IconComponent / ReactNode / ReactNode | An icon, a second line and a count at the end. | |
onSelect / disabled | () => void / boolean | Runs on tap; then the sheet closes. |
DrawerActions / DrawerBody / DrawerMenuLabel / DrawerClose
| Prop | Type | Default | Description |
|---|---|---|---|
stacking | "horizontal" | "vertical" | "horizontal" | Figma's Stacking for the buttons. |
DrawerBody | div | The content; it scrolls when needed. | |
DrawerMenuLabel | h3 | Names a group of rows. | |
DrawerClose render | ReactElement | A button that closes the sheet. |
Accessibility
Keyboard
Keys are written for left-to-right. In right-to-left the arrow keys swap: the one pointing toward the reading direction moves forward.
| Key | Action |
|---|---|
| TabShift+Tab | Between the sheet's controls only. |
| Esc | Closes it and returns focus to the button that opened it. |
ARIA
- The sheet is a
dialogwitharia-modal, named by its title. - Swiping down is never the only way to close: the close button (Close), Esc and the overlay.
- The drag handle is decorative; rows and buttons are at least 48px.
Arabic and RTL notes
Mirrors
- The title and menu icons sit on the right; the close button on the left.
- The primary button moves to the left, at the inline end; the secondary to the right.
Does not mirror
- The motion is vertical: it slides up and swipes down in both languages.
- Numerals
- Counts in rows follow the numerals setting; format them with
formatNumber. - Arabic typography
- Title
label-14Medium, subtitle and descriptionlabel-12, rows as in the Menu. - Mixed-direction text
- Names in another language inside rows get
dir="auto".