# A sheet is a dialog pinned to an edge, so choose by the task

> Sheet extends Dialog in shadcn. Choose a dialog for a decision and a sheet for a side task, with code for each in React.
> By Dave · 2026-10-13
> Source: https://otf-kit.dev/blog/shadcn-dialog-vs-sheet-howto

A sheet is a dialog that slides in from one edge. The shadcn docs build both on the same primitive and give no rule for choosing between them.

## What the shadcn docs say

The Dialog page describes the component as "A window overlaid on either the primary window or another dialog window, rendering the content underneath inert." Its usage example opens with a trigger, then a content panel with a header, a title and a description. It hides the close button with `showCloseButton={false}`. The page does not say when to use a dialog.

The Sheet page describes the component as "Extends the Dialog component to display content that complements the main content of the screen." The `side` prop on `SheetContent` takes `top`, `right`, `bottom` or `left`, and the page does not state a default. Like the dialog, the sheet hides its close button with `showCloseButton={false}`, set on `SheetContent`. The page has no sentence about when to use a sheet either.

So the docs tell you what each component is and how to place it. They do not tell you which one a task needs. The rest of this article is our rule for that choice, and it is labelled as ours.

## One base, two placements

Because the sheet extends the dialog, you write the same kind of code for both. The trigger, the content, the header and the footer have the same roles. The difference you choose is where the content appears on screen and how much of the page stays in view around it.

That is why the choice is about the task. A dialog takes the centre of the screen and asks for an answer. A sheet takes one edge and leaves the rest of the page in view. Pick the placement that matches what the user must do next.

## A dialog for a decision

Use a dialog when the user has to answer before the task can continue. A confirmation is the clearest case. The docs' own usage example is a confirmation, and its title asks "Are you absolutely sure?" Here is the same shape for a delete:

```tsx
import { Button } from "@/components/ui/button"
import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
  DialogTrigger,
} from "@/components/ui/dialog"

export function DeleteProject() {
  return (
    <Dialog>
      <DialogTrigger>Delete project</DialogTrigger>
      <DialogContent>
        <DialogHeader>
          <DialogTitle>Delete this project?</DialogTitle>
          <DialogDescription>
            This removes the project and its files. You cannot undo it.
          </DialogDescription>
        </DialogHeader>
        <DialogFooter>
          <Button variant="outline">Cancel</Button>
          <Button variant="destructive">Delete</Button>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  )
}
```

The trigger takes its label as children, as the docs' usage example does. The footer holds the actions. Put the safe action first and the destructive action last, so the reading order matches the choice.

![Luna points at a close control on a side panel that slides in from the right edge](https://cdn.otf-kit.dev/blog/shadcn-dialog-vs-sheet-howto/inbody1-20261013.png)

## A sheet for a side task

Use a sheet when the user should keep seeing the page behind the content. Filters, the details of one row and settings that change the view are common cases. The user can watch the list update while the panel is open, and the edge tells them where the content came from.

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

export function FilterPanel() {
  return (
    <Sheet>
      <SheetTrigger>Filters</SheetTrigger>
      <SheetContent side="right">
        <SheetHeader>
          <SheetTitle>Filter results</SheetTitle>
          <SheetDescription>
            Your list updates as you change these.
          </SheetDescription>
        </SheetHeader>
      </SheetContent>
    </Sheet>
  )
}
```

The `side` prop sets the edge. The sample uses `right`, which suits a detail or filter panel that opens beside the list. Our suggestion is `left` for navigation-style panels. The docs list all four values, so you are not limited to these two.

## Pick by the task

These are our rules, not the docs'.

- Use a dialog when the user must answer before continuing, such as a confirmation or a short form that has to finish first.
- Use a sheet when the user should still see the page behind the content, such as filters, a row's details or settings.
- Avoid a sheet for a single yes-or-no question. The edge panel adds a slide the question does not need, and a dialog reads faster.
- Match the edge to the content. Details often sit on the right, and navigation often sits on the left. If your product already uses one convention, follow it.

If you are unsure, ask what happens to the page behind the content. If the answer is nothing until the user responds, use a dialog. If the answer is that the user keeps working on the page, use a sheet.

![Dex at a centred dialog on the left and Luna at a side sheet on the right](https://cdn.otf-kit.dev/blog/shadcn-dialog-vs-sheet-howto/inbody2-20261013.png)

## Two mistakes to avoid

The first mistake is a sheet for a confirmation. The user slides a panel in from the edge, reads one question, and still has to pick an action. The slide adds a step without adding information, and a dialog answers the same question faster.

The second mistake is a dialog for a long settings form. A centred dialog often becomes a small scrolling box over the page, and users lose the context they were changing. A sheet on one edge gives the form more height and keeps the page in view, which is the trade you want for a task like that.

## Test before you ship

Both components fail in the same places, so test these for each one:

- Tab to the trigger and press Enter. Focus should move inside the content.
- Press Escape. The content should close, and focus should return to the trigger.
- Hide the close button with `showCloseButton={false}` only when the content has its own way out. Then check that a keyboard user can leave without a mouse.
- On a 390px phone, a right-side sheet can cover most of the screen. Check that its close control and its primary action are both reachable, with no horizontal overflow.
- Check that the page behind the overlay does not scroll while the content is open.

Run the phone check last. A layout that works on a desktop can hide the one action a phone user needs.

## Where OTF stands

If you want a dialog or a sheet already built into a product, we cannot point you to one. We checked the SaaS Dashboard, Booking, Fitness and Arcade kit pages, and none of them names a dialog or a sheet component. So this article does not say any OTF kit ships one. The pricing page lists the Full-stack kits at $99 each and the Everything Bundle at $149, both as one-time payments, and it offers a live demo before you buy. Check each kit's repository for the components it ships before you rely on any of them.

The [data table guide](/blog/shadcn-data-table-howto) covers a table that often opens its row details in one of these panels, and the [combobox guide](/blog/shadcn-combobox-howto) covers the searchable select that often sits inside a dialog form. If you are choosing a starting point for a product, the pricing page is the place to compare kits side by side: [OTF pricing](/pricing).

## Sources

- shadcn/ui, Dialog: https://ui.shadcn.com/docs/components/dialog
- shadcn/ui, Sheet: the Sheet page under Components on the same site
- OTF pricing: [otf-kit.dev/pricing](/pricing)
