shadcn.io is an independent project built on shadcn/ui
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.
Installation
Anatomy

SheetTrigger
SheetOverlay
SheetPositioner
SheetContent
SheetTitle
SheetDescription
SheetCloseUsage
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'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.
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.
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.
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.
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"
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"
asChild?boolean
Merge props onto the child element instead of rendering a host.
SheetClose
data-slot="sheet-close"
asChild?boolean
Merge props onto the child element instead of rendering a host.
SheetPortal
data-slot="sheet-portal"
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"
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"
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.
data-side?string
Mirrors side.
SheetHeader
<div> data-slot="sheet-header"
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"
className?string
Merged with the part's classes.
SheetTitle
data-slot="sheet-title"
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"
asChild?boolean
Merge props onto the child element instead of rendering a host.
className?string
Merged with the part's classes.












