# Sheet
URL: /design/components/sheet

A panel that slides in from an edge for secondary flows.

> For AI agents: a documentation index is available at [llms.txt](/llms.txt). Use `.md` for canonical markdown pages; `.mdx` is kept as a backwards-compatible alias on supported URL paths.

\[interactive preview component SheetSpecimen omitted]

Code for SheetSpecimen preview:

```tsx
"use client";

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

function SheetSpecimen(): ReactNode {
  return (
    <Sheet>
      <SheetTrigger
        render={<Button variant="outline">Open thread list</Button>}
      />
      <SheetContent side="right">
        <SheetHeader>
          <SheetTitle>Threads</SheetTitle>
          <SheetDescription>
            Recent conversations in this workspace.
          </SheetDescription>
        </SheetHeader>
      </SheetContent>
    </Sheet>
  );
}
```

## Installation

Install the dependencies:

- packages

  - @base-ui/react

Copy the source into `components/ui/sheet.tsx`. Registry items that depend on it install it automatically.

- code

  "use client"; import \* as React from "react"; import { Dialog as SheetPrimitive } from "@base-ui/react/dialog"; import { XIcon } from "lucide-react"; import { Button } from "@/components/ui/button"; import { cn } from "@/lib/utils"; function Sheet({ ...props }: SheetPrimitive.Root.Props) { return \<SheetPrimitive.Root data-slot="sheet" {...props} />; } function SheetTrigger({ ...props }: SheetPrimitive.Trigger.Props) { return \<SheetPrimitive.Trigger data-slot="sheet-trigger" {...props} />; } function SheetClose({ ...props }: SheetPrimitive.Close.Props) { return \<SheetPrimitive.Close data-slot="sheet-close" {...props} />; } function SheetPortal({ ...props }: SheetPrimitive.Portal.Props) { return \<SheetPrimitive.Portal data-slot="sheet-portal" {...props} />; } function SheetOverlay({ className, ...props }: SheetPrimitive.Backdrop.Props) { return ( \<SheetPrimitive.Backdrop data-slot="sheet-overlay" className={cn( "fixed inset-0 z-50 bg-black/10 transition-opacity duration-150 data-ending-style:opacity-0 data-starting-style:opacity-0 supports-backdrop-filter:backdrop-blur-xs", className, )} {...props} /> ); } function SheetContent({ className, children, side = "right", showCloseButton = true, ...props }: SheetPrimitive.Popup.Props & { side?: "top" | "right" | "bottom" | "left"; showCloseButton?: boolean; }) { return ( \<SheetPortal> \<SheetOverlay /> \<SheetPrimitive.Popup data-slot="sheet-content" data-side={side} className={cn( "bg-popover text-popover-foreground fixed z-50 flex flex-col gap-4 bg-clip-padding text-sm transition duration-200 ease-in-out data-ending-style:opacity-0 data-starting-style:opacity-0 data-\[side=bottom]:inset-x-0 data-\[side=bottom]:bottom-0 data-\[side=bottom]:h-auto data-\[side=bottom]:border-t data-\[side=bottom]:data-ending-style:translate-y-\[2.5rem] data-\[side=bottom]:data-starting-style:translate-y-\[2.5rem] data-\[side=left]:inset-y-0 data-\[side=left]:start-0 data-\[side=left]:h-full data-\[side=left]:w-3/4 data-\[side=left]:border-e data-\[side=left]:data-ending-style:translate-x-\[-2.5rem] data-\[side=left]:data-starting-style:translate-x-\[-2.5rem] data-\[side=right]:inset-y-0 data-\[side=right]:end-0 data-\[side=right]:h-full data-\[side=right]:w-3/4 data-\[side=right]:border-s data-\[side=right]:data-ending-style:translate-x-\[2.5rem] data-\[side=right]:data-starting-style:translate-x-\[2.5rem] data-\[side=top]:inset-x-0 data-\[side=top]:top-0 data-\[side=top]:h-auto data-\[side=top]:border-b data-\[side=top]:data-ending-style:translate-y-\[-2.5rem] data-\[side=top]:data-starting-style:translate-y-\[-2.5rem] data-\[side=left]:sm:max-w-sm data-\[side=right]:sm:max-w-sm rtl:data-\[side=left]:data-ending-style:translate-x-\[2.5rem] rtl:data-\[side=left]:data-starting-style:translate-x-\[2.5rem] rtl:data-\[side=right]:data-ending-style:translate-x-\[-2.5rem] rtl:data-\[side=right]:data-starting-style:translate-x-\[-2.5rem]", className, )} {...props} > {children} {showCloseButton && ( \<SheetPrimitive.Close data-slot="sheet-close" render={ \<Button variant="ghost" className="absolute end-3 top-3" size="icon-sm" /> } > \<XIcon /> \<span className="sr-only">Close\</span> \</SheetPrimitive.Close> )} \</SheetPrimitive.Popup> \</SheetPortal> ); } function SheetHeader({ className, ...props }: React.ComponentProps<"div">) { return ( \<div data-slot="sheet-header" className={cn("flex flex-col gap-0.5 p-4", className)} {...props} /> ); } function SheetFooter({ className, ...props }: React.ComponentProps<"div">) { return ( \<div data-slot="sheet-footer" className={cn("mt-auto flex flex-col gap-2 p-4", className)} {...props} /> ); } function SheetTitle({ className, ...props }: SheetPrimitive.Title.Props) { return ( \<SheetPrimitive.Title data-slot="sheet-title" className={cn( "cn-font-heading text-foreground text-base font-medium", className, )} {...props} /> ); } function SheetDescription({ className, ...props }: SheetPrimitive.Description.Props) { return ( \<SheetPrimitive.Description data-slot="sheet-description" className={cn("text-muted-foreground text-sm", className)} {...props} /> ); } export { Sheet, SheetTrigger, SheetClose, SheetContent, SheetHeader, SheetFooter, SheetTitle, SheetDescription, };

