All three stack types share the same animation API, but each serves different use cases. Choose based on your requirements.
Comparison
Stack Type Best For Performance Integration Blank Stack Most apps. Full control, all features. Excellent React Navigation Native Stack When you need native screen primitives. Excellent React Navigation Component Stack Embedded flows, isolated from navigation. Excellent Standalone
Blank Stack
Recommended for most apps. The Blank Stack is the default choice and provides the best developer experience with full feature support.
The Blank Stack uses react-native-screens for native screen containers, with animations powered by Reanimated worklets running on the UI thread (not the JS thread).
Installation
import { createBlankStackNavigator } from "react-native-screen-transitions/blank-stack" ;
const Stack = createBlankStackNavigator ();
Basic Example
import { createBlankStackNavigator } from "react-native-screen-transitions/blank-stack" ;
import Transition from "react-native-screen-transitions" ;
const Stack = createBlankStackNavigator ();
function App () {
return (
< Stack.Navigator >
< Stack.Screen name = "Home" component = { HomeScreen } />
< Stack.Screen
name = "Detail"
component = { DetailScreen }
options = { {
... Transition . Presets . SlideFromBottom (),
} }
/>
</ Stack.Navigator >
);
}
With Expo Router
// app/_layout.tsx
import { withLayoutContext } from "expo-router" ;
import {
createBlankStackNavigator ,
type BlankStackNavigationOptions ,
} from "react-native-screen-transitions/blank-stack" ;
const { Navigator } = createBlankStackNavigator ();
export const Stack = withLayoutContext <
BlankStackNavigationOptions ,
typeof Navigator
> ( Navigator );
export default function RootLayout () {
return (
< Stack >
< Stack.Screen name = "index" />
< Stack.Screen
name = "details"
options = { {
... Transition . Presets . SlideFromBottom (),
} }
/>
</ Stack >
);
}
Features
Full gesture support with all directions
Snap points for bottom sheets and drawers
Shared element transitions (Bounds API)
Overlay support
Custom interpolators
High refresh rate support
No touch event delays
Clean lifecycle events
When to Use
Full Control You need complete control over animations, gestures, and transitions without platform limitations.
Shared Elements Your app requires shared element transitions between screens (Instagram, Apple Music styles).
Bottom Sheets You’re building bottom sheets, drawers, or any screen with snap points.
Custom Animations You want gesture-driven, elastic, or complex custom animations.
TypeScript Types
import type {
BlankStackNavigationOptions ,
BlankStackNavigationProp ,
BlankStackScreenProps ,
BlankStackNavigatorProps ,
} from "react-native-screen-transitions/blank-stack" ;
Native Stack
Extends @react-navigation/native-stack with custom transition support. Uses native screen management with custom animation layers.
Installation
import { createNativeStackNavigator } from "react-native-screen-transitions/native-stack" ;
const Stack = createNativeStackNavigator ();
Basic Example
import { createNativeStackNavigator } from "react-native-screen-transitions/native-stack" ;
import Transition from "react-native-screen-transitions" ;
const Stack = createNativeStackNavigator ();
function App () {
return (
< Stack.Navigator >
< Stack.Screen name = "Home" component = { HomeScreen } />
< Stack.Screen
name = "Detail"
component = { DetailScreen }
options = { {
enableTransitions: true , // Required for custom transitions
... Transition . Presets . SlideFromBottom (),
} }
/>
</ Stack.Navigator >
);
}
You must set enableTransitions: true on any screen that uses custom transitions. Without this, screens will use platform-native transitions.
Requirements
Must be set to true on screens that use custom transitions. Platform-native transitions are used when this is false or omitted. < Stack.Screen
name = "Detail"
options = { {
enableTransitions: true ,
... Transition . Presets . SlideFromBottom (),
} }
/>
Features
All features from Blank Stack, plus:
Native screen stack management
Integration with existing @react-navigation/native-stack apps
Platform-native headers and navigation bar
Native swipe-back gesture (iOS) when enableTransitions: false
Caveats
The Native Stack uses transparent modal presentation to intercept transitions. This has trade-offs:
Delayed touch events - Exiting screens may have briefly delayed touch response due to modal layering.
beforeRemove listeners - Relies on navigation lifecycle events which can be affected by rapid navigation sequences.
Rapid navigation - Some edge cases with very fast navigation sequences may cause unexpected behavior.
When to Use
Existing Native Stack Apps You have an existing app using @react-navigation/native-stack and want to add custom transitions to specific screens.
Mixed Navigation You want some screens with platform-native transitions and others with custom animations.
Native Headers You need platform-native navigation bars and headers but want custom screen transitions.
For new projects, Blank Stack is recommended unless you specifically need native screen stack management.
TypeScript Types
import type {
NativeStackNavigationOptions ,
NativeStackNavigationProp ,
NativeStackScreenProps ,
NativeStackNavigatorProps ,
} from "react-native-screen-transitions/native-stack" ;
Component Stack (Experimental)
This API is experimental and may change based on community feedback.
Standalone navigator not connected to React Navigation. Ideal for embedded flows that shouldn’t affect the main navigation state.
Installation
import { createComponentStackNavigator } from "react-native-screen-transitions/component-stack" ;
const Stack = createComponentStackNavigator ();
Basic Example
import { createComponentStackNavigator } from "react-native-screen-transitions/component-stack" ;
import Transition from "react-native-screen-transitions" ;
const OnboardingStack = createComponentStackNavigator ();
function OnboardingFlow () {
return (
< OnboardingStack.Navigator initialRouteName = "step1" >
< OnboardingStack.Screen
name = "step1"
component = { Step1 }
options = { {
... Transition . Presets . SlideFromBottom (),
} }
/>
< OnboardingStack.Screen
name = "step2"
component = { Step2 }
options = { {
... Transition . Presets . SlideFromBottom (),
} }
/>
< OnboardingStack.Screen
name = "step3"
component = { Step3 }
options = { {
... Transition . Presets . SlideFromBottom (),
} }
/>
</ OnboardingStack.Navigator >
);
}
Navigation API
Component Stack provides its own navigation methods:
import { useNavigation } from "react-native-screen-transitions/component-stack" ;
function Step1 () {
const navigation = useNavigation ();
return (
< Button
title = "Next"
onPress = { () => navigation . navigate ( "step2" ) }
/>
);
}
Features
All animation features (gestures, snap points, shared elements)
Isolated navigation state
Touch pass-through by default
Embeddable in any screen or component
No deep linking integration
Characteristics
Isolated State Navigation state is completely isolated from parent navigators. Changes don’t affect the main app navigation.
No Deep Linking Routes aren’t part of your URL structure. Use Blank or Native Stack if you need deep linking.
Touch Pass-through Uses pointerEvents="box-none" by default, allowing touches to pass through to content behind. Override with backdropBehavior.
When to Use
Onboarding Flows Multi-step onboarding or tutorial flows that shouldn’t affect main navigation.
In-App Wizards Step-by-step forms or wizards embedded within a screen.
Modal Flows Self-contained modal flows that need their own navigation stack.
Isolated Features Features that need screen-based navigation but are isolated from the main app navigation.
Limitations
No integration with React Navigation’s linking/deep linking
No integration with React Navigation DevTools
Navigation state not accessible via useNavigationState from React Navigation
Cannot navigate to screens outside the Component Stack
Example: Embedded Onboarding
// Main app screen
import { createComponentStackNavigator } from "react-native-screen-transitions/component-stack" ;
import Transition from "react-native-screen-transitions" ;
const OnboardingStack = createComponentStackNavigator ();
function WelcomeScreen () {
const [ showOnboarding , setShowOnboarding ] = useState ( true );
if ( ! showOnboarding ) {
return < MainContent /> ;
}
return (
< View style = { { flex: 1 } } >
< MainContent />
< View style = { StyleSheet . absoluteFill } >
< OnboardingStack.Navigator initialRouteName = "intro" >
< OnboardingStack.Screen
name = "intro"
component = { IntroScreen }
options = { {
... Transition . Presets . SlideFromBottom (),
} }
/>
< OnboardingStack.Screen
name = "features"
component = { FeaturesScreen }
options = { {
... Transition . Presets . SlideFromBottom (),
} }
/>
</ OnboardingStack.Navigator >
</ View >
</ View >
);
}
TypeScript Types
import type {
ComponentStackNavigationOptions ,
ComponentStackNavigationProp ,
ComponentStackScreenProps ,
ComponentStackNavigatorProps ,
} from "react-native-screen-transitions/component-stack" ;
Choosing the Right Stack
Blank Stack
Native Stack
Component Stack
Choose Blank Stack when:
Building a new app from scratch
Need full control over animations
Want shared element transitions
Building bottom sheets or drawers
Need the best developer experience
This is the recommended choice for most apps. Choose Native Stack when:
Migrating from @react-navigation/native-stack
Need native headers and navigation bars
Want platform-native transitions on some screens
Need to gradually adopt custom transitions
Good for gradual migration from native-stack. Choose Component Stack when:
Building isolated, embeddable flows
Creating onboarding or tutorial sequences
Need navigation that doesn’t affect main app state
Building self-contained modal flows
Experimental - use with caution in production.
Migration Guide
From @react-navigation/native-stack
// Before
import { createNativeStackNavigator } from '@react-navigation/native-stack' ;
const Stack = createNativeStackNavigator ();
// After
import { createBlankStackNavigator } from 'react-native-screen-transitions/blank-stack' ;
import Transition from 'react-native-screen-transitions' ;
const Stack = createBlankStackNavigator ();
// Add custom transitions to any screen
< Stack.Screen
name = "Detail"
options = { {
... Transition . Presets . SlideFromBottom (),
} }
/>
// Before
import { createNativeStackNavigator } from '@react-navigation/native-stack' ;
const Stack = createNativeStackNavigator ();
// After
import { createNativeStackNavigator } from 'react-native-screen-transitions/native-stack' ;
import Transition from 'react-native-screen-transitions' ;
const Stack = createNativeStackNavigator ();
// Keep native transitions on most screens
< Stack.Screen name = "Home" component = { HomeScreen } />
// Add custom transitions only where needed
< Stack.Screen
name = "Detail"
options = { {
enableTransitions: true ,
... Transition . Presets . SlideFromBottom (),
} }
/>
From @react-navigation/stack
// Before
import { createStackNavigator } from '@react-navigation/stack' ;
const Stack = createStackNavigator ();
// After
import { createBlankStackNavigator } from 'react-native-screen-transitions/blank-stack' ;
import Transition from 'react-native-screen-transitions' ;
const Stack = createBlankStackNavigator ();
// Use presets or custom interpolators
< Stack.Screen
name = "Detail"
options = { {
... Transition . Presets . SlideFromBottom (),
} }
/>