Component

Sheet

A slide-out panel that extends from the edge of the screen.

Preview

Component sheet not found in registry.

Installation

pnpm dlx shadcn@latest add "https://ui-registry.com/r/sheet.json"

Usage

import {
  Sheet,
  SheetBody,
  SheetContent,
  SheetDescription,
  SheetFooter,
  SheetHeader,
  SheetTitle,
  SheetTrigger,
} from "@/components/ui/sheet"
<Sheet>
  <SheetTrigger>Open</SheetTrigger>
  <SheetContent>
    <SheetHeader>
      <SheetTitle>Edit profile</SheetTitle>
      <SheetDescription>
        Make changes to your profile here.
      </SheetDescription>
    </SheetHeader>
    <SheetBody>
      {/* Your content here */}
    </SheetBody>
    <SheetFooter>
      <Button>Save changes</Button>
    </SheetFooter>
  </SheetContent>
</Sheet>

Features

This is an enhanced version of the shadcn/ui Sheet component with additional features:

  • Body Section: Dedicated SheetBody component for scrollable content
  • Grid Layout: Uses CSS grid with 3 rows:
    • Header: max-content
    • Body: 1fr (scrollable)
    • Footer: max-content
  • Automatic Detection: Uses CSS :has() to detect presence of header/footer
  • Adaptive Rows: Grid rows adapt based on which parts are present
  • Fixed positioning: Proper fixed positioning on all browsers
  • Side variants: Supports top, right, bottom, and left positions
  • Accessible: Full keyboard navigation and screen reader support

Examples

Basic Example

<Sheet>
  <SheetTrigger asChild>
    <Button>Open Sheet</Button>
  </SheetTrigger>
  <SheetContent>
    <SheetHeader>
      <SheetTitle>Notifications</SheetTitle>
      <SheetDescription>
        You have 12 unread notifications.
      </SheetDescription>
    </SheetHeader>
    <SheetBody>
      {/* Scrollable content */}
      <div className="space-y-4">
        {notifications.map((notification) => (
          <NotificationItem key={notification.id} {...notification} />
        ))}
      </div>
    </SheetBody>
    <SheetFooter>
      <Button variant="outline" className="w-full">
        Mark all as read
      </Button>
    </SheetFooter>
  </SheetContent>
</Sheet>

Side Positions

// Right (default)
<Sheet>
  <SheetTrigger>Open Right</SheetTrigger>
  <SheetContent side="right">
    {/* Content */}
  </SheetContent>
</Sheet>
 
// Left
<Sheet>
  <SheetTrigger>Open Left</SheetTrigger>
  <SheetContent side="left">
    {/* Content */}
  </SheetContent>
</Sheet>
 
// Top
<Sheet>
  <SheetTrigger>Open Top</SheetTrigger>
  <SheetContent side="top">
    {/* Content */}
  </SheetContent>
</Sheet>
 
// Bottom
<Sheet>
  <SheetTrigger>Open Bottom</SheetTrigger>
  <SheetContent side="bottom">
    {/* Content */}
  </SheetContent>
</Sheet>

The grid layout automatically adapts when header or footer is not present.

<Sheet>
  <SheetTrigger asChild>
    <Button>Navigation</Button>
  </SheetTrigger>
  <SheetContent side="left">
    <SheetHeader>
      <SheetTitle>Navigation</SheetTitle>
      <SheetDescription>
        Browse our documentation.
      </SheetDescription>
    </SheetHeader>
    <SheetBody>
      <nav className="space-y-2">
        {navItems.map((item) => (
          <a key={item.href} href={item.href}>
            {item.label}
          </a>
        ))}
      </nav>
    </SheetBody>
    {/* No footer - grid adapts automatically */}
  </SheetContent>
</Sheet>

Bottom Sheet

<Sheet>
  <SheetTrigger asChild>
    <Button>Select Option</Button>
  </SheetTrigger>
  <SheetContent side="bottom">
    <SheetHeader>
      <SheetTitle>Select an option</SheetTitle>
      <SheetDescription>
        Choose from the available options below.
      </SheetDescription>
    </SheetHeader>
    <SheetBody>
      <div className="grid gap-2">
        {options.map((option) => (
          <button key={option.id} className="...">
            {option.label}
          </button>
        ))}
      </div>
    </SheetBody>
    <SheetFooter>
      <Button variant="outline" className="w-full">
        Cancel
      </Button>
    </SheetFooter>
  </SheetContent>
</Sheet>

API

Sheet

PropTypeDefaultDescription
openboolean-Controlled open state
onOpenChange(open: boolean) => void-Callback when open state changes

SheetContent

PropTypeDefaultDescription
side"top" | "right" | "bottom" | "left""right"The edge of the screen where the sheet appears
classNamestring-Additional CSS classes

SheetBody

The scrollable body section with overflow-y-auto and min-h-0.

PropTypeDefaultDescription
classNamestring-Additional CSS classes

Keyboard Interactions

KeyDescription
EscapeCloses the sheet
TabMoves focus to next focusable element
Shift + TabMoves focus to previous focusable element