* title

  components/ui/sheet.tsx

* copyText

  "use client"; import \* as React from "react"; import { Dialog as SheetPrimitive } from "@base-ui/react/dialog"; import { XIcon } from "lucide-react"; import { Button } from "@/components/ui/button"; import { cn } from "@/lib/utils"; function Sheet({ ...props }: SheetPrimitive.Root.Props) { return \<SheetPrimitive.Root data-slot="sheet" {...props} />; } function SheetTrigger({ ...props }: SheetPrimitive.Trigger.Props) { return \<SheetPrimitive.Trigger data-slot="sheet-trigger" {...props} />; } function SheetClose({ ...props }: SheetPrimitive.Close.Props) { return \<SheetPrimitive.Close data-slot="sheet-close" {...props} />; } function SheetPortal({ ...props }: SheetPrimitive.Portal.Props) { return \<SheetPrimitive.Portal data-slot="sheet-portal" {...props} />; } function SheetOverlay({ className, ...props }: SheetPrimitive.Backdrop.Props) { return ( \<SheetPrimitive.Backdrop data-slot="sheet-overlay" className={cn( "fixed inset-0 z-50 bg-black/10 transition-opacity duration-150 data-ending-style:opacity-0 data-starting-style:opacity-0 supports-backdrop-filter:backdrop-blur-xs", className, )} {...props} /> ); } function SheetContent({ className, children, side = "right", showCloseButton = true, ...props }: SheetPrimitive.Popup.Props & { side?: "top" | "right" | "bottom" | "left"; showCloseButton?: boolean; }) { return ( \<SheetPortal> \<SheetOverlay /> \<SheetPrimitive.Popup data-slot="sheet-content" data-side={side} className={cn( "bg-popover text-popover-foreground fixed z-50 flex flex-col gap-4 bg-clip-padding text-sm transition duration-200 ease-in-out data-ending-style:opacity-0 data-starting-style:opacity-0 data-\[side=bottom]:inset-x-0 data-\[side=bottom]:bottom-0 data-\[side=bottom]:h-auto data-\[side=bottom]:border-t data-\[side=bottom]:data-ending-style:translate-y-\[2.5rem] data-\[side=bottom]:data-starting-style:translate-y-\[2.5rem] data-\[side=left]:inset-y-0 data-\[side=left]:start-0 data-\[side=left]:h-full data-\[side=left]:w-3/4 data-\[side=left]:border-e data-\[side=left]:data-ending-style:translate-x-\[-2.5rem] data-\[side=left]:data-starting-style:translate-x-\[-2.5rem] data-\[side=right]:inset-y-0 data-\[side=right]:end-0 data-\[side=right]:h-full data-\[side=right]:w-3/4 data-\[side=right]:border-s data-\[side=right]:data-ending-style:translate-x-\[2.5rem] data-\[side=right]:data-starting-style:translate-x-\[2.5rem] data-\[side=top]:inset-x-0 data-\[side=top]:top-0 data-\[side=top]:h-auto data-\[side=top]:border-b data-\[side=top]:data-ending-style:translate-y-\[-2.5rem] data-\[side=top]:data-starting-style:translate-y-\[-2.5rem] data-\[side=left]:sm:max-w-sm data-\[side=right]:sm:max-w-sm rtl:data-\[side=left]:data-ending-style:translate-x-\[2.5rem] rtl:data-\[side=left]:data-starting-style:translate-x-\[2.5rem] rtl:data-\[side=right]:data-ending-style:translate-x-\[-2.5rem] rtl:data-\[side=right]:data-starting-style:translate-x-\[-2.5rem]", className, )} {...props} > {children} {showCloseButton && ( \<SheetPrimitive.Close data-slot="sheet-close" render={ \<Button variant="ghost" className="absolute end-3 top-3" size="icon-sm" /> } > \<XIcon /> \<span className="sr-only">Close\</span> \</SheetPrimitive.Close> )} \</SheetPrimitive.Popup> \</SheetPortal> ); } function SheetHeader({ className, ...props }: React.ComponentProps<"div">) { return ( \<div data-slot="sheet-header" className={cn("flex flex-col gap-0.5 p-4", className)} {...props} /> ); } function SheetFooter({ className, ...props }: React.ComponentProps<"div">) { return ( \<div data-slot="sheet-footer" className={cn("mt-auto flex flex-col gap-2 p-4", className)} {...props} /> ); } function SheetTitle({ className, ...props }: SheetPrimitive.Title.Props) { return ( \<SheetPrimitive.Title data-slot="sheet-title" className={cn( "cn-font-heading text-foreground text-base font-medium", className, )} {...props} /> ); } function SheetDescription({ className, ...props }: SheetPrimitive.Description.Props) { return ( \<SheetPrimitive.Description data-slot="sheet-description" className={cn("text-muted-foreground text-sm", className)} {...props} /> ); } export { Sheet, SheetTrigger, SheetClose, SheetContent, SheetHeader, SheetFooter, SheetTitle, SheetDescription, };

