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, React Aria 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, SheetDescription, SheetFooter, SheetHeader,
SheetTitle, SheetTrigger,
} from "@/components/ui/sheet";const SheetDemo = () => (
<SheetTrigger>
<Button variant="outline">Open</Button>
<Sheet>
<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>
<FieldLabel htmlFor="sheet-demo-name">Name</FieldLabel>
<Input
className="min-w-0 max-w-full"
defaultValue="Pedro Duarte"
id="sheet-demo-name"
type="text"
/>
</Field>
<Field>
<FieldLabel htmlFor="sheet-demo-username">Username</FieldLabel>
<Input
className="min-w-0 max-w-full"
defaultValue="@peduarte"
id="sheet-demo-username"
type="text"
/>
</Field>
</div>
<SheetFooter>
<Button type="submit">Save changes</Button>
<SheetClose variant="outline">Close</SheetClose>
</SheetFooter>
</Sheet>
</SheetTrigger>
);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 isOpen so onOpenChange receives the next boolean, set defaultOpen for the first state, and SheetTrigger wraps both the button and the Sheet.
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 React Aria?
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. SheetTrigger wraps the button and the Sheet together, and the button is the first child. There is no asChild prop.
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-content"
defaultOpen?boolean
Whether the overlay is open by default (uncontrolled).
onOpenChange?(isOpen: boolean) → void
Handler that is called when the overlay's open state changes.
- Parameters
isOpen– boolean- Returns
void
isDismissable?boolean
Whether to close the modal when the user interacts outside it.
- Default
false
isEntering?boolean
Whether the modal is currently performing an entry animation.
isExiting?boolean
Whether the modal is currently performing an exit animation.
isKeyboardDismissDisabled?boolean
Whether pressing the escape key to close the modal should be disabled.
- Default
false
isOpen?boolean
Whether the overlay is open by default (controlled).
onEnter?(element: HTMLElement) → void | Promise<void>
Callback when the modal starts entering. Return a promise to indicate when the entry animation has completed.
- Type
(element: HTMLElement) → void | Promise<void>- Parameters
element– HTMLElement- Returns
void | Promise<void>
onExit?(element: HTMLElement) → void | Promise<void>
Callback when the modal starts exiting. Return a promise to indicate when the exit animation has completed.
- Type
(element: HTMLElement) → void | Promise<void>- Parameters
element– HTMLElement- Returns
void | Promise<void>
overscrollPadding?boolean
Whether to add padding to the sheet so that it appears to continue outside the viewport.
shouldCloseOnInteractOutside?(element: Element) → boolean
When user interacts with the argument element outside of the overlay ref, return true if onClose should be called. This gives you a chance to filter out interaction with elements that should not dismiss the overlay. By default, onClose will always be called on interaction outside the overlay ref.
- Parameters
element– Element- Returns
boolean
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"
slot?string | null
A slot name for the component. Slots allow the component to receive props from a parent
component. An explicit null value indicates that the local props completely override all
props received from a parent.
- Type
string | null
stackAnimation?string
CSS @keyframes name for the animation that occurs when a sheet has descendants.
style?StyleOrFunction<T>
The inline [style](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/style) for the element. A function may be provided to compute the style based on component state.
- Type
StyleOrFunction<T>
swipeAnimation?string
CSS @keyframes name for the animation that occurs while the user swipes.
swipeAnimationRange?{ start?: number; end?: number; }
The snap point indices between which the swipe animation occurs. If omitted, the animation will occur over the full range of the sheet's movement.
- Type
{ start?: number; end?: number; }
UNSTABLE_deferUntilEntered?boolean
Whether to wait until the entry animation completes before hiding content outside the modal and moving focus into it.
UNSTABLE_overrideFocus?boolean
Whether to override the focus() method so it does not scroll the page.
This is temporary and will be removed after fully testing all overlays.
UNSTABLE_portalContainer?Element
The container element in which the overlay portal will be placed. This may have unknown behavior depending on where it is portalled to. Deprecated. - Use a parent UNSAFE_PortalProvider to set your portal container instead.
- Default
document.body
render?DOMRenderFunction<E, T>
Overrides the default DOM element with a custom render function.
This allows rendering existing components with built-in styles and behaviors
such as router links, animation libraries, and pre-styled components.
Requirements:
- You must render the expected element type (e.g. if <button> is expected, you cannot render an
<a>).
- Only a single root DOM element can be rendered (no fragments).
- You must pass through props and ref to the underlying DOM element, merging with your own prop
as appropriate.
- Type
DOMRenderFunction<E, T>
className?string
Merged with the part's classes.
data-side?string
Mirrors side.
SheetTrigger
data-slot="sheet-trigger"
defaultOpen?boolean
Whether the overlay is open by default (uncontrolled).
onOpenChange?(isOpen: boolean) → void
Handler that is called when the overlay's open state changes.
- Parameters
isOpen– boolean- Returns
void
isOpen?boolean
Whether the overlay is open by default (controlled).
SheetClose
data-slot="sheet-close"
children?ReactNode
The content to display in the button.
clickEffect?boolean
Apply a click effect to the button.
- Default
true
download?LinkPrimitiveProps["download"]
With href, downloads the linked file instead of opening it.
href?string
When set, the button renders as a link.
isDisabled?boolean
Whether the button is disabled.
isLoading?boolean
Show a loading indicator.
- Default
false
rel?string
With href, the link's relationship to the page (noreferrer, …).
target?string
With href, where the linked page opens (_blank, …).
className?string
Merged with the part's classes.
SheetOverlay
data-slot="sheet-overlay"
defaultOpen?boolean
Whether the overlay is open by default (uncontrolled).
onOpenChange?(isOpen: boolean) → void
Handler that is called when the overlay's open state changes.
- Parameters
isOpen– boolean- Returns
void
isDismissable?boolean
Whether to close the modal when the user interacts outside it.
- Default
false
isEntering?boolean
Whether the modal is currently performing an entry animation.
isExiting?boolean
Whether the modal is currently performing an exit animation.
isKeyboardDismissDisabled?boolean
Whether pressing the escape key to close the modal should be disabled.
- Default
false
isOpen?boolean
Whether the overlay is open by default (controlled).
onEnter?(element: HTMLElement) → void | Promise<void>
Callback when the modal starts entering. Return a promise to indicate when the entry animation has completed.
- Type
(element: HTMLElement) → void | Promise<void>- Parameters
element– HTMLElement- Returns
void | Promise<void>
onExit?(element: HTMLElement) → void | Promise<void>
Callback when the modal starts exiting. Return a promise to indicate when the exit animation has completed.
- Type
(element: HTMLElement) → void | Promise<void>- Parameters
element– HTMLElement- Returns
void | Promise<void>
position?"bottom" | "top" | "left" | "right" | "start" | "end" | "center"
The placement of the sheet on the screen. start and end are mirrored in right-to-left
locales.
- Type
"bottom" | "top" | "left" | "right" | "start" | "end" | "center"- Default
"bottom"
preventDismissal?boolean
Whether to prevent the user from closing the sheet by swiping, pressing Escape, or interacting outside it.
- Default
false
shouldCloseOnInteractOutside?(element: Element) → boolean
When user interacts with the argument element outside of the overlay ref, return true if onClose should be called. This gives you a chance to filter out interaction with elements that should not dismiss the overlay. By default, onClose will always be called on interaction outside the overlay ref.
- Parameters
element– Element- Returns
boolean
slot?string | null
A slot name for the component. Slots allow the component to receive props from a parent
component. An explicit null value indicates that the local props completely override all
props received from a parent.
- Type
string | null
snapPoints?Array<number | string>
Snap points the sheet will stop at, expressed as the amount of the sheet that is visible. Numbers are pixels, and percentages are relative to the size of the sheet. Sheets initially open to the first snap point.
- Type
Array<number | string>
style?StyleOrFunction<T>
The inline [style](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/style) for the element. A function may be provided to compute the style based on component state.
- Type
StyleOrFunction<T>
swipeDirection?"bottom" | "top" | "vertical" | "left" | "right" | "start" | "end" | "horizontal"
The direction the sheet can be swiped. start and end are mirrored in right-to-left locales.
Defaults to the same direction as position.
- Type
"bottom" | "top" | "vertical" | "left" | "right" | "start" | "end" | "horizontal"
UNSTABLE_deferUntilEntered?boolean
Whether to wait until the entry animation completes before hiding content outside the modal and moving focus into it.
UNSTABLE_overrideFocus?boolean
Whether to override the focus() method so it does not scroll the page.
This is temporary and will be removed after fully testing all overlays.
UNSTABLE_portalContainer?Element
The container element in which the overlay portal will be placed. This may have unknown behavior depending on where it is portalled to. Deprecated. - Use a parent UNSAFE_PortalProvider to set your portal container instead.
- Default
document.body
render?DOMRenderFunction<E, T>
Overrides the default DOM element with a custom render function.
This allows rendering existing components with built-in styles and behaviors
such as router links, animation libraries, and pre-styled components.
Requirements:
- You must render the expected element type (e.g. if <button> is expected, you cannot render an
<a>).
- Only a single root DOM element can be rendered (no fragments).
- You must pass through props and ref to the underlying DOM element, merging with your own prop
as appropriate.
- Type
DOMRenderFunction<E, T>
className?string
Merged with the part's classes.
SheetContent
defaultOpen?boolean
Whether the overlay is open by default (uncontrolled).
onOpenChange?(isOpen: boolean) → void
Handler that is called when the overlay's open state changes.
- Parameters
isOpen– boolean- Returns
void
isDismissable?boolean
Whether to close the modal when the user interacts outside it.
- Default
false
isEntering?boolean
Whether the modal is currently performing an entry animation.
isExiting?boolean
Whether the modal is currently performing an exit animation.
isKeyboardDismissDisabled?boolean
Whether pressing the escape key to close the modal should be disabled.
- Default
false
isOpen?boolean
Whether the overlay is open by default (controlled).
onEnter?(element: HTMLElement) → void | Promise<void>
Callback when the modal starts entering. Return a promise to indicate when the entry animation has completed.
- Type
(element: HTMLElement) → void | Promise<void>- Parameters
element– HTMLElement- Returns
void | Promise<void>
onExit?(element: HTMLElement) → void | Promise<void>
Callback when the modal starts exiting. Return a promise to indicate when the exit animation has completed.
- Type
(element: HTMLElement) → void | Promise<void>- Parameters
element– HTMLElement- Returns
void | Promise<void>
overscrollPadding?boolean
Whether to add padding to the sheet so that it appears to continue outside the viewport.
role?"dialog" | "alertdialog"
The accessibility role for the dialog.
- Type
"dialog" | "alertdialog"- Default
"dialog"
shouldCloseOnInteractOutside?(element: Element) → boolean
When user interacts with the argument element outside of the overlay ref, return true if onClose should be called. This gives you a chance to filter out interaction with elements that should not dismiss the overlay. By default, onClose will always be called on interaction outside the overlay ref.
- Parameters
element– Element- Returns
boolean
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"
slot?string | null
A slot name for the component. Slots allow the component to receive props from a parent
component. An explicit null value indicates that the local props completely override all
props received from a parent.
- Type
string | null
stackAnimation?string
CSS @keyframes name for the animation that occurs when a sheet has descendants.
style?StyleOrFunction<T>
The inline [style](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/style) for the element. A function may be provided to compute the style based on component state.
- Type
StyleOrFunction<T>
swipeAnimation?string
CSS @keyframes name for the animation that occurs while the user swipes.
swipeAnimationRange?{ start?: number; end?: number; }
The snap point indices between which the swipe animation occurs. If omitted, the animation will occur over the full range of the sheet's movement.
- Type
{ start?: number; end?: number; }
UNSTABLE_deferUntilEntered?boolean
Whether to wait until the entry animation completes before hiding content outside the modal and moving focus into it.
UNSTABLE_overrideFocus?boolean
Whether to override the focus() method so it does not scroll the page.
This is temporary and will be removed after fully testing all overlays.
UNSTABLE_portalContainer?Element
The container element in which the overlay portal will be placed. This may have unknown behavior depending on where it is portalled to. Deprecated. - Use a parent UNSAFE_PortalProvider to set your portal container instead.
- Default
document.body
render?DOMRenderFunction<E, T>
Overrides the default DOM element with a custom render function.
This allows rendering existing components with built-in styles and behaviors
such as router links, animation libraries, and pre-styled components.
Requirements:
- You must render the expected element type (e.g. if <button> is expected, you cannot render an
<a>).
- Only a single root DOM element can be rendered (no fragments).
- You must pass through props and ref to the underlying DOM element, merging with your own prop
as appropriate.
- Type
DOMRenderFunction<E, T>
className?string
Merged with the part's classes.
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
level?number
The heading level.
- Default
3
render?DOMRenderFunction<E, T>
Overrides the default DOM element with a custom render function.
This allows rendering existing components with built-in styles and behaviors
such as router links, animation libraries, and pre-styled components.
Requirements:
- You must render the expected element type (e.g. if <button> is expected, you cannot render an
<a>).
- Only a single root DOM element can be rendered (no fragments).
- You must pass through props and ref to the underlying DOM element, merging with your own prop
as appropriate.
- Type
DOMRenderFunction<E, T>
className?string
Merged with the part's classes.
SheetDescription
<div>
className?string
Merged with the part's classes.












