Components
Sheet
A modal panel anchored to a side of the viewport.
Installation
Choose the package when you want centralized updates, or copy the source through the registry when you want local ownership.
Install the package, import its global tokens once, then use this exact subpath.
pnpm add @constructive-io/ui@import '@constructive-io/ui/globals.css';import { Sheet } from '@constructive-io/ui/sheet';When to use
- Use Sheet for settings, forms, and detail views that enter from an edge while leaving part of the current page visible.
- Use Dialog for a centered interruption. Use Drawer when touch drag gestures and snap points are central to the task.
Basic usage
Compose SheetTrigger and SheetContent inside Sheet, then provide a title, description, body, and reachable close control. SheetContent supplies the portal, modal backdrop, motion, and icon close button.
'use client';
import { Button } from '@constructive-io/ui/button';
import { Input } from '@constructive-io/ui/input';
import { Label } from '@constructive-io/ui/label';
import { Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle, SheetTrigger } from '@constructive-io/ui/sheet';
export function BasicSheetDemo() {
return (<Sheet>
<SheetTrigger render={<Button variant="outline"/>}>Edit organization</SheetTrigger>
<SheetContent side="right">
<SheetHeader>
<SheetTitle>Organization settings</SheetTitle>
<SheetDescription>Update the workspace details. Changes apply on save.</SheetDescription>
</SheetHeader>
<div className="grid gap-4 py-2">
<div className="grid gap-2">
<Label htmlFor="sheet-organization-name">Name</Label>
<Input id="sheet-organization-name" defaultValue="Acme Corp"/>
</div>
<div className="grid gap-2">
<Label htmlFor="sheet-organization-slug">Slug</Label>
<Input id="sheet-organization-slug" defaultValue="acme"/>
</div>
</div>
<SheetFooter>
<SheetClose render={<Button />}>Save changes</SheetClose>
<SheetClose render={<Button variant="outline"/>}>Cancel</SheetClose>
</SheetFooter>
</SheetContent>
</Sheet>);
}
Controlled and uncontrolled visibility
Use defaultOpen when Sheet can own its initial visibility. Pass open and onOpenChange when application state is authoritative; the callback receives the next boolean value.
Examples
Accessibility
- Give every SheetContent an accessible name with SheetTitle. Add SheetDescription when supporting context is useful, and give each form control a visible Label or another accessible name.
- Keep a SheetClose inside the panel when showClose is false; the built-in icon close button already has an accessible name.
- Base UI moves focus into the panel, traps focus for the top-level modal sheet, closes on Escape or outside press, and returns focus to the trigger.
- Nested sheets keep the parent visible but make the newest sheet the active layer. Keep essential parent actions available again after the nested sheet closes.
API Reference
Constructive-specific behavior is listed here. Each part links to its inherited platform or primitive contract.
SheetStackProviderCoordinates registration, dimensions, order, and push offsets for nested sheets.
| Prop | Type | Default | Description |
|---|---|---|---|
| stackMode | 'cascade' | 'collapse' | 'cascade' | Indents every visible layer or moves lower sheets aside using the top sheet’s measured size. |
useSheetStackReturns the nearest stack context, or undefined outside SheetStackProvider. The context exposes sheets, measured sizes, stackMode, registration and size methods, isTopSheet, and getSheetsAbove.
useSheetReturns isOpen, sheetId, depth, sheetsAbove, isTopSheet, and close for the current Sheet; it throws when used outside Sheet.
SheetBase UI Dialog root adapter that manages one sheet and registers it with an optional stack.
| Prop | Type | Default | Description |
|---|---|---|---|
| open | boolean | — | Controlled visibility. |
| defaultOpen | boolean | false | Initial visibility in uncontrolled usage. |
| onOpenChange | (open: boolean) => void | — | Runs when visibility changes. |
| sheetId | string | — | Stable identifier used by SheetStackProvider; generated automatically when omitted. |
| modal | boolean | 'trap-focus' | true | Controls top-level modal behavior; nested sheets override it to false so interacting with a nested layer does not dismiss its parent. |
| disablePointerDismissal | boolean | false | Prevents outside presses from closing a top-level sheet. |
Also accepts Base UI Dialog props.
SheetPortalMoves sheet parts into the configured portal container and establishes the elevated floating layer.
| Prop | Type | Default | Description |
|---|---|---|---|
| container | HTMLElement | null | — | Explicit portal destination. |
| forceMountdeprecated | boolean | — | Compatibility prop retained as a no-op; Base UI manages mounting. |
Also accepts Base UI Dialog props.
SheetOverlayStyled Base UI backdrop for custom sheet compositions; SheetContent renders its own animated backdrop.
| Prop | Type | Default | Description |
|---|---|---|---|
| asChilddeprecated | boolean | — | Compatibility prop retained as a no-op; Base UI uses render for composition. |
| forceMountdeprecated | boolean | — | Compatibility prop retained as a no-op; Base UI manages mounting. |
Also accepts Base UI Dialog props.
SheetTriggerButton that opens the sheet and provides the default focus-return target.
| Prop | Type | Default | Description |
|---|---|---|---|
| render | ReactElement | function | — | Composes trigger behavior onto another element. |
| asChild | boolean | — | Compatibility composition prop; prefer render. |
| nativeButton | boolean | true | Set false when render or asChild produces a non-button element. |
Also accepts Base UI Dialog props.
SheetCloseButton that closes the current sheet.
| Prop | Type | Default | Description |
|---|---|---|---|
| render | ReactElement | function | — | Composes close behavior onto another element. |
| asChild | boolean | — | Compatibility composition prop; prefer render. |
| nativeButton | boolean | true | Set false when render or asChild produces a non-button element. |
Also accepts Base UI Dialog props.
SheetContentAnimated panel composition with an optional backdrop, built-in close button, and scoped floating portal.
| Prop | Type | Default | Description |
|---|---|---|---|
| side | 'top' | 'right' | 'bottom' | 'left' | 'right' | Selects the panel edge and motion direction. |
| transition | Motion Transition | — | Overrides the default panel transition. |
| overlay | boolean | true | Shows the backdrop for the top-level sheet without changing the root’s modal behavior. |
| showClose | boolean | true | Shows the accessible icon close button. |
| initialFocus | boolean | RefObject<HTMLElement> | function | — | Selects the element focused when the sheet opens. |
| finalFocus | boolean | RefObject<HTMLElement> | function | — | Selects the element focused after the sheet closes. |
| forceMountdeprecated | boolean | — | Compatibility prop retained as a no-op; Base UI and AnimatePresence manage mounting. |
| onInteractOutsidedeprecated | (event: Event) => void | — | Compatibility prop retained as a no-op; observe dismissal with Sheet onOpenChange. |
| onPointerDownOutsidedeprecated | (event: Event) => void | — | Compatibility prop retained as a no-op; observe dismissal with Sheet onOpenChange. |
| onEscapeKeyDowndeprecated | (event: KeyboardEvent) => void | — | Compatibility prop retained as a no-op; observe dismissal with Sheet onOpenChange. |
| onFocusOutsidedeprecated | (event: Event) => void | — | Compatibility prop retained as a no-op; observe dismissal with Sheet onOpenChange. |
Also accepts Base UI Dialog props.
SheetHeaderLayout wrapper for SheetTitle and SheetDescription.
SheetFooterResponsive action layout at the end of the sheet.
SheetTitleAccessible heading that labels the sheet.
Also accepts Base UI Dialog props.
SheetDescriptionAccessible supporting description for the sheet.
Also accepts Base UI Dialog props.
SHEET_INDENTThe 24-pixel offset applied per sheet above a panel in cascade mode.