Controlled
The buttons outside the sheet set open and activeSnapIndex, and the ref closes the sheet. Drag the sheet yourself and the buttons stay in sync.
tsx
import { useRef, useState } from "react";
import type { SheetHandle } from "snap-bottom-sheet/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 Controlled({ frame }: { frame: HTMLElement }) {
const [open, setOpen] = useState(false);
const [index, setIndex] = useState(0);
const handle = useRef<SheetHandle>(null);
return (
<div className="demo-page">
<div className="demo-row">
<button
type="button"
className="demo-button"
onClick={() => setOpen(true)}
>
Open
</button>
<button
type="button"
className="demo-button"
onClick={() => setIndex(0)}
>
Snap 0
</button>
<button
type="button"
className="demo-button"
onClick={() => setIndex(1)}
>
Snap 1
</button>
<button
type="button"
className="demo-button"
onClick={() => void handle.current?.close()}
>
Close via ref
</button>
</div>
<p className="demo-readout">
open {String(open)} · index {index}
</p>
<Sheet
ref={handle}
open={open}
onOpenChange={setOpen}
activeSnapIndex={index}
onSnapIndexChange={setIndex}
snapPoints={[0.35, 0.75]}
>
<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">
Driven from outside
</Sheet.Title>
</Sheet.Header>
<Sheet.Body className="demo-body">
<p>Drag it and the buttons above stay in sync.</p>
</Sheet.Body>
</Sheet.Content>
</Sheet.Portal>
</Sheet>
</div>
);
}