---
title: Sheet
description: Extends the Dialog component to display content that complements the main content of the screen.
icon: panel-right
author:
  name: imskyleen
  url: https://github.com/imskyleen
search:
  keywords: [side panel, slide-over, drawer]
related:
  - /dialog
  - /drawer
mode: wide
---

<PoweredBy
  packages={[
    { name: 'Base UI', url: 'https://base-ui.com/react/components/dialog' },
    { name: 'Shadcn UI', url: 'https://ui.shadcn.com/docs/components/sheet' },
    { name: 'Lucide', url: 'https://lucide.dev/' },
  ]}
/>

### Installation

```package-install
npx shadcn@latest add @redpanda/sheet
```

<ComponentPreview name="sheet-demo" />

## When to use

Sheets display supplementary content that slides from screen edges. Use this decision tree:

```mermaid
graph TD;
    A[❓ Need overlay content?] --> B{Covers full screen?};
    A --> C[ℹ️ Use other components];
    B --> D{Slides from side?};
    B --> E[✅ Use Dialog];
    D --> F[✅ Use Sheet];
    D --> G[✅ Use Popover];
    F --> H[✅ Choose side: top/right/bottom/left];

    classDef recommended fill:#4ade80,stroke:#22c55e,stroke-width:2px,color:#000;
    classDef discouraged fill:#fbbf24,stroke:#f59e0b,stroke-width:2px,color:#000;
    classDef decision fill:#60a5fa,stroke:#3b82f6,stroke-width:2px,color:#fff;
    classDef neutral fill:#9ca3af,stroke:#6b7280,stroke-width:2px,color:#fff;

    class E,F,G,H recommended;
    class A,B,D decision;
    class C neutral;
```

## Usage

```tsx
<Sheet>
  <SheetTrigger>Open Sheet</SheetTrigger>
  <SheetContent>
    <SheetHeader>
      <SheetTitle>Title</SheetTitle>
      <SheetDescription>Description</SheetDescription>
    </SheetHeader>
    <p>Sheet Content</p>
    <SheetFooter>
      <button>Close</button>
    </SheetFooter>
  </SheetContent>
</Sheet>
```

## Anatomy

The Sheet component extends Dialog for side-sliding content:

```
<Sheet> (Dialog-based)
├── <SheetTrigger> (Button)
├── <SheetContent> (Sliding Panel)
│   ├── Side positioning (top/right/bottom/left)
│   ├── Animation (slide + fade)
│   ├── <SheetHeader>
│   │   ├── <SheetTitle> (h2)
│   │   └── <SheetDescription>
│   ├── Content Area (scrollable)
│   └── <SheetFooter>
├── Sheet Overlay (backdrop)
└── Focus management
```

## Props

### Base UI

<div className="flex flex-row gap-x-3">
  <ExternalLink href="https://base-ui.com/react/components/dialog" text="Docs" />
  <ExternalLink href="https://base-ui.com/react/components/dialog#api-reference" text="API Reference" />
</div>

### Shadcn UI

<div className="flex flex-row gap-x-3">
  <ExternalLink href="https://ui.shadcn.com/docs/components/sheet" text="Docs" />
</div>

### SheetContent

| Prop | Type | Default | Description |
| - | - | - | - |
| `side?` | `'top' \| 'right' \| 'bottom' \| 'left'` | `'right'` | Viewport edge the panel docks to and slides in from. |
| `size?` | `'sm' \| 'md' \| 'lg' \| 'xl' \| '2xl' \| 'full'` | `'xl'` | Caps how wide a left or right sheet grows. A top or bottom sheet spans the full viewport width and takes its height from its content, so size does not apply to it. |
| `showCloseButton?` | `boolean` | `true` | Render the top-right close affordance. |
| `closeButtonGutter?` | `boolean` | `false` | Pad the top so content starts below the close button. Opt-in because it moves content down. Ignored when showCloseButton is false. |

The close button sits over the content, so a top-right control of your own (a code block's copy
button, a banner's dismiss) collides with it. Set `closeButtonGutter` to start the content below it:

```tsx lineNumbers
<SheetContent closeButtonGutter>
  <SimpleCodeBlock code={bootstrapServers} language="bash" />
</SheetContent>
```

## Validated sheet form

<ComponentPreview name="sheet-with-form-demo" />

The profile example keeps its actions visible while the field area scrolls. It uses the standard Sheet spacing and
Field composition: invalid inputs are associated with every visible error. Website is optional; non-empty values
must be complete HTTP or HTTPS URLs, not just a protocol prefix. Save closes the demo only after validation passes;
it does not send a profile update to a server.

## Keyboard interactions

| Key                                 | Action                                                       |
| ----------------------------------- | ------------------------------------------------------------ |
| `Enter` / `Space` on `SheetTrigger` | Opens the sheet and focuses the first focusable element.     |
| `Tab` / `Shift+Tab`                 | Cycles focus within the sheet — focus is trapped while open. |
| `Escape`                            | Closes the sheet and restores focus to the trigger.          |
| Click outside `SheetContent`        | Closes the sheet.                                            |

## Accessibility

- Always include a `SheetTitle` (visually hidden is fine) — Base UI requires it for the accessible name.
- Add a `SheetDescription` when the purpose isn't obvious from the title.
- Focus is trapped inside the panel while open and returned to the trigger on close; background is `aria-hidden` and
  inert.
- The sheet behaves exactly like `Dialog` semantically — it's a side-docked Dialog with a slide transition.

## Controlled vs uncontrolled

Uncontrolled — let the sheet own open state:

```tsx lineNumbers
<Sheet onOpenChange={(open) => console.log('open:', open)}>
  <SheetTrigger>Open</SheetTrigger>
  <SheetContent side="right">{/* ... */}</SheetContent>
</Sheet>
```

Controlled — drive `open` from parent state (e.g. to open the sheet from a table row action):

```tsx lineNumbers
const [open, setOpen] = useState(false);

<Sheet open={open} onOpenChange={setOpen}>
  <SheetContent side="right">
    <SheetHeader>
      <SheetTitle>Edit topic</SheetTitle>
    </SheetHeader>
    {/* ... */}
  </SheetContent>
</Sheet>;
```

## Credits

- We use [Base UI](https://base-ui.com/react/components/dialog) for the sheet component.
- We take our inspiration from [Shadcn UI](https://ui.shadcn.com/docs/components/sheet) for the sheet style.
