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:
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>
);
}
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.
Navigation object
Screens do not receive the navigation object as a prop in the static API. It's necessary to use the useNavigation hook instead:
function HomeScreen({ navigation }) {
return (
<Button
title="Go to profile"
onPress={() => navigation.navigate('Profile')}
/>
);
}
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.
Navigator options
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.
<Stack.Navigator
initialRouteName="Home"
screenOptions={{
headerTintColor: 'white',
headerStyle: { backgroundColor: 'tomato' },
}}
>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Profile" component={ProfileScreen} />
</Stack.Navigator>
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:
<Stack.Screen
name="Profile"
component={ProfileScreen}
options={({ route }) => ({
title: route.params.userId,
})}
listeners={{
focus: () => console.log('focused'),
}}
getId={({ params }