* viewportClassName

  max-h-\[450px]

- language

  tsx

- code

  "use client"; import \* as React from "react"; import { Dialog as SheetPrimitive } from "@base-ui/react/dialog"; import { XIcon } from "lucide-react"; import { Button } from "@/components/ui/button"; import { cn } from "@/lib/utils"; function Sheet({ ...props }: SheetPrimitive.Root.Props) { return \<SheetPrimitive.Root data-slot="sheet" {...props} />; } function SheetTrigger({ ...props }: SheetPrimitive.Trigger.Props) { return \<SheetPrimitive.Trigger data-slot="sheet-trigger" {...props} />; } function SheetClose({ ...props }: SheetPrimitive.Close.Props) { return \<SheetPrimitive.Close data-slot="sheet-close" {...props} />; } function SheetPortal({ ...props }: SheetPrimitive.Portal.Props) { return \<SheetPrimitive.Portal data-slot="sheet-portal" {...props} />; } function SheetOverlay({ className, ...props }: SheetPrimitive.Backdrop.Props) { return ( \<SheetPrimitive.Backdrop data-slot="sheet-overlay" className={cn( "fixed inset-0 z-50 bg-black/10 transition-opacity duration-150 data-ending-style:opacity-0 data-starting-style:opacity-0 supports-backdrop-filter:backdrop-blur-xs", className, )} {...props} /> ); } function SheetContent({ className, children, side = "right", showCloseButton = true, ...props }: SheetPrimitive.Popup.Props & { side?: "top" | "right" | "bottom" | "left"; showCloseButton?: boolean; }) { return ( \<SheetPortal> \<SheetOverlay /> \<SheetPrimitive.Popup data-slot="sheet-content" data-side={side} className={cn( "bg-popover text-popover-foreground fixed z-50 flex flex-col gap-4 bg-clip-padding text-sm transition duration-200 ease-in-out data-ending-style:opacity-0 data-starting-style:opacity-0 data-\[side=bottom]:inset-x-0 data-\[side=bottom]:bottom-0 data-\[side=bottom]:h-auto data-\[side=bottom]:border-t data-\[side=bottom]:data-ending-style:translate-y-\[2.5rem] data-\[side=bottom]:data-starting-style:translate-y-\[2.5rem] data-\[side=left]:inset-y-0 data-\[side=left]:start-0 data-\[side=left]:h-full data-\[side=left]:w-3/4 data-\[side=left]:border-e data-\[side=left]:data-ending-style:translate-x-\[-2.5rem] data-\[side=left]:data-starting-style:translate-x-\[-2.5rem] data-\[side=right]:inset-y-0 data-\[side=right]:end-0 data-\[side=right]:h-full data-\[side=right]:w-3/4 data-\[side=right]:border-s data-\[side=right]:data-ending-style:translate-x-\[2.5rem] data-\[side=right]:data-starting-style:translate-x-\[2.5rem] data-\[side=top]:inset-x-0 data-\[side=top]:top-0 data-\[side=top]:h-auto data-\[side=top]:border-b data-\[side=top]:data-ending-style:translate-y-\[-2.5rem] data-\[side=top]:data-starting-style:translate-y-\[-2.5rem] data-\[side=left]:sm:max-w-sm data-\[side=right]:sm:max-w-sm rtl:data-\[side=left]:data-ending-style:translate-x-\[2.5rem] rtl:data-\[side=left]:data-starting-style:translate-x-\[2.5rem] rtl:data-\[side=right]:data-ending-style:translate-x-\[-2.5rem] rtl:data-\[side=right]:data-starting-style:translate-x-\[-2.5rem]", className, )} {...props} > {children} {showCloseButton && ( \<SheetPrimitive.Close data-slot="sheet-close" render={ \<Button variant="ghost" className="absolute end-3 top-3" size="icon-sm" /> } > \<XIcon /> \<span className="sr-only">Close\</span> \</SheetPrimitive.Close> )} \</SheetPrimitive.Popup> \</SheetPortal> ); } function SheetHeader({ className, ...props }: React.ComponentProps<"div">) { return ( \<div data-slot="sheet-header" className={cn("flex flex-col gap-0.5 p-4", className)} {...props} /> ); } function SheetFooter({ className, ...props }: React.ComponentProps<"div">) { return ( \<div data-slot="sheet-footer" className={cn("mt-auto flex flex-col gap-2 p-4", className)} {...props} /> ); } function SheetTitle({ className, ...props }: SheetPrimitive.Title.Props) { return ( \<SheetPrimitive.Title data-slot="sheet-title" className={cn( "cn-font-heading text-foreground text-base font-medium", className, )} {...props} /> ); } function SheetDescription({ className, ...props }: SheetPrimitive.Description.Props) { return ( \<SheetPrimitive.Description data-slot="sheet-description" className={cn("text-muted-foreground text-sm", className)} {...props} /> ); } export { Sheet, SheetTrigger, SheetClose, SheetContent, SheetHeader, SheetFooter, SheetTitle, SheetDescription, };

