Skip to content

Overview

One React component for bottom sheets, drawers, dialogs, side panels, and toasts. Start with install, or jump straight to a topic below.

Install

Add scrollsheet with bun, npm, pnpm, or yarn, or pull individual patterns in with the shadcn CLI. Read more

Anatomy

The five pieces of a scrollsheet Root, Trigger, Content, Handle, Title, Description, and Close, and what each one owns. Read more

CSS

scrollsheet ships mechanics only; style the panel with a plain className like any other card. Read more

Tailwind

Tailwind v4 utilities apply directly to Sheet.Content and Sheet.Handle, no plugin or config required. Read more

Detents

DetentSpec accepts full, medium, content, a fraction, or pixels, and onTravel reports progress against each one. Read more

Imperative control

actionsRef exposes open, close, and snapTo for deep links, push notifications, and any caller without a prop to drill through. Read more

Nested and morph

A Sheet.Root rendered inside another sheet’s content registers as a child automatically, and the sheet morphs height when its content changes. Read more

Scrollable content inside a sheet

Mark an inner scroller with data-scrollsheet-nested-scroll so a swipe scrolls the list first and hands off to sheet travel at the edge. Read more

Keyboard

scrollsheet reads visualViewport and writes the on-screen keyboard inset to –scrollsheet-keyboard automatically, no prop required. Read more

Side panels

side anchors a sheet to bottom, top, left, or right; left and right sheets scroll on the x axis and their detents are widths. Read more

Non-modal

modal={false} keeps the page fully interactive behind the sheet, with no backdrop, no focus trap, and no inert-ing the rest of the page. Read more

Desktop drag

Mouse pointers can grab the panel anywhere and fling it; touch and pen keep riding native scroll-snap instead. Read more

Desktop presentation

Above 768px, bottom sheets dock right as a fixed-width drawer and side sheets square off their lead edge; below it, sheets stay full-bleed. Read more

Page transitions

Mark your page wrapper data-scrollsheet-background and backgroundEffect scales, insets, and rounds it in sync with the sheet’s travel, iOS-style. Read more

Toasts

toast() and Toaster ship in the package: per-toast DOM, six positions, swipe to dismiss, Sonner-compatible API. Read more

Using scrollsheet with Motion

onTravel and –scrollsheet-progress are the integration boundary for Motion or any animation library that takes a plain number. Read more

Migrate from vaul

Drop-in replacement for vaul’s Drawer namespace, same props, same data-vaul-* attributes for existing CSS. Read more

Migrate from Sonner

Drop-in replacement for toast, useToasts, and Toaster, running on the same per-toast DOM as real Sonner, with every position, per-toast overrides, and swipe-to-dismiss. Read more

Shadow DOM

injectStylesInto adopts the core stylesheet into a shadow root, since document.head styles don’t cross the boundary on their own. Read more

Framework support

SSR-safe by design across Next.js App Router and Pages Router, Remix, React Router v7, Astro, and plain Vite. Read more

API

Full prop reference for Sheet.Root, the styling hooks on Sheet.Content, and every other named export. Read more

Accessibility

Focus containment comes from the platform’s own showModal(), and Sheet.Handle exposes role=slider with full keyboard control once there are two or more detents. Read more

Browser support

dialog and spring easing reach about 96% of browsers; the remaining ~4% get a plain fixed-position modal with no drag or detents. Read more

Which example shows which prop

A table mapping every Sheet.Root prop to the example that exercises it. Read more

Cart and checkout

Full source on GitHub ↗

A checkout that locks itself while it processes, then hands dismissal back when it settles.