Skip to main content
A sheet is a panel that slides in from the edge of the screen. It’s useful for displaying navigation menus, filters, settings, or any content that should be accessible but not always visible.

Installation

npx shadcn@latest add @eo-n/sheet

Usage

Import all parts and piece them together:
import {
  Sheet,
  SheetContent,
  SheetDescription,
  SheetHeader,
  SheetTitle,
  SheetTrigger,
} from "@/components/ui/sheet";
<Sheet>
  <SheetTrigger>Open</SheetTrigger>
  <SheetContent>
    <SheetHeader>
      <SheetTitle>Are you sure you want to proceed?</SheetTitle>
      <SheetDescription>
        This action may have permanent effects. Please confirm if you want to
        continue.
      </SheetDescription>
    </SheetHeader>
  </SheetContent>
</Sheet>

Examples

Basic Sheet

import {
  Sheet,
  SheetContent,
  SheetDescription,
  SheetHeader,
  SheetTitle,
  SheetTrigger,
} from "@/components/ui/sheet";
import { Button } from "@/components/ui/button";

export default function SheetDemo() {
  return (
    <Sheet>
      <SheetTrigger asChild>
        <Button>Open Sheet</Button>
      </SheetTrigger>
      <SheetContent>
        <SheetHeader>
          <SheetTitle>Sheet Title</SheetTitle>
          <SheetDescription>
            This is a sheet that slides in from the right side of the screen.
          </SheetDescription>
        </SheetHeader>
      </SheetContent>
    </Sheet>
  );
}

Different Sides

Control which side the sheet slides in from using the side prop:
import {
  Sheet,
  SheetContent,
  SheetDescription,
  SheetHeader,
  SheetTitle,
  SheetTrigger,
} from "@/components/ui/sheet";
import { Button } from "@/components/ui/button";

export default function SheetSides() {
  return (
    <div className="flex gap-4">
      <Sheet>
        <SheetTrigger asChild>
          <Button>Left</Button>
        </SheetTrigger>
        <SheetContent side="left">
          <SheetHeader>
            <SheetTitle>Left Sheet</SheetTitle>
            <SheetDescription>
              This sheet slides in from the left.
            </SheetDescription>
          </SheetHeader>
        </SheetContent>
      </Sheet>

      <Sheet>
        <SheetTrigger asChild>
          <Button>Right</Button>
        </SheetTrigger>
        <SheetContent side="right">
          <SheetHeader>
            <SheetTitle>Right Sheet</SheetTitle>
            <SheetDescription>
              This sheet slides in from the right.
            </SheetDescription>
          </SheetHeader>
        </SheetContent>
      </Sheet>

      <Sheet>
        <SheetTrigger asChild>
          <Button>Top</Button>
        </SheetTrigger>
        <SheetContent side="top">
          <SheetHeader>
            <SheetTitle>Top Sheet</SheetTitle>
            <SheetDescription>
              This sheet slides in from the top.
            </SheetDescription>
          </SheetHeader>
        </SheetContent>
      </Sheet>

      <Sheet>
        <SheetTrigger asChild>
          <Button>Bottom</Button>
        </SheetTrigger>
        <SheetContent side="bottom">
          <SheetHeader>
            <SheetTitle>Bottom Sheet</SheetTitle>
            <SheetDescription>
              This sheet slides in from the bottom.
            </SheetDescription>
          </SheetHeader>
        </SheetContent>
      </Sheet>
    </div>
  );
}
Add action buttons in a footer:
import {
  Sheet,
  SheetClose,
  SheetContent,
  SheetDescription,
  SheetFooter,
  SheetHeader,
  SheetTitle,
  SheetTrigger,
} from "@/components/ui/sheet";
import { Button } from "@/components/ui/button";

export default function SheetWithFooter() {
  return (
    <Sheet>
      <SheetTrigger asChild>
        <Button>Edit Profile</Button>
      </SheetTrigger>
      <SheetContent>
        <SheetHeader>
          <SheetTitle>Edit Profile</SheetTitle>
          <SheetDescription>
            Make changes to your profile here. Click save when you're done.
          </SheetDescription>
        </SheetHeader>
        <div className="flex-1">
          {/* Form content */}
        </div>
        <SheetFooter>
          <SheetClose asChild>
            <Button variant="outline">Cancel</Button>
          </SheetClose>
          <Button>Save Changes</Button>
        </SheetFooter>
      </SheetContent>
    </Sheet>
  );
}

