Skip to main content

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+)
tip

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 Propv3 Prop
sizesdetents
initialIndexinitialDetentIndex
initialIndexAnimatedinitialDetentAnimated
contentContainerStyle(removed - no longer needed)
FooterComponentfooter
edgeToEdgeedgeToEdgeFullScreen
onPresentonDidPresent
onDismissonDidDismiss
onSizeChangeonDetentChange
dimmedIndexdimmedDetentIndex

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>
tip

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]} />
tip

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, and detent values are calculated purely from the actual sheet position relative to screen height
  • A sheet at detent 0.5 will be taller than 0.5 * screenHeight because 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.

note

On iPad, the sheet is displayed as a floating modal, so bottom padding is not needed.

tip

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 />
info

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 ready
  • onWillPresent - Called when the sheet is about to be presented
  • onDidPresent - Called when the sheet has been presented
  • onWillDismiss - Called when the sheet is about to be dismissed
  • onDidDismiss - Called when the sheet has been dismissed
  • onDragBegin - Called when the sheet begins dragging
  • onDragChange - Called continuously while the sheet is being dragged
  • onDragEnd - Called when sheet dragging has ended
  • onPositionChange - Called continuously when the sheet's position changes
  • onWillFocus - Called when the sheet is about to regain focus after a sheet on top begins dismissing
  • onDidFocus - Called when the sheet has regained focus after a sheet on top is dismissed
  • onWillBlur - Called when the sheet is about to lose focus because another sheet is about to be presented on top
  • onDidBlur - 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 />
info

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 values
  • ReanimatedTrueSheet - Drop-in replacement for TrueSheet with automatic position tracking
  • useReanimatedTrueSheet - 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>
)
}
tip

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>
info

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.

note

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

  1. Enable New Architecture in your React Native app (see above)

  2. Update the package:

    yarn add @lodev09/react-native-true-sheet@^3.0.0
    npm install @lodev09/react-native-true-sheet@^3.0.0
  3. Clean and reinstall iOS dependencies:

    cd ios
    rm -rf Pods Podfile.lock build
    pod install
    cd ..
  4. Clean Android build:

    cd android
    ./gradlew clean
    cd ..
  5. Update your code:

    • Replace sizes with detents
    • Replace initialIndex with initialDetentIndex
    • Replace initialIndexAnimated with initialDetentAnimated
    • Replace FooterComponent with footer
    • Replace edgeToEdge with edgeToEdgeFullScreen
    • Replace onPresent with onDidPresent
    • Replace onDismiss with onDidDismiss
    • Replace onSizeChange with onDetentChange
    • Replace dimmedIndex with dimmedDetentIndex
    • Remove scrollRef prop (iOS - now auto-detected)
    • Remove contentContainerStyle prop (no longer needed)
    • Remove grabberProps prop (grabber is now native on both platforms)
    • Change detent percentage strings to fractional numbers (e.g., "50%"0.5)
  6. Test your app:

    npx react-native run-ios
    npx react-native run-android

Need Help?

If you encounter any issues during migration: