Scrolling Content
Scrolling content within the sheet requires some special configuration on both iOS and Android. Let's admit, some things just don't work out of the box.
Follow the guide below so you can scroll your content using TrueSheet.
How?
Enable the scrollable prop on your TrueSheet to properly handle scrolling within the sheet.
const App = () => {
const sheet = useRef<TrueSheet>(null)
return (
<TrueSheet ref={sheet} scrollable>
<View>{/* Header content */}</View>
<ScrollView>
<View />
</ScrollView>
</TrueSheet>
)
}
By default, scrollable is false. Set it to true to enable automatic ScrollView fitting.
The auto detent is not supported with scrollable. Use fixed fractional detents (e.g., 0.5, 0.8, 1) instead.
iOS
On iOS, scrollable automatically pins scroll views (including ScrollView and FlatList) to fit within the sheet's available space. The ScrollView's top edge will be pinned below any top sibling views, and its left, right, and bottom edges will be pinned to the container.
The scroll view detection supports up to 2 levels deep in the view hierarchy, so you can wrap your ScrollView or FlatList in a container View if needed:
<TrueSheet scrollable>
<View style={{ flex: 1 }}>
<View>{/* Header content */}</View>
<FlatList data={data} renderItem={renderItem} />
</View>
</TrueSheet>
Android
On Android, scrollable ensures the scroll view fills the available sheet space. Nested scrolling is automatically enabled on the detected scroll view, so you don't need to set nestedScrollEnabled manually.
When scrollable is enabled, any nestedScrollEnabled prop on your ScrollView or FlatList is ignored — TrueSheet manages nested scrolling internally. RefreshControl is also fully supported.
Preventing Scroll-to-Expand
By default, scrolling content to the top edge expands the sheet to the next detent. To disable this (e.g., for YouTube-style comments sheets where only the grabber should expand the sheet), set scrollingExpandsSheet to false:
<TrueSheet
scrollable
scrollableOptions={{ scrollingExpandsSheet: false }}
detents={[0.5, 1]}
>
<ScrollView>
<View />
</ScrollView>
</TrueSheet>
On iOS, this uses prefersScrollingExpandsWhenScrolledToEdge. On Android, a custom BottomSheetBehavior selectively blocks scroll-driven expansion while preserving normal scrolling, fling deceleration, and grabber-based dragging.
Scroll Edge Effect
On iOS 26+, you can apply a blur/gradient edge effect on the header and footer when content scrolls behind them. This uses UIScrollEdgeElementContainerInteraction under the hood.
Set topScrollEdgeEffect and/or bottomScrollEdgeEffect in scrollableOptions to enable it:
<TrueSheet
scrollable
scrollableOptions={{
topScrollEdgeEffect: 'automatic',
bottomScrollEdgeEffect: 'soft',
}}
header={<Header />}
footer={<Footer />}
>
<ScrollView>
<View />
</ScrollView>
</TrueSheet>
Available values: 'automatic', 'hard', 'soft', or 'hidden' (default). See ScrollEdgeEffect.
This also controls the scroll view's own edge effects (UIScrollEdgeEffect). topScrollEdgeEffect applies to both the header interaction and the scroll view's top edge, and bottomScrollEdgeEffect applies to both the footer interaction and the scroll view's bottom edge.
This feature requires iOS 26 or later. On older versions, these options are ignored.
Using scrollToEnd
When using scrollable, the native scroll view frame is modified to fit within the sheet container. This can cause scrollToEnd() on FlatList or ScrollView to not work as expected because React Native's JS-side scroll metrics may not reflect the actual native dimensions.
Workaround: Use scrollToOffset with a large value instead:
const scrollRef = useRef<FlatList>(null);
const scrollToEnd = () => {
// Large offset will be clamped to max scrollable position
scrollRef.current?.scrollToOffset({ offset: 99999, animated: true });
};
return (
<TrueSheet scrollable onDidPresent={scrollToEnd}>
<FlatList ref={scrollRef} data={data} renderItem={renderItem} />
</TrueSheet>
);