Sheet
Extends the Dialog component to display content that complements the main content of the screen.
Installation
npx shadcn@latest add @redpanda/sheetpnpm dlx shadcn@latest add @redpanda/sheetyarn dlx shadcn@latest add @redpanda/sheetbunx shadcn@latest add @redpanda/sheetnubx shadcn@latest add @redpanda/sheetaube dlx shadcn@latest add @redpanda/sheetWhen to use
Sheets display supplementary content that slides from screen edges. Use this decision tree:
Usage
<Sheet> <SheetTrigger>Open Sheet</SheetTrigger> <SheetContent> <SheetHeader> <SheetTitle>Title</SheetTitle> <SheetDescription>Description</SheetDescription> </SheetHeader> <p>Sheet Content</p> <SheetFooter> <button>Close</button> </SheetFooter> </SheetContent></Sheet>Anatomy
The Sheet component extends Dialog for side-sliding content:
<Sheet> (Dialog-based)├── <SheetTrigger> (Button)├── <SheetContent> (Sliding Panel)│ ├── Side positioning (top/right/bottom/left)│ ├── Animation (slide + fade)│ ├── <SheetHeader>│ │ ├── <SheetTitle> (h2)│ │ └── <SheetDescription>│ ├── Content Area (scrollable)│ └── <SheetFooter>├── Sheet Overlay (backdrop)└── Focus managementProps
Base UI
Shadcn UI
SheetContent
side?'top' | 'right' | 'bottom' | 'left'
Viewport edge the panel docks to and slides in from.
'top' | 'right' | 'bottom' | 'left''right'size?'sm' | 'md' | 'lg' | 'xl' | '2xl' | 'full'
Caps how wide a left or right sheet grows. A top or bottom sheet spans the full viewport width and takes its height from its content, so size does not apply to it.
'sm' | 'md' | 'lg' | 'xl' | '2xl' | 'full''xl'showCloseButton?boolean
Render the top-right close affordance.
booleantruecloseButtonGutter?boolean
Pad the top so content starts below the close button. Opt-in because it moves content down. Ignored when showCloseButton is false.
booleanfalseThe close button sits over the content, so a top-right control of your own (a code block’s copy
button, a banner’s dismiss) collides with it. Set closeButtonGutter to start the content below it:
<SheetContent closeButtonGutter> <SimpleCodeBlock code={bootstrapServers} language="bash" /></SheetContent>Validated sheet form
The profile example keeps its actions visible while the field area scrolls. It uses the standard Sheet spacing and Field composition: invalid inputs are associated with every visible error. Website is optional; non-empty values must be complete HTTP or HTTPS URLs, not just a protocol prefix. Save closes the demo only after validation passes; it does not send a profile update to a server.
Keyboard interactions
| Key | Action |
|---|---|
Enter / Space on SheetTrigger |
Opens the sheet and focuses the first focusable element. |
Tab / Shift+Tab |
Cycles focus within the sheet — focus is trapped while open. |
Escape |
Closes the sheet and restores focus to the trigger. |
Click outside SheetContent |
Closes the sheet. |
Accessibility
- Always include a
SheetTitle(visually hidden is fine) — Base UI requires it for the accessible name. - Add a
SheetDescriptionwhen the purpose isn’t obvious from the title. - Focus is trapped inside the panel while open and returned to the trigger on close; background is
aria-hiddenand inert. - The sheet behaves exactly like
Dialogsemantically — it’s a side-docked Dialog with a slide transition.
Controlled vs uncontrolled
Uncontrolled — let the sheet own open state:
<Sheet onOpenChange={(open) => console.log('open:', open)}> <SheetTrigger>Open</SheetTrigger> <SheetContent side="right">{/* ... */}</SheetContent></Sheet>Controlled — drive open from parent state (e.g. to open the sheet from a table row action):
const [open, setOpen] = useState(false);
<Sheet open={open} onOpenChange={setOpen}> <SheetContent side="right"> <SheetHeader> <SheetTitle>Edit topic</SheetTitle> </SheetHeader> {/* ... */} </SheetContent></Sheet>;