Sheet

A sheet component that slides in from the side of the screen, used for displaying additional content or actions without taking up the full screen.

import Image from "next/image";
 
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import {
  Sheet,
  SheetClose,
  SheetContent,
  SheetDescription,
  SheetFooter,
  SheetHeader,
  SheetTitle,
  SheetTrigger,
} from "@/components/ui/sheet";
 
export function SheetDemo() {
  return (
    <Sheet>
      <SheetTrigger render={<Button variant="outline">Open</Button>} />
      <SheetContent>
        <SheetHeader>
          <SheetTitle>Edit profile</SheetTitle>
          <SheetDescription>
            Update the details below and click save to apply changes.
          </SheetDescription>
        </SheetHeader>
        <div className="flex flex-col gap-4 px-4">
          <div className="relative aspect-video w-full overflow-hidden">
            <Image
              src="/images/dialog-demo-image.jpg"
              alt="dialog-placeholder"
              priority
              className="rounded-md"
              objectFit="cover"
              fill
            />
          </div>
          <div className="flex flex-col gap-4">
            <div className="flex flex-col gap-3">
              <Label htmlFor="title">Title</Label>
              <Input id="title" value="Sample Title" className="col-span-3" />
            </div>
            <div className="flex flex-col gap-3">
              <Label htmlFor="description">Description</Label>
              <Input
                id="description"
                value="Sample Description"
                className="col-span-3"
              />
            </div>
          </div>
        </div>
        <SheetFooter>
          <Button type="submit">Save changes</Button>
          <SheetClose render={<Button variant="outline">Close</Button>} />
        </SheetFooter>
      </SheetContent>
    </Sheet>
  );
}

Installation

npx shadcn@latest add "https://eo-n.vercel.app/r/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

Side

import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import {
  Sheet,
  SheetClose,
  SheetContent,
  SheetDescription,
  SheetFooter,
  SheetHeader,
  SheetTitle,
  SheetTrigger,
} from "@/components/ui/sheet";
 
export function SheetSide() {
  return (
    <Sheet>
      <SheetTrigger render={<Button variant="outline">Open</Button>} />
      <SheetContent side="bottom" hideCloseIcon>
        <SheetHeader>
          <SheetTitle>Edit profile</SheetTitle>
          <SheetDescription>
            Update the details below and click save to apply changes.
          </SheetDescription>
        </SheetHeader>
        <div className="flex flex-col gap-4 px-4">
          <div className="flex flex-col gap-3">
            <Label htmlFor="title">Title</Label>
            <Input id="title" value="Sample Title" className="col-span-3" />
          </div>
          <div className="flex flex-col gap-3">
            <Label htmlFor="description">Description</Label>
            <Input
              id="description"
              value="Sample Description"
              className="col-span-3"
            />
          </div>
        </div>
        <SheetFooter className="group-data-[side=bottom]:flex-row-reverse group-data-[side=top]:flex-row-reverse">
          <Button type="submit">Save changes</Button>
          <SheetClose render={<Button variant="outline">Close</Button>} />
        </SheetFooter>
      </SheetContent>
    </Sheet>
  );
}

Open On Menu

In order to open a sheet using a menu, control the sheet state and open it imperatively using the onClick handler on the menu item.

Return Focus Properly

Make sure to also use the sheet’s finalFocus prop to return focus back to the menu trigger.

import * as React from "react";

import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuPositioner,
  DropdownMenuTrigger,
} from "../ui/dropdown-menu";
import {
  Sheet,
  SheetContent,
  SheetDescription,
  SheetHeader,
  SheetTitle,
  SheetTrigger,
} from "../ui/sheet";

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

  return (
    <React.Fragment>
      <DropdownMenu>
        {/* Set the trigger ref */}
        <DropdownMenuTrigger ref={menuTriggerRef}>Open</DropdownMenuTrigger>
        <DropdownMenuPositioner>
          <DropdownMenuContent>
            {/* Open the sheet when the menu item is clicked */}
            <DropdownMenuItem onClick={() => setSheetOpen(true)}>
              Open sheet
            </DropdownMenuItem>
          </DropdownMenuContent>
        </DropdownMenuPositioner>
      </DropdownMenu>
      {/* Control the sheet state */}
      <Sheet open={sheetOpen} onOpenChange={setSheetOpen}>
        {/* Return focus to the menu trigger when the sheet is closed */}
        <SheetContent finalFocus={menuTriggerRef}>
          <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>
    </React.Fragment>
  );
}

API Reference

Content

Contains content to be rendered in the open sheet.

PropTypeDefault
hideCloseIcon?
boolean
false
side
"top" | "right" | "bottom" | "left"
right