Easy Shadcn
Components

Sheet

Sheet flattens shadcn's Dialog-backed Sheet parts into a conventional application side panel with required title and content, an optional trigger, description and footer slots, native open-state details, and a scroll-safe body.

Installation

With the @easy-shadcn namespace configured:

pnpm dlx shadcn@latest add @easy-shadcn/sheet

Or install via the full URL (zero configuration):

pnpm dlx shadcn@latest add https://easy-shadcn.vercel.app/r/sheet.json

The underlying shadcn sheet primitive is installed automatically.

Basic use

The first use case needs a trigger, a visible title, and body content:

import { Sheet } from "@/components/easy/sheet"

<Sheet
  trigger={<Button variant="outline">Open settings</Button>}
  title="Account settings"
  content={<AccountForm />}
/>

trigger is optional when open is controlled or defaultOpen starts the panel open. The supplied trigger element becomes the primitive trigger itself; Compose does not add a wrapper button.

Props

PropTypeDefaultDescription
titleReactNoderequiredVisible title connected to the dialog's accessible name.
contentReactNoderequiredMain body inside the scroll-safe data-slot="sheet-body" wrapper.
triggerReactElement-Optional element that becomes the primitive trigger without an extra wrapper.
descriptionReactNode-Optional accessible description below the title.
footerReactNode-Optional caller-owned footer. Compose does not inject action or close behavior.
side"top" | "right" | "bottom" | "left""right"shadcn Sheet placement.
showCloseButtonbooleantrueShows the primitive close button with the built-in English accessible name Close.
openboolean-Controlled open state.
defaultOpenbooleanfalseInitial uncontrolled open state.
onOpenChange(open: boolean, details: Dialog.Root.ChangeEventDetails) => void-Native Base UI change callback, including dismissal reason and details.cancel().
onOpenChangeComplete(open: boolean) => void-Fires after the opening or closing transition completes.
disablePointerDismissalbooleanfalsePrevents outside-pointer dismissal while preserving Escape and explicit close behavior.
initialFocuspopup focus targetprimitive defaultChooses the element focused after opening, or disables automatic focus.
finalFocuspopup focus targetprimitive defaultChooses the element focused after closing.
classNameClassValue-Class override for the shadcn Sheet content popup.
headerClassNameClassValue-Class override for the header wrapper.
titleClassNameClassValue-Class override for the title.
descriptionClassNameClassValue-Class override for the description.
contentClassNameClassValue-Class override for the body wrapper.
footerClassNameClassValue-Class override for the footer wrapper.

Other safe popup props such as id, style, refs, native event handlers, and test data attributes are forwarded to data-slot="sheet-content".

Open state and dismissal

The shadcn/Base UI Dialog root owns controlled and uncontrolled resolution, focus trapping, page scroll locking, Escape, outside-pointer dismissal, focus restoration, portals, and transitions. Compose forwards the native change details instead of inventing its own event shape:

<Sheet
  open={open}
  onOpenChange={(nextOpen, details) => {
    if (!nextOpen && hasUnsavedChanges) {
      details.cancel()
      return
    }

    setOpen(nextOpen)
  }}
  title="Edit profile"
  content={<ProfileForm />}
/>

footer remains plain caller-owned content. A footer button does not close automatically; control open yourself or use the shadcn SheetClose primitive in a custom primitive composition.

Layout and ownership

The body wrapper uses min-h-0 flex-1 overflow-y-auto, which keeps long left/right panels scrollable while the header and footer remain available. Optional slot values follow React rendering rules: null, undefined, and booleans are absent; valid falsy nodes such as 0 and "" render.

Compose owns the dialog role and ARIA title/description relationships, child structure, raw HTML, root replacement, data-slot, data-side, and Base UI transition/state markers. These keys cannot override the flat structure through typed passthrough.

When to use the primitive

Use components/ui/sheet directly for non-modal or focus-trap-only panels, custom overlays or portals, externally handled or payload-bearing triggers, custom close icons or labels, force-mounted content, nested-sheet orchestration, arbitrary descendants, slots objects, render functions, or action workflows that need primitive-level composition.

On this page