Overlaysreadycomponentready
Sheet
A modal side panel primitive with trigger, portal, overlay, side placement, header, footer, close controls, and focus management.
Import
import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetTrigger } from 'radcn/sheet'Live package example
Render the upstream profile Sheet and four-side Sheet examples with RadCN modal primitives and app-owned form controls.
Demo and Side
Render the upstream profile Sheet and four-side Sheet examples with RadCN modal primitives and app-owned form controls.
import { Button } from 'radcn/button'
import { Input } from 'radcn/input'
import { Label } from 'radcn/label'
import {
Sheet,
SheetClose,
SheetContent,
SheetDescription,
SheetFooter,
SheetHeader,
SheetOverlay,
SheetPortal,
SheetTitle,
SheetTrigger,
} from 'radcn/sheet'
const sheetSides = ['top', 'right', 'bottom', 'left'] as const
export function SheetPreview() {
return (
<>
<Sheet id="profile-sheet">
<SheetTrigger class="radcn-button radcn-button--outline">Open</SheetTrigger>
<SheetPortal>
<SheetOverlay />
<SheetContent>
<SheetHeader>
<SheetTitle>Edit profile</SheetTitle>
<SheetDescription>Make changes to your profile here. Click save when you're done.</SheetDescription>
</SheetHeader>
<Label for="sheet-demo-name">Name</Label>
<Input id="sheet-demo-name" value="Pedro Duarte" />
<Label for="sheet-demo-username">Username</Label>
<Input id="sheet-demo-username" value="@peduarte" />
<SheetFooter>
<Button type="submit">Save changes</Button>
<SheetClose class="radcn-button radcn-button--outline">Close</SheetClose>
</SheetFooter>
</SheetContent>
</SheetPortal>
</Sheet>
<div>
{sheetSides.map((side) => (
<Sheet id={`profile-sheet-${side}`}>
<SheetTrigger class="radcn-button radcn-button--outline">{side}</SheetTrigger>
<SheetPortal>
<SheetOverlay />
<SheetContent side={side}>
<SheetHeader>
<SheetTitle>Edit profile</SheetTitle>
<SheetDescription>Make changes to your profile here. Click save when you're done.</SheetDescription>
</SheetHeader>
<Label for={`sheet-side-${side}-name`}>Name</Label>
<Input id={`sheet-side-${side}-name`} value="Pedro Duarte" />
<Label for={`sheet-side-${side}-username`}>Username</Label>
<Input id={`sheet-side-${side}-username`} value="@peduarte" />
<SheetFooter><SheetClose>Save changes</SheetClose></SheetFooter>
</SheetContent>
</SheetPortal>
</Sheet>
))}
</div>
</>
)
}Installation
Intended future install command. RadCN is private and not published to npm yet, so this snippet documents the target user-facing API rather than something external consumers can run today.
pnpm add radcn # intended future package
import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetTrigger } from 'radcn/sheet'Theming
RadCN tokens read the resolved theme from the document. Store the user's preference separately, then resolve system preferences to a concrete light or dark theme before setting package tokens.
<html data-radcn-theme-mode="system" data-radcn-theme="dark">
...
</html>Accessibility
- SheetContent is enhanced with role="dialog", aria-modal, aria-labelledby, and aria-describedby from visible title and description parts.
- Sheet focus is trapped while open, returns to the trigger when closed, and locks body scrolling during the modal session.
- Escape, overlay pointer dismissal, default close button, and explicit SheetClose controls close dismissible sheets.
- Profile inputs and labels remain native Input and Label composition inside the sheet surface.
Customization
- Sheet exposes root, trigger, portal, overlay, content, header, title, description, footer, and close hooks through data-radcn-sheet* attributes.
- Side placement maps to side="top", side="right", side="bottom", and side="left" on SheetContent.
- Button, Input, and Label composition stays app-owned while Sheet owns the modal side panel behavior.
- Grid layout, padding, text alignment, widths, and repeated side examples map to class, style, CSS variables, or app CSS rather than Tailwind utilities.
Remix 3 Notes
- React props, Radix Dialog primitives, className, data-slot, Tailwind utilities, cn, and vendor source map to explicit RadCN props, class, public data hooks, package CSS, inline style, and CSS variables.
- asChild maps to explicit SheetTrigger and SheetClose composition with RadCN Button styling instead of React Slot behavior.
- SHEET_SIDES, React keys, and repeated rendering map to deterministic server-rendered markup with valid unique ids.
- Form submit actions, profile persistence, and input state remain app-owned; Sheet owns the modal surface and close behavior.
- vendor source remains read-only evidence and is not imported by RadCN.
