Migrating to v3
This guide will help you migrate from TrueSheet v2 to v3. Version 3 brings full Fabric (New Architecture) support and several improvements.
Breaking Changes
1. Fabric Architecture Required
Version 3 is built exclusively for React Native's New Architecture (Fabric). The old Paper architecture is no longer supported.
Requirements:
- React Native >= 0.76 (Expo SDK 52+)
- New Architecture enabled (default in RN 0.76+)
If you're on React Native 0.76+, the New Architecture is enabled by default. No additional configuration is needed.
2. Prop Renames
Several props have been renamed for better clarity:
| v2 Prop | v3 Prop |
|---|---|
sizes | detents |
initialIndex | initialDetentIndex |
initialIndexAnimated | initialDetentAnimated |
contentContainerStyle | (removed - no longer needed) |
FooterComponent | footer |
edgeToEdge | edgeToEdgeFullScreen |
onPresent | onDidPresent |
onDismiss | onDidDismiss |
onSizeChange | onDetentChange |
dimmedIndex | dimmedDetentIndex |
Migration:
// ❌ v2
<TrueSheet
sizes={['auto', '50%', '100%']}
initialIndex={1}
initialIndexAnimated={false}
contentContainerStyle={styles.container}
FooterComponent={<MyFooter />}
edgeToEdge
onPresent={(e) => console.log(e.nativeEvent)}
onDismiss={() => console.log('dismissed')}
onSizeChange={(e) => console.log(e.nativeEvent)}
/>
// ✅ v3
<TrueSheet
detents={['auto', 0.5, 1]}
initialDetentIndex={1}
initialDetentAnimated={false}
footer={<MyFooter />}
edgeToEdgeFullScreen
onDidPresent={(e) => console.log(e.nativeEvent)}
onDidDismiss={() => console.log('dismissed')}
onDetentChange={(e) => console.log(e.nativeEvent)}
/>
3. Removed Props
The following props have been removed in v3:
grabberProps (Android)
The grabberProps prop has been removed. The grabber is now rendered natively on both iOS and Android following Material Design 3 specifications. Use the grabber boolean prop to show or hide the native grabber.
Migration:
// ❌ v2
<TrueSheet grabberProps={{ color: 'red', width: 40 }}>
{/* ... */}
</TrueSheet>
// ✅ v3 - grabber is native, use boolean to show/hide
<TrueSheet grabber={true}>
{/* ... */}
</TrueSheet>
scrollRef (iOS)
The scrollRef prop has been removed. Scroll views are now automatically detected on iOS. Use the new scrollable prop to enable automatic ScrollView pinning.
Migration:
// ❌ v2
const scrollViewRef = useRef<ScrollView>(null)
<TrueSheet scrollRef={scrollViewRef}>
<ScrollView ref={scrollViewRef}>{/* ... */}</ScrollView>
</TrueSheet>
// ✅ v3
<TrueSheet scrollable>
<ScrollView>{/* ... */}</ScrollView>
</TrueSheet>
See the Scrolling guide for more information about scrollable.
contentContainerStyle
The contentContainerStyle prop has been removed as it's no longer needed with the new architecture. Style your content directly instead.
4. Detent Value Changes
Detent values now use fractional values (0-1) instead of percentage strings.
Migration:
// ❌ v2
<TrueSheet detents={["50%", "80%", "100%"]} />
// ✅ v3
<TrueSheet detents={[0.5, 0.8, 1]} />
The "auto" detent still works the same way!
5. Native Bottom Safe Area Handling
Both iOS and Android now handle bottom safe area insets natively for the sheet. This means the sheet height includes the safe area automatically, providing consistent behavior across all devices.
What this means:
- The
position,index, anddetentvalues are calculated purely from the actual sheet position relative to screen height - A sheet at detent
0.5will be taller than0.5 * screenHeightbecause the system adds the bottom safe area internally
Footer with auto detent:
The footer is pinned to the bottom edge of the sheet. When using an auto detent, the sheet height includes the safe area, so your footer needs to extend into it:
import { Platform } from 'react-native';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
const isIPad = Platform.OS === 'ios' && Platform.isPad;
const MyFooter = () => {
const insets = useSafeAreaInsets();
const bottomInset = isIPad ? 0 : insets.bottom;
return (
<View style={{ paddingBottom: bottomInset, backgroundColor: '#333' }}>
<View style={{ height: 60, justifyContent: 'center', alignItems: 'center' }}>
<Text>Footer Content</Text>
</View>
</View>
);
};
// Usage
<TrueSheet detents={['auto']} footer={<MyFooter />}>
{/* content */}
</TrueSheet>
This ensures the footer background extends into the safe area while keeping the content above the home indicator.
On iPad, the sheet is displayed as a floating modal, so bottom padding is not needed.
See the Footer guide for more details.
6. Background and Blur Behavior Changes
backgroundColor Default Value
The backgroundColor prop no longer defaults to "white". It now uses the system default when not provided, which matches the platform's native sheet background.
Migration:
// ❌ v2 - backgroundColor defaults to white
<TrueSheet />
// ✅ v3 - backgroundColor uses system default (transparent on iOS)
// If you want white background, explicitly set it:
<TrueSheet backgroundColor="white" />
blurTint No Longer Overrides backgroundColor
In v2, setting blurTint would completely override the backgroundColor. In v3, blurTint now applies a blur effect over the backgroundColor, allowing you to combine both for richer visual effects.
Migration:
// ❌ v2 - blurTint overrides backgroundColor (backgroundColor is ignored)
<TrueSheet backgroundColor="red" blurTint="light" />
// ✅ v3 - blurTint applies over backgroundColor (red + light blur)
<TrueSheet backgroundColor="red" blurTint="light" />
// If you want only blur without background color:
<TrueSheet blurTint="light" />
New Features in v3
1. Automatic Edge-to-Edge Detection (Android)
TrueSheet now automatically detects and adapts to Android's edge-to-edge display mode. By default, the sheet respects the status bar when fully expanded.
For a true full-screen experience where the sheet extends behind the status bar, use the new edgeToEdgeFullScreen prop:
<TrueSheet edgeToEdgeFullScreen />
This feature is Android-only and requires React Native 0.81+ with edgeToEdgeEnabled=true in your android/gradle.properties.
See the Edge-to-Edge guide for details.
2. Enhanced Event System
Version 3 introduces new lifecycle and interaction events for better control:
New Events:
onMount- Called when the sheet's content is mounted and readyonWillPresent- Called when the sheet is about to be presentedonDidPresent- Called when the sheet has been presentedonWillDismiss- Called when the sheet is about to be dismissedonDidDismiss- Called when the sheet has been dismissedonDragBegin- Called when the sheet begins draggingonDragChange- Called continuously while the sheet is being draggedonDragEnd- Called when sheet dragging has endedonPositionChange- Called continuously when the sheet's position changesonWillFocus- Called when the sheet is about to regain focus after a sheet on top begins dismissingonDidFocus- Called when the sheet has regained focus after a sheet on top is dismissedonWillBlur- Called when the sheet is about to lose focus because another sheet is about to be presented on toponDidBlur- Called when the sheet has lost focus because another sheet is presented on top
These events provide more granular control over the sheet's lifecycle and user interactions.
See the Lifecycle Events documentation for detailed usage and examples.
3. iPad Support with Page Sizing
Version 3 now includes first-class iPad support with the new pageSizing prop. When enabled (default), the sheet uses a large page sheet for better readability on iPad. When disabled, the sheet uses a centered form sheet.
<TrueSheet pageSizing />
This feature is iOS 17+ only and defaults to true. Set to false to use a centered form sheet presentation on iPad.
4. Improved Performance
- Faster rendering with Fabric architecture
- Built-in lazy loading by default
- Smoother animations and transitions
5. First-Class Reanimated v4 Support
Version 3 introduces dedicated components for seamless integration with react-native-reanimated v4:
New Components:
ReanimatedTrueSheetProvider- Context provider for managing shared valuesReanimatedTrueSheet- Drop-in replacement forTrueSheetwith automatic position trackinguseReanimatedTrueSheet- Hook to access the sheet's animated position
Example:
import {
ReanimatedTrueSheetProvider,
ReanimatedTrueSheet,
useReanimatedTrueSheet,
} from '@lodev09/react-native-true-sheet/reanimated'
import Animated, { useAnimatedStyle, interpolate, Extrapolation } from 'react-native-reanimated'
// 1. Wrap your app with the provider
const App = () => (
<ReanimatedTrueSheetProvider>
<YourApp />
</ReanimatedTrueSheetProvider>
)
// 2. Use ReanimatedTrueSheet instead of TrueSheet
const MySheet = () => (
<ReanimatedTrueSheet detents={[0.3, 0.6, 1]} initialDetentIndex={1}>
<Text>Sheet Content</Text>
</ReanimatedTrueSheet>
)
// 3. Access the sheet's position for custom animations
const MyAnimatedComponent = () => {
const { animatedPosition, animatedIndex } = useReanimatedTrueSheet()
const positionStyle = useAnimatedStyle(() => ({
transform: [{ translateY: -animatedPosition.value }],
}))
// Example: Fade in as sheet expands from index 0 to 1
const opacityStyle = useAnimatedStyle(() => ({
opacity: interpolate(animatedIndex.value, [0, 1], [0, 1], Extrapolation.CLAMP)
}))
return (
<Animated.View style={[opacityStyle, positionStyle]}>
<Text>This moves with the sheet</Text>
</Animated.View>
)
}
The onPositionChange event now runs on the UI thread (worklet) when using ReanimatedTrueSheet. Make sure to add the 'worklet' directive to your handler if you override it.
See the Reanimated Integration guide for complete examples.
6. Native Header Support
Version 3 introduces a new header prop for adding fixed headers above your scrollable content. The header is rendered as a native view and is properly accounted for in layout calculations.
<TrueSheet header={<MyHeader />}>
<FlatList data={items} renderItem={renderItem} />
</TrueSheet>
This ensures proper scrolling behavior on both iOS and Android when using ScrollView or FlatList with a header.
See the Header guide for more details.
7. Draggable Control
Version 3 introduces the draggable prop to disable user dragging entirely. When set to false, the sheet becomes static and can only be resized programmatically using the resize method.
<TrueSheet draggable={false} detents={[0.5, 1]}>
<Button title="Expand" onPress={() => sheet.current?.resize(1)} />
</TrueSheet>
When draggable is false, the grabber is automatically hidden.
See the Configuration reference for more details.
8. Blur Intensity and Interaction Control
Version 3 introduces new props for fine-grained control over the blur effect on iOS:
blurIntensity- Control the blur intensity (0-100)blurInteraction- Enable/disable user interaction on the blur view
<TrueSheet
blurTint="light"
blurOptions={{
intensity: 80,
interaction: false,
}}
/>
See the Configuration reference for more details.
9. React Native Screens Integration
Version 3 works seamlessly with React Navigation! You can navigate to other screens from within a sheet and the sheet will remain visible in the background when presenting modals on top.
This requires changes to react-native-screens. There is a pending PR that adds support for this. In the meantime, you can apply the patch from the example app.
const sheet = useRef<TrueSheet>(null)
const navigate = () => {
// Navigate directly - no need to dismiss first!
navigation.navigate('SomeScreen')
}
return (
<TrueSheet ref={sheet}>
<Button onPress={navigate} title="Navigate to SomeScreen" />
</TrueSheet>
)
See the React Navigation guide for more details.
Step-by-Step Migration
-
Enable New Architecture in your React Native app (see above)
-
Update the package:
yarn add @lodev09/react-native-true-sheet@^3.0.0npm install @lodev09/react-native-true-sheet@^3.0.0 -
Clean and reinstall iOS dependencies:
cd ios
rm -rf Pods Podfile.lock build
pod install
cd .. -
Clean Android build:
cd android
./gradlew clean
cd .. -
Update your code:
- Replace
sizeswithdetents - Replace
initialIndexwithinitialDetentIndex - Replace
initialIndexAnimatedwithinitialDetentAnimated - Replace
FooterComponentwithfooter - Replace
edgeToEdgewithedgeToEdgeFullScreen - Replace
onPresentwithonDidPresent - Replace
onDismisswithonDidDismiss - Replace
onSizeChangewithonDetentChange - Replace
dimmedIndexwithdimmedDetentIndex - Remove
scrollRefprop (iOS - now auto-detected) - Remove
contentContainerStyleprop (no longer needed) - Remove
grabberPropsprop (grabber is now native on both platforms) - Change detent percentage strings to fractional numbers (e.g.,
"50%"→0.5)
- Replace
-
Test your app:
npx react-native run-ios
npx react-native run-android
Need Help?
If you encounter any issues during migration:
- Check the Troubleshooting guide
- Open an issue on GitHub
- Review the example app for reference implementations