Why sheet-scope
@gorhom/bottom-sheet handles the hard part of a bottom sheet: gestures, animation and the keyboard. It leaves the bookkeeping to you:
- where each sheet is rendered,
- how a screen opens it,
- how the answer gets back to the screen,
- what happens to the sheet when its screen goes away.
sheet-scope is that bookkeeping. It's JavaScript only, and adds about 7.5 KB (minified) to your bundle.
The same sheet, both ways
Here is a delete confirmation with gorhom alone. The screen renders the modal, keeps a ref to it, and gets the answer through a callback:
function ItemScreen() {
const confirmRef = useRef<BottomSheetModal>(null);
const onDone = (confirmed: boolean) => {
confirmRef.current?.dismiss();
if (confirmed) deleteItem();
};
return (
<>
<Button
title="Delete"
onPress={() => confirmRef.current?.present()}
/>
{/* One of these, and a ref, per sheet */}
<BottomSheetModal ref={confirmRef}>
<ConfirmContent title="Delete this item?" onDone={onDone} />
</BottomSheetModal>
</>
);
}
And the same confirmation with sheet-scope:
function ItemScreen() {
const sheets = useSheets();
const onDelete = async () => {
const confirmed = await sheets.show('confirm', {
title: 'Delete this item?',
});
if (confirmed) deleteItem();
};
return <Button title="Delete" onPress={onDelete} />;
}
The sheet itself is still a gorhom sheet. Its root element, <SheetModal>, takes BottomSheetModal's props, apart from two the library controls, so snap points, backdrops and gestures work the way you already know.
What it adds
Scoped
The problem. A global sheet manager lets you open a sheet from anywhere, but then the sheet belongs to the whole app. If the screen that opened it goes away, the sheet stays up over whatever comes next.
With sheet-scope. Every sheet belongs to a <SheetScope>. When the scope unmounts, for example because its screen was popped, its sheets close. See Scopes.
Lazily mounted
The problem. The usual pattern adds a <BottomSheetModal> and a ref to the screen for every sheet it might show, and loads each sheet's code along with the screen.
With sheet-scope. A sheet isn't rendered, and its module isn't evaluated, until you call show() (or preload(), which loads the module early). Once it has closed, it unmounts.
Typed
The problem. Sheet names passed as strings, loosely typed props, and results handed back through callbacks are all easy to get wrong.
With sheet-scope. Names, props and results are inferred from your sheet components. await sheets.show('confirm', { title }) checks title at compile time and resolves with the sheet's result.
What it doesn't do
- Replace gorhom's inline
BottomSheet. Managed sheets are modals. An inline sheet is still rendered with gorhom directly; see Inline sheets. - Depend on a navigation library. Scopes work under React Navigation or Expo Router, but the library imports neither.
- Persist open sheets across reloads, or deep-link into sheets.
- Own gestures, animation or keyboard handling. That all stays in gorhom.