Skip to content

shadcn.io is an independent project built on shadcn/ui

Components

Free

Sheet for Radix UI

Slide a panel in from any edge for a form or detail, leaving the page in sight. For React with Radix UI, when a centered dialog would hide too much.

The panel is anchored to one edge of the screen, rather than floating over the center.

Use it when the task needs its own surface and the page underneath should stay in sight. A short confirmation fits better in a Dialog. In React, Radix UI traps focus while the panel is open, and it can sit on any edge, including the bottom on a narrow screen.

Preview width

Installation

Sign in to get your install command
Free with an account. It carries your token, so the CLI installs everything in one go.
Use email insteadNo credit card

Anatomy

Sheet anatomy: content, close, title, description, footer
SheetTrigger
SheetOverlay
SheetPositioner
SheetContent
SheetTitle
SheetDescription
SheetClose

Usage

import { Button } from "@/components/ui/button";
import { Field, FieldLabel } from "@/components/ui/field";
import { Input } from "@/components/ui/input";
import {
  Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter,
  SheetHeader, SheetTitle, SheetTrigger,
} from "@/components/ui/sheet";
<Sheet>
  <SheetTrigger asChild>
    <Button variant="outline">Open</Button>
  </SheetTrigger>
  <SheetContent>
    <SheetHeader>
      <SheetTitle>Edit profile</SheetTitle>
      <SheetDescription>
        Make changes to your profile here. Click save when you&apos;re done.
      </SheetDescription>
    </SheetHeader>
    <div className="grid flex-1 auto-rows-min gap-6 px-4">
      <Field className="grid gap-3">
        <FieldLabel>Name</FieldLabel>
        <Input defaultValue="Pedro Duarte" type="text" />
      </Field>
      <Field className="grid gap-3">
        <FieldLabel>Username</FieldLabel>
        <Input defaultValue="@peduarte" type="text" />
      </Field>
    </div>
    <SheetFooter>
      <Button type="submit">Save changes</Button>
      <SheetClose asChild>
        <Button variant="outline">Close</Button>
      </SheetClose>
    </SheetFooter>
  </SheetContent>
</Sheet>

Accessibility

Enter
Activate the focused control.
Space
Activate the focused control.

Demos

Side

side on SheetContent is top, right, bottom, or left, with right as the default. Top and bottom are capped at half the viewport here, so a long form scrolls inside the panel. Use the bottom edge on a phone, and a side edge for an inspector next to a list. The title, the focus trap, and Escape stay the same on every edge.

Preview width

No Close Button

showCloseButton set to false removes the corner icon. This panel then has a title and a description, and no footer button, so the written hint is to click outside. Hide that icon only when another close control is obvious, usually next to the primary action. Escape still dismisses a modal sheet, and the overlay click is a backup rather than the thing you want people to hunt for.

Preview width

Rtl

The trigger is Arabic, and the panel opens from the left with dir set to rtl on SheetContent. Title, fields, and footer actions align to the start of the line. Put dir on the content, not only the trigger, because the panel is portaled and can miss the page direction. side is still an explicit edge and does not flip on its own.

Preview width

FAQ

How do I control whether a Sheet is open in React?

The sheet starts closed and opens from its trigger. Control it when another part of the page must open or close it. Keep that boolean in state and pass it back, or use the default prop for the first state only. Pass open so onOpenChange receives the next boolean, and set defaultOpen when you only need the first state.

How do I change which edge a Sheet uses?

Set side on SheetContent to top, right, bottom, or left. The default is right, and bottom slides up from the bottom edge, which suits a narrow screen. The Side demo shows all four edges. The sheet still traps focus the same way on every edge.

Why does a Sheet need a SheetTitle?

SheetTitle names the dialog for assistive tech, so always render one. If the design has no visible heading, hide the title with the sr-only utility and keep it in the tree. SheetDescription explains the task and is optional. Escape closes the sheet and returns focus to the trigger.

How do I hide the Sheet close button?

The close button is shown by default. Set showCloseButton to false on SheetContent to hide it, as the No Close Button demo does. Still offer a SheetClose in the footer, or people may not see how to dismiss it. Escape still closes a modal sheet.

How do I use a button as the Sheet trigger with Radix UI?

The trigger should look like your button, not like an extra wrapper. Put the visible label on the button so the control has an accessible name. Put one Button inside SheetTrigger and set asChild, so the trigger merges onto that button.

Should I use a Sheet, a Dialog, or a Drawer?

Use a Sheet when the panel should slide in from a screen edge and leave part of the page in view. Use a Dialog for a centered modal, such as a short confirmation. Use a Drawer when the panel should support swipe, usually from the bottom on a phone.

Why is my Sheet behind another layer, or clipping a long form?

Sheet content is portaled and stacked at z-50, so it sits above the page. If another overlay uses the same layer, edit the sheet file under components/ui and raise the overlay and content together. If a long form is clipped, let the body scroll, for example by wrapping it in a Scroll Area.

How do I install Sheet in React?

Run the command in the Installation section above. It adds Sheet and its parts to your ui folder. Import from that folder when the command finishes, instead of copying the source by hand, and rerun the command later if you want the latest files. The command uses the shadcn CLI.

API Reference

Sheet

data-slot="sheet"

PropType
open?boolean

Whether it's open. Pair with onOpenChange to control it.

defaultOpen?boolean

Whether it starts open, when you don't control open.

onOpenChange?{ bivarianceHack(open: boolean): void; }["bivarianceHack"]

Called when it opens or closes.

Type
{ bivarianceHack(open: boolean): void; }["bivarianceHack"]
modal?boolean

Whether it's modal: the rest of the page can't be reached and focus stays inside while it's open.

SheetTrigger

data-slot="sheet-trigger"

PropType
asChild?boolean

Merge props onto the child element instead of rendering a host.

SheetClose

data-slot="sheet-close"

PropType
asChild?boolean

Merge props onto the child element instead of rendering a host.

SheetPortal

data-slot="sheet-portal"

PropType
container?Element | DocumentFragment | null

Specify a container element to portal the content into.

Type
Element | DocumentFragment | null
forceMount?true

Used to force mounting when more control is needed. Useful when controlling animation with React animation libraries.

SheetOverlay

data-slot="sheet-overlay"

PropType
forceMount?true

Used to force mounting when more control is needed. Useful when controlling animation with React animation libraries.

asChild?boolean

Merge props onto the child element instead of rendering a host.

className?string

Merged with the part's classes.

SheetContent

data-slot="sheet-content"

PropType
showCloseButton?boolean

Show close button at the top right corner.

Default
true
side?"top" | "right" | "bottom" | "left"

Edge of the screen where the sheet appears.

Type
"top" | "right" | "bottom" | "left"
Default
"right"
asChild?boolean

Merge props onto the child element instead of rendering a host.

className?string

Merged with the part's classes.

AttributeType
data-side?string

Mirrors side.

SheetHeader

<div> data-slot="sheet-header"

PropType
description?string

The description of the sheet.

title?string

The title of the sheet.

className?string

Merged with the part's classes.

SheetFooter

<div> data-slot="sheet-footer"

PropType
className?string

Merged with the part's classes.

SheetTitle

data-slot="sheet-title"

PropType
asChild?boolean

Merge props onto the child element instead of rendering a host.

className?string

Merged with the part's classes.

SheetDescription

data-slot="sheet-description"

PropType
asChild?boolean

Merge props onto the child element instead of rendering a host.

className?string

Merged with the part's classes.