Skip to content

Scrollable

A hundred rows, with scroll: true on the top snap. Drag the list up and the sheet rises to that snap, then the same movement scrolls the list. Scroll back to the top and the sheet drags again. You never lift your finger.

tsx
import { useState } from "react";
import { Sheet } from "snap-bottom-sheet/react";

// `frame` is the box this demo runs in: it is passed to
// `Sheet.Portal container`, so the sheet stays inside the box instead of
// covering the page. Leave `container` out and the sheet portals to <body>.
function Scrollable({ frame }: { frame: HTMLElement }) {
  const [open, setOpen] = useState(false);

  return (
    <div className="demo-page">
      <button
        type="button"
        className="demo-button"
        onClick={() => setOpen(true)}
      >
        Open the sheet
      </button>

      <Sheet
        open={open}
        onOpenChange={setOpen}
        snapPoints={[0.4, { value: 1, scroll: true }]}
        defaultSnapIndex={1}
      >
        <Sheet.Portal container={frame}>
          <Sheet.Overlay className="demo-overlay" />
          <Sheet.Content className="demo-sheet">
            <Sheet.Handle className="demo-handle" />
            <Sheet.Header className="demo-header">
              <Sheet.Title className="demo-title">
                Scroll, then drag
              </Sheet.Title>
              <Sheet.Description className="demo-description">
                Drag the list up: the sheet rises, then the same movement
                scrolls. Scroll back to the top and it drags again.
              </Sheet.Description>
            </Sheet.Header>
            <Sheet.Body className="demo-body">
              <ul className="demo-list">
                {Array.from({ length: 100 }, (_, i) => i + 1).map((n) => (
                  <li key={n}>Item {n}</li>
                ))}
              </ul>
            </Sheet.Body>
          </Sheet.Content>
        </Sheet.Portal>
      </Sheet>
    </div>
  );
}

Released under the MIT License.