Skip to main content
Version: 8.x

Static API vs Dynamic API

React Navigation provides two ways to configure your navigation:

  • Static - object-based configuration with automatic TypeScript types and deep linking
  • Dynamic - component-based dynamic configuration

If you're already familiar with the dynamic API, this guide explains how each concept maps to the static API - whether you're migrating an existing app or just learning the static API.

Limitations

The main limitation of the static API is that the navigation structure must be static. This means that you cannot create the list of screens dynamically.

It's still possible to use dynamic logic for other parts with if, with etc.

Basic usage

In the dynamic API, navigators are React components rendered inside NavigationContainer. In the static API, you pass the configuration object to createXNavigator and render the component returned by createStaticNavigation:

Dynamic API
import * as React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';

const Stack = createNativeStackNavigator();

function RootStack() {
return (
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} />
</Stack.Navigator>
);
}

function App() {
return (
<NavigationContainer>
<RootStack />
</NavigationContainer>
);
}
Static API
import * as React from 'react';
import { createStaticNavigation } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';

const RootStack = createNativeStackNavigator({
screens: {
Home: HomeScreen,
},
});

const Navigation = createStaticNavigation(RootStack);

function App() {
return <Navigation />;
}

The component returned by createStaticNavigation accepts the same props as NavigationContainer.

Screens do not receive the navigation object as a prop in the static API. It's necessary to use the useNavigation hook instead:

Dynamic API
function HomeScreen({ navigation }) {
return (
<Button
title="Go to profile"
onPress={() => navigation.navigate('Profile')}
/>
);
}
Static API
function HomeScreen() {
const navigation = useNavigation('Home');

return (
<Button
title="Go to profile"
onPress={() => navigation.navigate('Profile')}
/>
);
}

The route prop is still passed to the screen component as a prop in the static API. But it maybe preferable to use the useRoute hook when defining types with linking.

In the dynamic API, navigator configuration is passed as props to the navigator component. In the static API, they become top-level keys in the config object.

The screens are defined in a screens property instead of as children. It contains a mapping of screen name to screen components, a nested navigator, or a screen configuration object.

Dynamic API
<Stack.Navigator
initialRouteName="Home"
screenOptions={{
headerTintColor: 'white',
headerStyle: { backgroundColor: 'tomato' },
}}
>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Profile" component={ProfileScreen} />
</Stack.Navigator>
Static API
const RootStack = createNativeStackNavigator({
initialRouteName: 'Home',
screenOptions: {
headerTintColor: 'white',
headerStyle: { backgroundColor: 'tomato' },
},
screens: {
Home: HomeScreen,
Profile: ProfileScreen,
},
});

Screen configuration

All props passed to <Stack.Screen> except name and component become properties in the screen configuration object. The component passed to component becomes the value of the screen property in the screen config:

Dynamic API
<Stack.Screen
name="Profile"
component={ProfileScreen}
options={({ route }) => ({
title: route.params.userId,
})}
listeners={{
focus: () => console.log('focused'),
}}
getId={({ params }