## Usage

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

export function Example() {
  return (
    <Sheet>
      <SheetTrigger render={<Button variant="outline">Open threads</Button>} />
      <SheetContent side="right">
        <SheetHeader>
          <SheetTitle>Threads</SheetTitle>
          <SheetDescription>Recent conversations.</SheetDescription>
        </SheetHeader>
      </SheetContent>
    </Sheet>
  );
}
```

## Examples

### Sides

The mobile thread list in the assistant-ui examples is a `left` sheet.

\[interactive preview component SheetSidesSpecimen omitted]

Code for SheetSidesSpecimen preview:

```tsx
"use client";

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

function SheetSidesSpecimen(): ReactNode {
  return (
    {(["left", "right", "top", "bottom"] as const).map((side) => (
      <Sheet key={side}>
        <SheetTrigger render={<Button variant="outline">{side}</Button>} />
        <SheetContent side={side}>
          <SheetHeader>
            <SheetTitle>From the {side}</SheetTitle>
            <SheetDescription>
              The panel enters from the {side} edge.
            </SheetDescription>
          </SheetHeader>
        </SheetContent>
      </Sheet>
    ))}
  );
}
```

## API Reference

The parts wrap Base UI's Dialog primitives and accept their props.

### SheetContent

- `side`: `"top" | "right" | "bottom" | "left"` (default `"right"`) — The edge the panel enters from.
- `showCloseButton`: `boolean` (default `true`) — Renders the corner close control.