Skip to main content
All three stack types share the same animation API, but each serves different use cases. Choose based on your requirements.

Comparison

Stack TypeBest ForPerformanceIntegration
Blank StackMost apps. Full control, all features.ExcellentReact Navigation
Native StackWhen you need native screen primitives.ExcellentReact Navigation
Component StackEmbedded flows, isolated from navigation.ExcellentStandalone

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

enableTransitions
boolean
required
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>
  );
}
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

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.

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(),
  }}
/>

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(),
  }}
/>