Components / Overlays & disclosure
Drawer
A temporary side surface for focused work.
Install
npx shadcn@latest add https://irisui.dev/r/drawer.jsonimport { Drawer } from "@/components/iris/drawer/drawer";Preview
Drawer
A temporary panel that slides in from any edge for focused side work, such as editing a record without leaving the list behind it.
When to use
- Editing or inspecting one item while keeping the page context visible (settings for a row, a filter panel, details).
- Forms that are too long for a popover but do not need to block the whole task.
- Not for decisions that must interrupt: use
Dialog. On touch layouts where a peek height helps, useBottomSheet.
Usage
import { Drawer } from "@/components/iris/drawer/drawer";
h(
Drawer,
null,
h(DrawerTrigger, { asChild: true }, h(Button, { variant: "secondary" }, "Edit token")),
h(
DrawerContent,
{
title: "Edit project token",
description: "Agents use this token to edit the project over MCP and the CLI.",
side: "right",
},
h(Input, { label: "Name", defaultValue: "claude-code@agent" }),
h(DrawerClose, { asChild: true }, h(Button, { variant: "ghost" }, "Cancel")),
),
);
Props
Drawer (root, wraps Radix Dialog Root)
| Prop | Type | Default | Description |
|---|---|---|---|
open |
boolean |
— | Controlled open state. |
defaultOpen |
boolean |
false |
Initial state when uncontrolled. |
onOpenChange |
(open: boolean) => void |
— | Called on open, close, Escape, outside click, or a dismissing drag. |
modal |
boolean |
true |
Radix modal behaviour (focus trap, scroll lock). |
DrawerContent
| Prop | Type | Default | Description |
|---|---|---|---|
title |
string |
— | Required. Rendered as the dialog title; changes cross-fade while open. |
description |
string |
— | Optional supporting line under the title. |
side |
"left" | "right" | "top" | "bottom" |
"right" |
Edge the panel slides from. |
container |
HTMLElement | null |
— | Portal into this element instead of body. It needs position: relative and overflow: hidden. |
children |
ReactNode |
— | Body content. |
DrawerTrigger and DrawerClose are the Radix Dialog Trigger and Close; use asChild to render your own button.
useDrawerItem(item) (Iris.useDrawerItem) returns the last non-null item it was given. For a drawer driven by a selection, pass the selected item (or null when nothing is open) and render the content from the result, so the content stays on screen while the panel slides out:
const { useDrawerItem } = window.Iris;
const shown = useDrawerItem(selectedKey); // selectedKey is null once closed
h(
Drawer,
{
open: selectedKey != null,
onOpenChange: (open) => {
if (!open) setSelectedKey(null);
},
},
shown && h(DrawerContent, { title: shown.name }, h(TokenForm, { token: shown })),
);
Notes
- Built on Radix Dialog: focus is trapped and returned to the trigger, Escape closes, and the title and description are wired to
aria-labelledby/aria-describedby. The close button is labelled "Close drawer". - The header is a drag handle: dragging past a third of the panel or flicking toward the edge dismisses it; shorter drags spring back.
- Reduced motion swaps the slide for a short fade and disables dragging.
- Reads
--surface,--hover,--line,--line-strong,--ink,--ink-2,--shadow-floatingand--radius-button(the close button).