Modal sheets
ModalBottomSheet renders above other content with an optional scrim
(transparent by default).
const [index, setIndex] = useState(0);
const insets = useSafeAreaInsets();
<ModalBottomSheet
index={index}
onIndexChange={setIndex}
surface={
<View style={[StyleSheet.absoluteFill, { backgroundColor: 'white' }]} />
}
>
<View style={{ padding: 16, paddingBottom: insets.bottom + 16 }}>
<Text>Sheet content</Text>
</View>
</ModalBottomSheet>
Scrim
Tapping the scrim collapses the sheet to the closed detent. Use scrimColor to
customize the scrim color:
<ModalBottomSheet
index={index}
onIndexChange={setIndex}
surface={/* ... */}
scrimColor="rgba(0, 0, 0, 0.3)"
>
{/* ... */}
</ModalBottomSheet>
By default, the scrim fades in as the sheet opens and then holds at full
opacity, so detents above the first share the same scrim. Use scrimOpacities
to control the opacity at each detent: It takes one value in 0–1 per detent,
indexed to match detents, and interpolates linearly as the sheet is dragged
between them. A shorter array reuses its last value for any
remaining detents.
The default maps each detent to 0 when it is closed and 1 otherwise, so the scrim is transparent at any closed detent and fully opaque at every open one.
To keep the scrim deepening across every detent, pass one value per detent:
<ModalBottomSheet
index={index}
onIndexChange={setIndex}
detents={[0, 300, 'content']}
scrimColor="rgba(0, 0, 0, 0.3)"
scrimOpacities={[0, 0.5, 1]}
surface={/* ... */}
>
{/* ... */}
</ModalBottomSheet>
Native overlay
By default ModalBottomSheet renders through BottomSheetProvider’s portal.
That portal lives in your React tree, so a sheet opened from a screen presented
as a native modal (for example, a React Navigation native-stack screen with
presentation: 'modal') is confined to that screen and cannot cover it.
Set nativeOverlay to present the sheet in a native overlay above
everything—including native modal screens—so it always covers the full window.
On iOS, a UIWindow-attached container is used; on Android, a full-screen,
edge-to-edge, transparent dialog.
<ModalBottomSheet
nativeOverlay
index={index}
onIndexChange={setIndex}
surface={/* ... */}
>
{/* ... */}
</ModalBottomSheet>