Sheet
A panel that slides in from any edge, with swipe-to-dismiss, scroll lock and stacked nesting.
Used in Pro blocks: Diff Review
pnpm dlx shadcn@latest add https://hextaui.com/r/sheet.jsonAdds the component, the HextaUI theme tokens and any HextaUI components it depends on.
Add the theme tokens to your global CSS, if you haven’t yet.
Install the dependencies.
pnpm add @base-ui/react @tabler/icons-react class-variance-authority cnCopy and paste the following code into your project.
components/ui/sheet.tsx components/ui/button.tsx Update the import paths to match your project setup.
Set side to slide in from any edge. Each sheet can be swiped back towards its own edge, and the close gesture follows your finger with momentum.
<SheetBody /> scrolls on its own, so the header and footer stay in place however long the content gets.
Set showCloseButton={false} when the footer already has a way out. Escape, the backdrop and swiping still close it.
A sheet or alert dialog opened from a sheet layers on top. The parent scales back slightly and each extra layer adds a lighter backdrop, so the stack stays readable. Escape closes only the top layer.
Pass open and onOpenChange to open it from anywhere, without a trigger.
Share one sheet between several triggers with createSheetHandle. Each trigger passes a payload that the sheet renders through a function child.
With dir="rtl", the right side mirrors to the left edge, slides in from there and swipes out the same way.
| Key | Action |
|---|---|
| EnterSpace | On the trigger, opens the sheet and moves focus inside it. |
| TabShift + Tab | Moves between focusable elements. Focus stays inside the sheet. |
| Esc | Closes the topmost sheet and returns focus to its trigger. |
- The sheet is a modal dialog.
<SheetTitle />labels it and<SheetDescription />describes it, so always include a title. - Page scroll is locked and content behind it is hidden from assistive tech while it is open.
- The close button is labelled “Close”. The handle on the inner edge shows the sheet can be dragged and is hidden from assistive tech.
- With reduced motion enabled, the sheet fades in and out instead of sliding.
Built on the Base UI drawer. Every part accepts the props of the primitive it wraps.
| Prop | Type | Default |
|---|---|---|
defaultOpen | boolean | false |
open | boolean | – |
onOpenChange | (open: boolean, details) => void | – |
onOpenChangeCompleteCalled after the open or close animation ends. | (open: boolean) => void | – |
modal | boolean | "trap-focus" | true |
disablePointerDismissalKeep it open when the backdrop is clicked. | boolean | false |
swipeDirectionSet automatically from the content’s side and direction. | "up" | "down" | "left" | "right" | – |
handle | SheetHandle<Payload> | – |
children | ReactNode | ({ payload }) => ReactNode | – |
| Prop | Type | Default |
|---|---|---|
handle | SheetHandle<Payload> | – |
payload | Payload | – |
render | ReactElement | (props, state) => ReactElement | <button> |
| Attribute | Description |
|---|---|
data-slot="sheet-trigger" | Target the trigger in CSS. |
data-popup-open | Present while its sheet is open. |
Renders the portal, backdrop, viewport and popup, plus the drag handle and close button.
| Prop | Type | Default |
|---|---|---|
side | "top" | "right" | "bottom" | "left" | "right" |
showCloseButton | boolean | true |
dirSets the direction for the sheet. right and left mirror in rtl. | "ltr" | "rtl" | – |
initialFocus | boolean | RefObject | (type) => HTMLElement | boolean | – |
finalFocus | boolean | RefObject | (type) => HTMLElement | boolean | – |
classNameSide sheets are 75% wide, up to 24rem from the sm breakpoint. | string | (state) => string | – |
render | ReactElement | (props, state) => ReactElement | <div> |
| Attribute | Description |
|---|---|
data-slot="sheet-content" | The sheet panel. |
data-side | The side it opens from. |
data-open | Present while the sheet is open. |
data-starting-style | Present while it animates in. |
data-ending-style | Present while it animates out. |
data-swiping | Present while it is being dragged. |
data-swipe-direction | The direction that dismisses it. |
data-nested-drawer-open | Present while a nested sheet is open on top. |
data-slot="sheet-handle" | The drag handle on the inner edge. |
data-slot="sheet-close-button" | The built-in close button. |
--drawer-swipe-movement-x | Horizontal drag distance. A -y variable exists too. |
--nested-drawers | How many nested sheets are open on top. |
Rendered by <SheetContent />. Nested layers get a lighter backdrop.
| Attribute | Description |
|---|---|
data-slot="sheet-overlay" | The backdrop. |
data-nested | Present on backdrops of nested layers. |
Plain <div> elements that lay out the sheet. The header leaves room for the close button, the body scrolls and takes the remaining height, and the footer stacks its actions on small screens and lines them up at the end from the sm breakpoint.
| Attribute | Description |
|---|---|
data-slot="sheet-header" | Title and description. |
data-slot="sheet-body" | Scrollable content. |
data-slot="sheet-footer" | Actions. |
| Prop | Type | Default |
|---|---|---|
render | ReactElement | (props, state) => ReactElement | <h2> |
| Attribute | Description |
|---|---|
data-slot="sheet-title" | Labels the sheet. |
| Prop | Type | Default |
|---|---|---|
render | ReactElement | (props, state) => ReactElement | <p> |
| Attribute | Description |
|---|---|
data-slot="sheet-description" | Describes the sheet. |
| Prop | Type | Default |
|---|---|---|
render | ReactElement | (props, state) => ReactElement | <button> |
| Attribute | Description |
|---|---|
data-slot="sheet-close" | Closes the sheet when pressed. |
createSheetHandle<Payload>() returns a handle that connects a <Sheet /> to triggers rendered elsewhere. Create it once, outside your component.
- ButtonButtons in every variant and size, with a built-in loading, success and error flow that skips the spinner for fast requests.
- Alert dialogA confirmation dialog for destructive or important actions that waits for async work, and becomes a bottom sheet on phones.
- CommandA searchable list of actions, inline or as a ⌘K palette, with pages, shortcuts and highlighted matches.
- Context menuA menu of actions on right click or long press, with submenus, checkbox and radio items, and hold feedback on touch.
- DialogA window over the page for forms and focused tasks, with a pinned header and footer, nesting, and a swipeable bottom sheet on phones.
- DrawerA panel that slides in from any edge and follows your finger, with snap points, a working handle and nested drawers that stack.
Used in Pro blocks
Blocks that build on Sheet.
Every block comes with HextaUI Pro, and Prompt Input is free.