Skip to main content

Overview

All three stack types share the same animation API, but each has specific trade-offs. Understanding these limitations helps you choose the right stack for your use case.

Stack Type Comparison

StackBest For
Blank StackMost apps. Full control, all features.
Native StackWhen you need native screen primitives.
Component StackEmbedded flows, isolated from React Navigation. (Experimental)

Blank Stack

This is the recommended stack type for most applications.

How It Works

Uses react-native-screens for native screen containers, with animations powered by Reanimated worklets running on the UI thread (not the JS thread).
import { createBlankStackNavigator } from "react-native-screen-transitions/blank-stack";

const Stack = createBlankStackNavigator();

Advantages

  • Full feature support – All gestures, snap points, shared elements work seamlessly
  • Native performance – Animations run on UI thread via Reanimated
  • Reliable touch handling – No modal-related touch delays
  • React Navigation integration – Full compatibility with React Navigation ecosystem

Limitations

Blank Stack has minimal limitations. It’s the recommended choice for most apps.
  • Slightly less “native” – Uses custom animations instead of platform defaults
  • Bundle size – Includes Reanimated (though most RN apps already use it)

When to Use

  • You need custom animations, gestures, or shared elements
  • You want reliable touch handling and gesture coordination
  • You’re building any app that needs more than basic push/pop transitions

Native Stack

How It Works

Extends @react-navigation/native-stack and uses transparent modal presentation to intercept transitions. Requires enableTransitions: true on screens with custom animations.
import { createNativeStackNavigator } from "react-native-screen-transitions/native-stack";

const Stack = createNativeStackNavigator();

<Stack.Screen
  name="Detail"
  options={{
    enableTransitions: true,
    ...Transition.Presets.SlideFromBottom(),
  }}
/>

Advantages

  • Native screen primitives – Uses platform-native screen containers
  • Fallback to native transitions – Screens without enableTransitions: true use platform defaults
  • React Navigation compatibility – Extends official native-stack navigator

Limitations

Exiting screens may have briefly delayed touch response due to modal presentation layer.Impact: Users might notice a ~50-100ms delay when tapping on the previous screen during/after a transition.Workaround: Use Blank Stack if this affects your UX.
The Native Stack relies on React Navigation lifecycle events for beforeRemove listeners.Impact: If you have complex beforeRemove logic (e.g., “unsaved changes” prompts), behavior may differ slightly from standard native-stack.Workaround: Test your beforeRemove logic thoroughly, or use Blank Stack.
Very fast navigation sequences (multiple pushes/pops within milliseconds) may occasionally cause unexpected behavior.Impact: Rare edge cases in automated tests or stress testing. Unlikely to affect real users.Workaround: Add small delays between rapid navigations, or use Blank Stack.
You must explicitly enable custom transitions on each screen:
<Stack.Screen
  name="Detail"
  options={{
    enableTransitions: true, // Required!
    ...Transition.Presets.SlideFromBottom(),
  }}
/>
Impact: Extra configuration step. Easy to forget.Benefit: Screens without the flag use platform-native transitions automatically.

When to Use

  • You need native screen primitives for specific platform features
  • You want to mix custom and native transitions in the same stack
  • Touch delay and edge cases don’t affect your app’s UX

When to Avoid

  • Touch responsiveness is critical (e.g., games, creative tools)
  • You have complex beforeRemove logic
  • You’re using rapid programmatic navigation

Component Stack (Experimental)

This API is experimental and may change based on community feedback.

How It Works

Standalone navigator, not connected to React Navigation. Ideal for embedded flows that don’t need routing integration.
import { createComponentStackNavigator } from "react-native-screen-transitions/component-stack";

const Stack = createComponentStackNavigator();

<Stack.Navigator initialRouteName="step1">
  <Stack.Screen name="step1" component={Step1} />
  <Stack.Screen name="step2" component={Step2} />
</Stack.Navigator>

Advantages

  • Isolated state – Doesn’t affect parent navigation
  • Embeddable – Can be used inside any component
  • Simple API – No React Navigation setup required
  • Touch pass-through – Uses pointerEvents="box-none" by default

Limitations

Routes aren’t part of your URL structure.Impact: Can’t link directly to screens inside the component stack. Not suitable for top-level navigation.Workaround: Use Blank Stack or Native Stack for top-level navigation. Use Component Stack only for embedded flows.
Component Stack doesn’t integrate with React Navigation’s state management.Impact:
  • No access to useNavigation() from React Navigation
  • Can’t use React Navigation helpers like navigation.goBack()
  • Parent navigators don’t know about Component Stack routes
Workaround: Use Component Stack’s own navigation methods, or use Blank Stack if you need React Navigation integration.
Uses pointerEvents="box-none" by default to allow touch pass-through.Impact: Touch events pass through empty areas to content below. This is usually desired for embedded stacks, but may cause unexpected behavior if not understood.Workaround: Adjust pointerEvents prop if needed.
The API may change in future versions based on community feedback.Impact: Breaking changes possible in minor/patch releases.Recommendation: Pin your version and test thoroughly before upgrading.

When to Use

  • Embedded multi-step forms or wizards
  • Modal flows that don’t need routing
  • Isolated feature flows inside a screen
  • Prototypes and experiments

When to Avoid

  • Top-level app navigation
  • Any flow that needs deep linking
  • Production apps requiring API stability
  • Integration with React Navigation state

Performance Comparison

MetricBlank StackNative StackComponent Stack
Animation performanceExcellentExcellentExcellent
Touch responsivenessExcellentGood*Excellent
Memory usageLowLowLowest
Bundle size impactMediumMediumSmall
*Native Stack touch responsiveness is “Good” due to modal-related delays. For most apps, this is not noticeable.

Choosing the Right Stack

Use Blank Stack if:

  • You’re building a new app (default choice)
  • You need custom animations, gestures, or shared elements
  • Touch responsiveness is important
  • You want the fewest limitations

Use Native Stack if:

  • You specifically need native screen primitives
  • You want to mix custom and platform-native transitions
  • Touch delay doesn’t affect your UX
  • You’re migrating from @react-navigation/native-stack

Use Component Stack if:

  • You’re building embedded flows (wizards, onboarding)
  • You don’t need routing/deep linking
  • You’re okay with experimental API
  • You want isolation from parent navigation

Migration Path

All three stacks share the same animation API, making migration straightforward:
1

Change the import

// From
import { createNativeStackNavigator } from "react-native-screen-transitions/native-stack";

// To
import { createBlankStackNavigator } from "react-native-screen-transitions/blank-stack";
2

Update type annotations

// From
import type { NativeStackNavigationOptions } from "react-native-screen-transitions/native-stack";

// To
import type { BlankStackNavigationOptions } from "react-native-screen-transitions/blank-stack";
3

Remove enableTransitions flag (Native Stack only)

// Native Stack requires this
options={{
  enableTransitions: true,
  ...Transition.Presets.SlideFromBottom(),
}}

// Blank Stack doesn't need it
options={{
  ...Transition.Presets.SlideFromBottom(),
}}
All animation options, presets, and hooks work identically across stack types.