Guides

Expo Router

Create a v3 blank stack layout for Expo Router with createBlankStackNavigator and withLayoutContext.

Overview

In v3, Expo Router integration wraps createBlankStackNavigator() with Expo Router's withLayoutContext().

Looking for Expo SDK 55+ support? See the v4 documentation.

Blank Stack Layout

This is the pattern from the e2e app:

TSX

1// layouts/blank-stack.tsx
2import "react-native-reanimated";
3import type {
4 ParamListBase,
5 StackNavigationState,
6} from "@react-navigation/native";
7import { withLayoutContext } from "expo-router";
8import type { ComponentProps } from "react";
9import {
10 type BlankStackNavigationEventMap,
11 type BlankStackNavigationOptions,
12 createBlankStackNavigator,
13} from "react-native-screen-transitions/blank-stack";
14
15const { Navigator } = createBlankStackNavigator();
16
17function BlankStackNavigator(props: ComponentProps<typeof Navigator>) {
18 return <Navigator {...props} nativeScreens={false} />;
19}
20
21export const BlankStack = withLayoutContext<
22 BlankStackNavigationOptions,
23 typeof BlankStackNavigator,
24 StackNavigationState<ParamListBase>,
25 BlankStackNavigationEventMap
26>(BlankStackNavigator);

The local wrapper lets your screens use screen options like screenStyleInterpolator, gestureEnabled, snapPoints, and overlay.

The blank-stack navigator itself also accepts navigator props like independent and nativeScreens. The e2e wrapper above pins nativeScreens={false}; remove that fixed prop if route layouts should choose the value themselves.

Using It In Route Layouts

TSX

1// app/gestures/_layout.tsx
2import Transition from "react-native-screen-transitions";
3import { BlankStack } from "@/layouts/blank-stack";
4
5export default function GesturesLayout() {
6 return (
7 <BlankStack>
8 <BlankStack.Screen name="index" />
9 <BlankStack.Screen
10 name="sheet"
11 options={{
12 ...Transition.Presets.SlideFromBottom(),
13 }}
14 />
15 </BlankStack>
16 );
17}

router.push(), router.replace(), and router.back() still work normally. The transition behavior comes from the options you put on BlankStack.Screen.

Bounds And Router Navigation

TSX

1import { router } from "expo-router";
2import Transition from "react-native-screen-transitions";
3
4<Transition.Boundary
5 id="hero"
6 onPress={() => router.push("/detail")}
7>
8 <Card />
9</Transition.Boundary>

The destination can use a matching passive Transition.Boundary, Transition.Boundary.Target, navigationMaskEnabled, and screenStyleInterpolator the same way it would outside Expo Router.

Embedded Flows

TSX

1<BlankStack independent enableNativeScreens={false}>
2 <BlankStack.Screen name="index" />
3</BlankStack>

Use independent only when you want an isolated navigation tree, not for normal route layouts.