Detents and index
Detents are the points to which the sheet snaps. Each detent is either a number
(a fixed height in pixels) or 'content' (the sheet’s content height, capped by
the available screen height). The default detents are [0, 'content']. Pass
detents in ascending order, from shortest to tallest. Fixed detents can be
taller than the measured content height, so [0, 'content', 600] lets a compact
content-sized sheet expand to a larger surface.
Sheet children are laid out in a flex container. For a full-height
sheet, apply flex: 1 to your content and use the 'content' detent.
surface is sized by the library, so flex: 1 only ever belongs on your
content, never on the surface:
<BottomSheet
// `detents` defaults to `[0, 'content']`.
index={index}
onIndexChange={setIndex}
surface={
<View style={[StyleSheet.absoluteFill, { backgroundColor: 'white' }]} />
}
>
<View style={{ flex: 1 }}>{/* Full-height sheet content. */}</View>
</BottomSheet>
By default, full-height detents are capped below the status bar. Set
extendUnderStatusBar when the sheet should be allowed to occupy the full
screen height:
<BottomSheet // Or `ModalBottomSheet`.
extendUnderStatusBar
index={index}
onIndexChange={setIndex}
surface={/* ... */}
>
<View style={{ flex: 1 }}>{/* Full-screen sheet content. */}</View>
</BottomSheet>
Index events
The index prop is a zero-based index into the detents array.
onIndexChange and onSettle have different responsibilities:
onIndexChangefires when a user-triggered snap is initiated: the moment a drag commits to a detent, before the animation settles. It does not fire for programmaticindexchanges; you already know when you make those. Treat it as the signal to update your controlledindexstate.onSettlefires when the sheet finishes snapping to a detent, regardless of whether that snap was user-triggered or programmatic. It is the signal for the end of any movement. Use it for observability or side effects (analytics, reacting to collapse, etc.), not for updating the controlledindexstate.
const [index, setIndex] = useState(0);
<BottomSheet // Or `ModalBottomSheet`.
detents={[0, 300, 'content']} // Collapsed, 300 px, content height.
index={index}
onIndexChange={setIndex} // Fires when a drag commits; keep state in sync.
surface={/* ... */}
onSettle={(nextIndex) => {
if (nextIndex === 0) console.log('Sheet finished collapsing.');
}}
>
{/* ... */}
</BottomSheet>
Detents can also change over time. When you update detents, the sheet keeps
the current index and animates to the updated detent height when needed.
Programmatic-only detents
If you want a detent to be reachable only via code (not by dragging), use the
object form or the programmatic helper. Programmatic detents are excluded from
drag snapping but can still be targeted via index updates. If the closed
detent is programmatic-only, tapping the scrim does not dismiss the sheet.
<BottomSheet
detents={[0, programmatic(300), 'content']}
index={index}
onIndexChange={setIndex}
surface={/* ... */}
onSettle={(nextIndex) => {
console.log(`Settled at ${nextIndex}.`);
}}
>
{/* ... */}
</BottomSheet>