Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Skip to content

Sheet (registry composite)

A shadcn-aligned wrapper around the Sheet primitive — the same edge-anchored, Radix-Dialog-backed slide-over, re-exposed as a flat SheetContent / SheetHeader / SheetFooter / SheetTitle / SheetDescription API with data-slot attributes. Reach for this when you want to own the source and match the shadcn component shape; reach for the package primitive when you want the compound Sheet.Content statics straight from npm.

This is the registry composite. The bundled @roger-emerson/ploxum-primitives Sheet primitive is documented separately at Sheet.

Import

Sheet is a registry composite — you own the source after installing:

npx ploxum add sheet
import {
  Sheet,
  SheetTrigger,
  SheetClose,
  SheetContent,
  SheetHeader,
  SheetFooter,
  SheetTitle,
  SheetDescription,
} from "@/components/sheet";

Depends on @roger-emerson/ploxum-primitives (re-exposes its Sheet primitive — Radix Dialog over ploxum tokens + motion).

Usage

Trigger-based (uncontrolled) — wrap your own Button with SheetTrigger asChild. Slots are flat (no compound statics):

<Sheet>
  <SheetTrigger asChild>
    <Button variant="secondary">Edit profile</Button>
  </SheetTrigger>
  <SheetContent side="right" size={480}>
    <SheetHeader>
      <SheetTitle>Edit profile</SheetTitle>
      <SheetDescription>Make changes and save when you are done.</SheetDescription>
    </SheetHeader>
    <div className="flex-1 space-y-4 px-4 py-3">{/* form fields */}</div>
    <SheetFooter>
      <SheetClose asChild>
        <Button variant="ghost">Cancel</Button>
      </SheetClose>
      <SheetClose asChild>
        <Button variant="primary">Save changes</Button>
      </SheetClose>
    </SheetFooter>
  </SheetContent>
</Sheet>

side re-anchors the panel to any edge; size sets the width (left/right) or height (top/bottom). A left-anchored nav drawer or a bottom-anchored console are the same component:

<SheetContent side="left" size={300}>{/* navigation */}</SheetContent>
<SheetContent side="bottom" size="40vh">{/* log console */}</SheetContent>

Controlled — drive open / onOpenChange on the root yourself (e.g. opening a detail panel from a row click):

const [open, setOpen] = useState(false);
 
<Sheet onOpenChange={setOpen} open={open}>
  <SheetContent side="right" size={480}>
    <SheetHeader>
      <SheetTitle>Incident INC-2041</SheetTitle>
      <SheetDescription>Gate 4 — acknowledged 12:04:21</SheetDescription>
    </SheetHeader>
    {/* detail body */}
  </SheetContent>
</Sheet>;

Props

Sheet (root) / SheetTrigger / SheetClose

Re-exports of the primitive's Sheet, Sheet.Trigger, and Sheet.Close — Radix Dialog / Dialog.Trigger / Dialog.Close passthroughs. The root takes open / defaultOpen / onOpenChange; use asChild on the trigger and close to wrap your own Button. Any number of SheetClose elements may sit inside the content (e.g. both Cancel and Save in the footer).

SheetContent

PropTypeDefaultDescription
side"right" | "left" | "top" | "bottom""right"Edge the panel docks to.
sizenumber | stringprimitive defaultWidth for left/right, height for top/bottom. Numbers are px; strings (e.g. "40vh") pass through.
classNamestringMerged onto the panel surface.
childrenReactNodeHeader / body / footer slots.

SheetHeader / SheetFooter

Thin wrappers over the primitive's Header / Footer (forward all underlying props, set data-slot="sheet-header" / "sheet-footer"). Header is the bordered title block at the top; Footer is the bordered, right-aligned action bar pinned to the bottom.

SheetTitle / SheetDescription

Wrap the primitive's Title / Description (Radix Dialog.Title / Dialog.Description) — they register the dialog's accessible name and description, so include them (or an equivalent aria-label). Carry data-slot attrs and forward all underlying props plus className.

Design rules

  • Same motion + scrim contract as the primitive. The panel springs in from the docked edge and degrades to an opacity-only fade under prefers-reduced-motion; a flat --ploxum-color-background-scrim overlay sits behind it. Never blur the content behind the scrim — glassmorphism on content surfaces is banned. Don't add secondary entrance flourishes.
  • Focus is trapped and returns on close. Radix moves focus into the panel on open and restores it to the trigger on close; Esc and overlay click both dismiss.
  • data-slot everywhere. Each part stamps a data-slot (sheet-content / -header / -footer / -title / -description) so consumers can target the slots from their own CSS without prop drilling.
  • Sheet is for edge-anchored panels; Dialog is for centered modals. Reach for Sheet when the content is a side rail, drawer, or console (detail drawers, navigation, log consoles). Reach for Dialog when the interaction is a centered, focused decision.
  • Composite vs primitive is an ownership choice, not a behavior one. Both render the same surface. Use this composite (flat API, shadcn-aligned, source you own) when you're matching the shadcn component shape or porting shadcn blocks; use the package Sheet primitive (compound Sheet.Content statics, shipped from npm) otherwise.

Storybook

Composites/Sheet ↗