Components
Sheet
A panel that slides in from any edge of the screen to hold supplementary content, forms, or navigation without leaving the current page.
Basic usage
Compose a SheetTrigger and SheetContent. The Radix root manages open state for you — no useState required. Only the header and footer are padded, so free-standing content needs its own px-4.
Sides
Set side on SheetContent to choose the edge the panel enters from. Left and right panels span the viewport height at w-3/4, capped at sm:max-w-sm; top and bottom span its width and size to their content.
Header and footer
SheetFooter pins itself to the bottom of the panel — ideal for a primary action paired with a SheetClose dismiss button.
Scrollable content
SheetContent is a flex column, so a middle region with flex-1 overflow-y-auto takes the scroll and leaves the header and the mt-auto footer in place.
Sheet or Drawer?
Sheet is a Radix Dialog: it slides in on a fixed track and closes on Escape or a click on the overlay. Use it for filters, detail panels, and desktop-first side panels — the Tier 3 shell's MobileMenu is a side="left" sheet. When the panel should be dragged — a grab handle, momentum, swipe-to-dismiss on touch — use Drawer, which is built on vaul instead.
Sheet Props
| Prop | Type | Default | Description |
|---|---|---|---|
open | boolean | — | Controlled open state. Pair with onOpenChange. |
defaultOpen | boolean | false | Open state on first render, for uncontrolled usage. |
onOpenChange | (open: boolean) => void | — | Called whenever the sheet opens or closes. |
modal | boolean | true | When true, the rest of the page is inert and hidden from assistive technology while the panel is open. |
childrenrequired | React.ReactNode | — | Sheet trigger and content. |
SheetContent Props
| Prop | Type | Default | Description |
|---|---|---|---|
side | 'top' | 'right' | 'bottom' | 'left' | 'right' | Edge the panel slides in from. left/right span the full height at w-3/4 (capped at sm:max-w-sm); top/bottom span the full width at h-auto. |
closeLabel | string | 'Close' | Visually hidden label on the built-in close button. Follows ComitorLocaleProvider — “Đóng” under locale="vi" — and a value passed here wins, e.g. 'Close filters' when the panel should be named. |
className | string | — | Merged onto the panel with cn(); the usual place to widen a side panel past sm:max-w-sm. |
onCloseAutoFocus | (event: Event) => void | — | Runs as the sheet closes, before focus moves — ahead of both Radix and the package's own focus return, so it wins: call event.preventDefault() and focus what should come next. Keep one that sends focus somewhere else; one that only re-focused the opener can go, since SheetContent does that itself (see Accessibility). |
…Radix Content | DialogPrimitive.ContentProps | — | Everything else is forwarded to Radix Dialog.Content — onEscapeKeyDown, onInteractOutside, onOpenAutoFocus, forceMount. |
Parts
Every part that renders an element accepts className; on the styled parts it is merged onto the defaults with cn(). Sheet and SheetPortal render no element of their own, so they take no className — passing one is a type error; style SheetContent instead.
| Prop | Type | Default | Description |
|---|---|---|---|
Sheet | DialogPrimitive.Root | — | Stateful container. Holds the open state and the trigger/content pair. |
SheetTrigger | DialogPrimitive.Trigger | — | Element that toggles the sheet. Pass asChild to render your own Button. |
SheetPortal | DialogPrimitive.Portal | — | Portal target. SheetContent renders one already — reach for it only when building a custom panel shell. |
SheetOverlay | DialogPrimitive.Overlay | — | Dimmed backdrop (bg-ink/60, dark:bg-ink/80, backdrop-blur). Also rendered for you by SheetContent. |
SheetContent | DialogPrimitive.Content | — | The sliding panel: portal + overlay + a flex column with gap-4 and a built-in close button. |
SheetHeader | div | — | Top region for title and description. Carries its own p-4 — the panel itself is unpadded. |
SheetTitle | DialogPrimitive.Title | — | Accessible name for the panel. Required for screen-reader announcements. |
SheetDescription | DialogPrimitive.Description | — | Supporting text linked to the panel via aria-describedby. |
SheetFooter | div | — | Bottom region for actions: mt-auto pins it to the base of the panel, p-4 matches the header. |
SheetClose | DialogPrimitive.Close | — | Dismisses the sheet. Use asChild to wrap a custom Button. |
Accessibility
- Built on Radix Dialog: the panel is a modal
role="dialog"with focus trapped inside while open. - On close, focus returns to the
SheetTrigger— or, with no Trigger, to the element that had focus just before the panel opened. That second half is the package’s, since 3.3.0; it is how the shell’sMobileMenu, which the header’s menu button opens from state, hands focus back to that button. The rules are Dialog’s: aDropdownMenuitem resolves to the menu’s trigger button, an opener that is gone gets no guess, and your ownonCloseAutoFocusruns first and wins. - Always include a
SheetTitle— it provides thearia-labelledbyname announced to screen readers;SheetDescriptionwires uparia-describedby. - Pressing Esc or clicking the dimmed overlay closes the sheet. The built-in close button has no visible text — its accessible name is the visually hidden
closeLabel, “Close” by default and “Đóng” under a Vietnamese ComitorLocaleProvider. A screen reader can meet that button out of context, so name the panel when it matters — the shell'sMobileMenupasses its own “Close menu” for exactly that reason. - Content outside the open panel is inert and hidden from assistive technology, keeping keyboard navigation scoped to the sheet.