Open From Menu

Control the sheet state to open it from a dropdown menu:
import * as React from "react";
import {
  Sheet,
  SheetContent,
  SheetDescription,
  SheetHeader,
  SheetTitle,
} from "@/components/ui/sheet";
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { Button } from "@/components/ui/button";

export default function SheetFromMenu() {
  const menuTriggerRef = React.useRef<HTMLButtonElement>(null);
  const [sheetOpen, setSheetOpen] = React.useState(false);

  return (
    <>
      <DropdownMenu>
        <DropdownMenuTrigger ref={menuTriggerRef} asChild>
          <Button>Open Menu</Button>
        </DropdownMenuTrigger>
        <DropdownMenuContent>
          <DropdownMenuItem onClick={() => setSheetOpen(true)}>
            Open Sheet
          </DropdownMenuItem>
        </DropdownMenuContent>
      </DropdownMenu>
      
      <Sheet open={sheetOpen} onOpenChange={setSheetOpen}>
        <SheetContent finalFocus={menuTriggerRef}>
          <SheetHeader>
            <SheetTitle>Sheet from Menu</SheetTitle>
            <SheetDescription>
              This sheet was opened from a menu item. Focus will return to
              the menu trigger when closed.
            </SheetDescription>
          </SheetHeader>
        </SheetContent>
      </Sheet>
    </>
  );
}
Make sure to use the sheet’s finalFocus prop to return focus back to the menu trigger for proper accessibility.
Use a sheet for mobile navigation:
import {
  Sheet,
  SheetContent,
  SheetHeader,
  SheetTitle,
  SheetTrigger,
} from "@/components/ui/sheet";
import { Button } from "@/components/ui/button";
import { Menu } from "lucide-react";

export default function NavigationSheet() {
  return (
    <Sheet>
      <SheetTrigger asChild>
        <Button variant="ghost" size="icon">
          <Menu className="h-5 w-5" />
          <span className="sr-only">Toggle menu</span>
        </Button>
      </SheetTrigger>
      <SheetContent side="left">
        <SheetHeader>
          <SheetTitle>Navigation</SheetTitle>
        </SheetHeader>
        <nav className="flex flex-col gap-4 mt-6">
          <a href="#" className="text-lg font-medium hover:underline">
            Home
          </a>
          <a href="#" className="text-lg font-medium hover:underline">
            About
          </a>
          <a href="#" className="text-lg font-medium hover:underline">
            Services
          </a>
          <a href="#" className="text-lg font-medium hover:underline">
            Contact
          </a>
        </nav>
      </SheetContent>
    </Sheet>
  );
}

API Reference

Sheet

The root component that manages the sheet state.
open
boolean
Controls the open state of the sheet.
defaultOpen
boolean
The initial open state for uncontrolled usage.
onOpenChange
(open: boolean) => void
Callback fired when the open state changes.

SheetTrigger

The button that opens the sheet.
asChild
boolean
default:"false"
Merge props onto the child element instead of wrapping it.

SheetContent

Contains the content to be rendered in the open sheet.
side
'top' | 'right' | 'bottom' | 'left'
default:"'right'"
The side of the screen from which the sheet will slide in.
hideCloseIcon
boolean
default:"false"
Hides the X close icon button.
flush
boolean
default:"false"
Removes padding and gap from the content container. Useful when using SheetHeader and SheetFooter with borders.
finalFocus
React.RefObject<HTMLElement>
Element to receive focus when the sheet closes.
className
string
Additional CSS classes to apply.

SheetHeader

Wrapper for the sheet title and description.
className
string
Additional CSS classes to apply.

SheetFooter

Wrapper for sheet action buttons.
className
string
Additional CSS classes to apply.

SheetTitle

The accessible title of the sheet.
className
string
Additional CSS classes to apply.

SheetDescription

The accessible description of the sheet.
className
string
Additional CSS classes to apply.

SheetClose

A button that closes the sheet.
asChild
boolean
default:"false"
Merge props onto the child element instead of wrapping it.

Build docs developers (and LLMs) love