Skip to content

Sheet

A gesture-driven sheet with snap points and accessible modal behavior.

Installation

Terminal
pnpm dlx shadcn@latest add \
  https://react-toolbox.io/r/sheet.json \
  https://react-toolbox.io/r/button.json

New here? Set up your project first. Styles, fonts, and required components are included. The command includes the components used by the examples below.

Usage

Use Sheet for dragging and snap points. Use Drawer for a fixed navigation surface. Gesture behavior comes from Vaul and is installed only with this family. Keep long content in an inner min-h-0 overflow-y-auto region so it can scroll while the surrounding sheet handles dragging.

Composition

Fractional snap points use the viewport height. The Expand notes and Reduce notes button provides a keyboard alternative to dragging the handle. Escape or the Done action dismisses the modal.

Show composition code
tsx
'use client';

import { useId, useState } from 'react';
import { Button } from '@/components/ui/material/button';
import {
  SheetRoot,
  SheetTrigger,
  SheetPortal,
  SheetOverlay,
  SheetContent,
  SheetTitle,
  SheetDescription,
  SheetClose,
  SheetHandle,
} from '@/components/ui/material/sheet';

export default function SheetExample() {
  const contentId = useId();
  const [snapPoint, setSnapPoint] = useState<number | string | null>(0.5);
  const expanded = snapPoint === 0.9;
  return (
    <SheetRoot snapPoints={[0.5, 0.9]} fadeFromIndex={0} activeSnapPoint={snapPoint} setActiveSnapPoint={setSnapPoint}>
      <SheetTrigger asChild>
        <Button primary raised>
          Open visit notes
        </Button>
      </SheetTrigger>
      <SheetPortal>
        <SheetOverlay />
        <SheetContent id={contentId}>
          <SheetHandle />
          <div className="mx-auto min-h-0 w-full max-w-xl overflow-y-auto p-6">
            <SheetTitle>Visit notes</SheetTitle>
            <SheetDescription>
              Drag the handle or use the resize button to switch between two sheet sizes.
            </SheetDescription>
            <p className="my-6 text-toolbox-body">Bring a notebook, a camera, and the latest survey map.</p>
            <Button
              primary
              aria-controls={contentId}
              aria-expanded={expanded}
              onClick={() => setSnapPoint(expanded ? 0.5 : 0.9)}
            >
              {expanded ? 'Reduce notes' : 'Expand notes'}
            </Button>
            <SheetClose asChild>
              <Button primary>Done</Button>
            </SheetClose>
          </div>
        </SheetContent>
      </SheetPortal>
    </SheetRoot>
  );
}

State and callbacks

  • SheetRoot accepts Vaul’s open/onOpenChange, direction, snapPoints, activeSnapPoint/setActiveSnapPoint, and dismissible options.
  • Use SheetHandle for drag and keyboard resizing; always provide a visible close action. SheetNestedRoot coordinates sheets opened from another sheet.
  • The parts forward primitive refs, events, and styling props. Full primitive API.

API reference

Key props and defaults. Full types and inline documentation are included with the source in material/sheet.

SheetRoot

Property Type Default
activeSnapPoint number | string | null

Controlled snap point; pair with setActiveSnapPoint.

—
autoFocus boolean

Move focus into the sheet when it opens.

true
container HTMLElement | null

Element that receives the portaled overlay and content. Defaults to document.body.

—
defaultOpen boolean

Open on mount without the enter animation.

false
direction 'top' | 'bottom' | 'left' | 'right'

Edge the sheet enters from.

'bottom'
dismissible boolean

Close on Escape, overlay interaction, or drag. With false, control open yourself.

true
modal boolean

Modal sheets trap focus and lock page scrolling.

true
onOpenChange (open: boolean) => void —
open boolean

Controlled visibility.

—
setActiveSnapPoint (snapPoint: number | string | null) => void —
snapPoints (number | string)[]

Viewport fractions or CSS lengths the sheet rests at.

—

SheetHandle

Property Type Default
preventCycle boolean

Keep a handle tap from cycling through snap points.

false

Material guidelines

  • Use a sheet for contextual content or actions while retaining the underlying task.
  • Give the sheet a title and a visible dismissal action. Dragging supplements keyboard and button interaction.
  • The default modal sheet uses a white surface, a 20% black backdrop, and a regular 16px title. Theme overrides can adapt these colors.

Material Design 1: Bottom sheets