Upgrading from 6.x
React Navigation 7 focuses on streamlining the API to avoid patterns that can cause bugs. This means deprecating some of the legacy behavior kept for backward compatibility reasons.
This guides lists all the breaking changes and new features in React Navigation 7 that you need to be aware of when upgrading from React Navigation 6.
Minimum Requirements
react-native
>= 0.72.0expo
>= 52 (if you use Expo Go)typescript
>= 5.0.0 (if you use TypeScript)
Breaking changes
Changes to the navigate
action
The navigate
method no longer navigates to screen in a nested child navigator
Due to backward compatibility reasons, React Navigation 5 and 6 support navigating to a screen in a nested child navigator with navigation.navigate(ScreenName)
syntax. But this is problematic:
- It only works if the navigator is already mounted - making navigation coupled to other logic.
- It doesn't work with the TypeScript types.
Due to these issues, we have a special API to navigate to a nested screen (navigation.navigate(ParentScreenName, { screen: ScreenName })
).
From these release, this is no longer the default behavior. If you're relying on this behavior in your app, you can pass the navigationInChildEnabled
prop to NavigationContainer
to keep the behavior until you are able to migrate:
<NavigationContainer navigationInChildEnabled>{/* ... */}</NavigationContainer>
The navigationInChildEnabled
prop will be removed in the next major.
See navigate
for updated usage.
The navigate
method no longer goes back, use popTo
instead
Previously, navigate
method navigated back if the screen already exists in the stack. We have seen many people get confused by this behavior.
To avoid this confusion, we have removed the going back behavior from navigate
and added a new method popTo
to explicitly go back to a specific screen in the stack:
navigation.navigate('PreviousScreen', { foo: 42 });
navigation.popTo('PreviousScreen', { foo: 42 });
The methods now behave as follows:
navigate(screenName)
will stay on the current screen if the screen is already focused, otherwise push a new screen to the stack.popTo(screenName)
will go back to the screen if it exists in the stack, otherwise pop the current screen and add this screen to the stack.
See popTo
for more details.
To achieve a behavior similar to before with navigate
, you can use the getId
prop in which case it'll go to the screen with the matching ID and push or pop screens accordingly.
To help with the migration, we have added a new method called navigateDeprecated
which will behave like the old navigate
method. You can replace your current navigate
calls with navigateDeprecated
to gradually migrate to the new behavior:
navigation.navigate('SomeScreen');
navigation.navigateDeprecated('SomeScreen');
The navigateDeprecated
method will be removed in the next major.
The navigate
method no longer accepts a key
option
Previously, you could specify a route key
to navigate to, e.g.:
navigation.navigate({ key: 'someuniquekey' })`
It's problematic since:
key
is an internal implementation detail and created by the library internally - which makes it weird to use.- None of the other actions support such usage.
- Specifying a
key
is not type-safe, making it easy to cause bugs.
In React Navigation 5, we added the getId
prop which can be used for similar use cases - and gives users full control since they provide the ID and it's not autogenerated by the
library.
So the key
option is now being removed from the navigate
action.
See navigate
for updated usage.
Changes to NavigationContainer
The onReady
callback on NavigationContainer
now fires only when there are navigators rendered
Previously, the onReady
prop and navigationRef.isReady()
worked slightly differently:
- The
onReady
callback fired whenNavigationContainer
finishes mounting and deep links is resolved. - The
navigationRef.isReady()
method additionally checks if there are any navigators rendered - which may not be true if the user is rendering their navigators conditionally inside aNavigationContainer
.
This is important to know since if no navigator is rendered, we can't dispatch any navigation actions as there's no navigator to handle them. But the inconsistency between onReady
and navigationRef.isReady()
made it easy to cause issues and confusion.
This changes onReady
to work similar to navigationRef.isReady()
. The onReady
callback will now fire only when there are navigators rendered - reflecting the value of navigationRef.isReady()
.
This change is not breaking for most users, so you may not need to do anything.
See onReady
for usage.
The independent
prop on NavigationContainer
is removed in favor of NavigationIndependentTree
component
The independent
prop on NavigationContainer
was added to support rendering navigators in a separate tree from the rest of the app. This is useful for use cases such as miniapps.
However, there are issues with this approach:
- When building a miniapp, the responsibility of adding this prop was on the miniapp developer, which isn't ideal since forgetting it can cause problems.
- A lot of beginners mistakenly added this prop and were confused why navigation wasn't working.
So we've removed this prop instead of a NavigationIndependentTree
component which you can use to wrap the navigation container:
<NavigationContainer independent>
{/* ... */}
</NavigationContainer>
<NavigationIndependentTree>
<NavigationContainer>
{/* ... */}
</NavigationContainer>
</NavigationIndependentTree>
This way, the responsibility no longer lies on the miniapp developer, but on the parent app. It's also harder for beginners to accidentally add this.
See Independent navigation containers for usage.
The theme
prop now accepts a fonts
property
Previously, the theme
prop on NavigationContainer
accepted a colors
property to customize the colors used by various UI elements from React Navigation. We have now added a fonts
property to customize the fonts as well. If you are passing a custom theme in the theme
prop, you'll need to update it to include the fonts
property.
import { DefaultTheme } from '@react-navigation/native';
const theme = {
colors: {
// ...
},
fonts: DefaultTheme.fonts,
};
If you want to customize the fonts, see the themes guide for more details.
Changes to linking
Encoding of params in path position is now more relaxed
Previously, params were always URL encoded with encodeURIComponent
regardless of their position (e.g. query position such as ?user=jane
or path position such as /users/jane
) when generating a link for a screen (e.g. URL on the Web). This made it hard to use special characters in the params.
Now, only the params in the query position are URL encoded. For the params in the path position, we only encode the characters that are not allowed in the path position.
With this change, it's easier to use special characters such as @
in the path. For example, to have a URL such as profile/@username
, you can use the following in the linking config:
const config = {
prefixes: ['https://mysite.com'],
config: {
screens: {
Profile: {
path: 'profile/:username',
parse: {
username: (username) => username.replace(/^@/, ''),
},
stringify: {
username: (username) => `@${username}`,
},
},
},
},
};
See Configuring links for usage of the linking config.
The Link
component and useLinkProps
hook now use screen names instead of paths
Previously, the Link
component and useLinkProps
hook were designed to work with path strings via the to
prop. But it had few issues:
- The path strings are not type-safe, making it easy to cause typos and bugs after refactor
- The API made navigating via screen name more inconvenient, even if that's the preferred approach
Now, instead of the to
prop that took a path string, they now accept screen
and params
props, as well as an optional href
prop to use instead of the generated path:
<Link to="/details?foo=42">Go to Details</Link>
<Link screen="Details" params={{ foo: 42 }}>Go to Details</Link>
or
const props = useLinkProps({ to: '/details?foo=42' });
const props = useLinkProps({ screen: 'Details', params: { foo: 42 } });
With this change, you'd now have full type-safety when using the Link
component given that you have configured the global type.
See Link
and useLinkProps
for usage.
The useLinkBuilder
hooks now returns an object instead of a function
Previously, the useLinkBuilder
hooks returned a function to build a href
for a screen - which is primarily useful for building custom navigators. Now, it returns an object with buildHref
and buildAction
methods:
const { buildHref, buildAction } = useLinkBuilder();
const href = buildHref('Details', { foo: 42 }); // '/details?foo=42'
const action = buildAction('/details?foo=42'); // { type: 'NAVIGATE', payload: { name: 'Details', params: { foo: 42 } } }
The buildHref
method acts the same as the previously returned function. The new buildAction
method can be used to build a navigation action from a href
string.
Note that this hook is intended to be primarily used by custom navigators and not by end users. For end users, the Link
component and useLinkProps
are the recommended way to navigate.
See useLinkBuilder
for usage.
Changes to navigators
Screens pushed on top of modals are now shown as modals in the Stack and Native Stack navigators
Previously, screens pushed on top of modals were shown as regular screens in the Stack and Native Stack navigators. This often caused glitchy animation on Stack Navigator and appeared behind the modal on Native Stack Navigator. This can be especially confusing if the user came to the screen from a deep link.
Now, screens pushed on top of modals are automatically shown as modals to avoid these issues. This behavior can be disabled by explicitly setting the presentation
option to card
:
<Stack.Screen
name="MyModal"
component={MyModalScreen}
options={{
presentation: 'card',
}}
/>
See Stack Navigator and Native Stack Navigator docs for usage.
headerBackTitleVisible
is removed in favor of headerBackButtonDisplayMode
in Stack and Native Stack navigators
Previously, headerBackTitleVisible
could be used to control whether the back button title is shown in the header. It's now removed in favor of headerBackButtonDisplayMode
which provides more flexibility.
The previous behavior can be achieved by setting headerBackButtonDisplayMode
to default
and minimal
for showing and hiding the back button title respectively:
<Stack.Screen
name="Details"
component={DetailsScreen}
options={{
headerBackTitleVisible: false,
headerBackButtonDisplayMode: 'minimal',
}}
/>
animationEnabled
option is removed in favor of animation
option in Stack Navigator
Previously, animationEnabled: false
was used to disable the animation for the screen transition in Stack Navigator.
There's now a new animation
prop to configure animations similar to the Native Stack. So you can now use animation: 'none'
to disable the animation instead:
<Stack.Screen
name="Details"
component={DetailsScreen}
options={{
animationEnabled: false,
animation: 'none',
}}
/>
See Stack Navigator animation for usage.
customAnimationOnGesture
is renamed to animationMatchesGesture
in Native Stack Navigator
The customAnimationOnGesture
option in Native Stack Navigator is renamed to animationMatchesGesture
to better reflect its purpose. If you are using customAnimationOnGesture
in your project, you can rename it to animationMatchesGesture
:
<Stack.Navigator options={{ customAnimationOnGesture: true }}>
<Stack.Navigator options={{ animationMatchesGesture: true }}>
See Native Stack Navigator for usage.
statusBarColor
is renamed to statusBarBackgroundColor
in Native Stack Navigator
The statusBarColor
option in Native Stack Navigator is renamed to statusBarBackgroundColor
to better reflect its purpose. If you are using statusBarColor
in your project, you can rename it to statusBarBackgroundColor
:
<Stack.Navigator options={{ statusBarColor: 'tomato' }}>
<Stack.Navigator options={{ statusBarBackgroundColor: 'tomato' }}>
See Native Stack Navigator for usage.
Native Stack now requires react-native-screens
4
@react-navigation/native-stack
now requires react-native-screens
4 and will break when using an earlier version. If you are using Native Stack Navigator in your project, make sure to upgrade react-native-screens
to version 4.
See Native Stack Navigator for usage.
Material Top Tab Navigator no longer requires installing react-native-tab-view
Previously, @react-navigation/material-top-tabs
required installing react-native-tab-view
as a dependency in the project. We have now moved this package to the React Navigation monorepo and able to coordinate the releases together, so it's no longer necessary to install it separately.
If you use @react-navigation/material-top-tabs
and don't use react-native-tab-view
anywhere else in your project, you can remove it from your dependencies after upgrading.
If you need to enforce a specific version of react-native-tab-view
for some reason, we recommend using Yarn resolutions or npm overrides to do so.
See Material Top Tab Navigator for usage.
The unmountOnBlur
option is removed in favor of popToTopOnBlur
in Bottom Tab Navigator and Drawer Navigator
In many cases, the desired behavior is to return to the first screen of the stack nested in a tab or drawer navigator after it's unfocused. Previously, the unmountOnBlur
option was used to achieve this behavior. However, it had some issues:
- It destroyed the local state of the screen in the stack.
- It was slow to remount the nested navigator on tab navigation.
The popToTopOnBlur
option provides an alternative approach - it pops the screens on a nested stack to go back to the first screen in the stack and doesn't have the above issues.
See Bottom Tab Navigator and Drawer Navigator docs for usage.
It's still possible to achieve the old behavior of unmountOnBlur
by using the useIsFocused hook in the screen:
const isFocused = useIsFocused();
if (!isFocused) {
return null;
}
This could also be combined with the new layout props to specify it at the screen configuration level.
The tabBarTestID
option is renamed to tabBarButtonTestID
in Bottom Tab Navigator and Material Top Tab Navigator
The tabBarTestID
option in @react-navigation/bottom-tabs
and @react-navigation/material-top-tabs
is renamed to tabBarButtonTestID
to better reflect its purpose. If you are using tabBarTestID
in your project, you can rename it to tabBarButtonTestID
:
<Tab.Navigator tabBarOptions={{ tabBarTestID: 'test-id' }}>
<Tab.Navigator tabBarOptions={{ tabBarButtonTestID: 'test-id' }}>
See Bottom Tab Navigator and Material Top Tab Navigator docs for usage.
The sceneContainerStyle
prop and option are removed from Bottom Tab Navigator, Material Top Tab Navigator and Drawer Navigator in favor of sceneStyle
Previously, the Bottom Tab Navigator and Material Top Tab Navigator accepted a sceneContainerStyle
prop to style the container of the scene. This was inflexible as it didn't allow different styles for different screens. Now, the sceneStyle
option is added to these navigators to style individual screens.
Similarly, the sceneContainerStyle
option in Drawer Navigator is renamed to sceneStyle
for consistency.
If you are using sceneContainerStyle
prop, you can pass sceneStyle
in screenOptions
instead to keep the same behavior:
<Tab.Navigator sceneContainerStyle={{ backgroundColor: 'white' }}>
<Tab.Navigator screenOptions={{ sceneStyle: { backgroundColor: 'white' } }}>
Drawer Navigator now requires Reanimated 2 or 3 on native platforms
Previously, @react-navigation/drawer
supported both Reanimated 1 and Reanimated 2 APIs with the useLegacyImplementation
option. This is now no longer supported and the useLegacyImplementation
option is removed.
If you are using Reanimated 1 in your project, you'll need to upgrade to Reanimated 2 or 3 to use @react-navigation/drawer
.
If you're using Drawer Navigator on the Web, it'll now use CSS transitions instead of Reanimated for a smaller bundle size.
See Drawer Navigator for usage.
Changes to elements
labelVisible
is removed in favor of displayMode
in headerLeft
and HeaderBackButton
elements
Previously, labelVisible
could be used to control whether the back button title is shown in the header. It's now removed in favor of displayMode
which provides more flexibility.
The new possible values are:
default
: Displays one of the following depending on the available space: previous screen's title, generic title (e.g. 'Back') or no title (only icon).generic
: Displays one of the following depending on the available space: generic title (e.g. 'Back') or no title (only icon). iOS >= 14 only, falls back to "default" on older iOS versions.minimal
: Always displays only the icon without a title.
The previous behavior can be achieved by setting displayMode
to default
or generic
for showing and minimal
for hiding the back button title respectively:
<HeaderBackButton
labelVisible={false}
displayMode="minimal"
/>
Deprecations and removals
Material Bottom Tab Navigator now lives in react-native-paper
package
The @react-navigation/material-bottom-tabs
package provided React Navigation integration for react-native-paper
's BottomNavigation
component. To make it easier to keep it updated with the changes in react-native-paper
, we have now moved it to the react-native-paper
package.
If you are using @react-navigation/material-bottom-tabs
in your project, you can remove it from your dependencies and change the imports to react-native-paper/react-navigation
instead:
import { createMaterialBottomTabNavigator } from '@react-navigation/material-bottom-tabs';
import { createMaterialBottomTabNavigator } from 'react-native-paper/react-navigation';
See Material Bottom Tab Navigator for usage.
Alternatively, you can use the BottomNavigation.Bar
component as a custom tab bar with @react-navigation/bottom-tabs
.
For any issues related to the Material Bottom Tab Navigator or BottomNavigation.Bar
, please open them in the react-native-paper repository instead of the React Navigation repository.
The flipper devtools plugin is now removed
Previously, we added a Flipper plugin for React Navigation to make debugging navigation easier. However, it has added significant maintenance overhead for us. The Flipper team hasn't been focused on React Native recently, so the overall experience of using Flipper with React Native has been poor.
Currently, the Flipper team has been focused on native developer experience, so we are going back to the drawing board. We have created a new pillar within our team focused on Developer Experience. We are currently investigating improved Chrome Debugger protocol support from the Hermes team as well as migrating the debugging experience from Flipper to Chrome DevTools so we can deliver a debugging experience that meets our standard.
react-native-community/discussions-and-proposals#546 (comment)
Since the React Native team migrating away from Flipper, it doesn't make much sense for us to spend additional resources to keep supporting it. So we've removed the Flipper plugin from @react-navigation/devtools
.
Alternatively, you can use the following developer tools:
- Logger
- Integration for Redux DevTools Extension
- Devtools plugin for Expo if you are using Expo.
Various deprecated APIs are removed
We have removed all of the previously deprecated APIs. These APIs were deprecated in React Navigation 6 and showed a warning when used. So make sure that you have addressed all the warnings before upgrading.
Full list of removed APIs
@react-navigation/stack
- Removed
mode
prop - usepresentation
option instead - Removed
headerMode
prop - useheaderMode
andheaderShown
options instead - Removed
keyboardHandlingEnabled
prop - usekeyboardHandlingEnabled
option instead
- Removed
@react-navigation/drawer
- Removed
openByDefault
prop - usedefaultStatus
prop instead - Removed
lazy
prop - uselazy
option instead - Removed
drawerContentOptions
prop which contained following options:drawerPosition
- usedrawerPosition
option insteaddrawerType
- usedrawerType
option insteadedgeWidth
- useswipeEdgeWidth
option insteadhideStatusBar
- usedrawerHideStatusBarOnOpen
option insteadkeyboardDismissMode
- usekeyboardDismissMode
option insteadminSwipeDistance
- useswipeMinDistance
option insteadoverlayColor
- useoverlayColor
option insteadstatusBarAnimation
- usedrawerStatusBarAnimation
option insteadgestureHandlerProps
- useconfigureGestureHandler
option instead
- Removed
@react-navigation/bottom-tabs
- Removed
lazy
prop - uselazy
option instead - Removed
tabBarOptions
prop which contained following options:keyboardHidesTabBar
- usetabBarHideOnKeyboard
option insteadactiveTintColor
- usetabBarActiveTintColor
option insteadinactiveTintColor
- usetabBarInactiveTintColor
option insteadactiveBackgroundColor
- usetabBarActiveBackgroundColor
option insteadinactiveBackgroundColor
- usetabBarInactiveBackgroundColor
option insteadallowFontScaling
- usetabBarAllowFontScaling
option insteadshowLabel
- usetabBarShowLabel
option insteadlabelStyle
- usetabBarLabelStyle
option insteadiconStyle
- usetabBarIconStyle
option insteadtabStyle
- usetabBarItemStyle
option insteadlabelPosition
andadapative
- usetabBarLabelPosition
option insteadtabBarVisible
- usedisplay: 'none'
tabBarStyle
option instead
- Removed
@react-navigation/material-top-tabs
- Removed
swipeEnabled
prop - useswipeEnabled
option instead - Removed
lazy
prop - uselazy
option instead - Removed
lazyPlaceholder
prop - uselazyPlaceholder
option instead - Removed
lazyPreloadDistance
prop - uselazyPreloadDistance
option instead - Removed
tabBarOptions
prop which contained following options: -renderBadge
- usetabBarBadge
option instead -renderIndicator
- usetabBarIndicator
option instead -activeTintColor
- usetabBarActiveTintColor
option instead -inactiveTintColor
- usetabBarInactiveTintColor
option instead -pressColor
- usetabBarPressColor
option instead -pressOpacity
- usetabBarPressOpacity
option instead -showLabel
- usetabBarShowLabel
option instead -showIcon
- usetabBarShowIcon
option instead -allowFontScaling
- usetabBarAllowFontScaling
option instead -bounces
- usetabBarBounces
option instead -scrollEnabled
- usetabBarScrollEnabled
option instead -iconStyle
- usetabBarIconStyle
option instead -labelStyle
- usetabBarLabelStyle
option instead -tabStyle
- usetabBarItemStyle
option instead -indicatorStyle
- usetabBarIndicatorStyle
option instead -indicatorContainerStyle
- usetabBarIndicatorContainerStyle
option instead -contentContainerStyle
- usetabBarContentContainerStyle
option instead -style
- usetabBarStyle
option instead
- Removed
Miscellaneous
Various UI elements now follow Material Design 3 guidelines
Previously, the UI elements in React Navigation such as the header on platforms other than iOS, drawer, material top tabs etc. were following the Material Design 2 guidelines. We have now updated them to follow the Material Design 3 guidelines.
React Native Tab View now has a new API to specify various options
The API for the TabView
and TabBar
component in react-native-tab-view
has been revamped. Previously, the TabBar
took the following props:
getLabelText
getAccessible
getAccessibilityLabel
getTestID
renderIcon
renderLabel
renderBadge
These props have been replaced with commonOptions
and options
props on TabView
:
<TabView
commonOptions={{
icon: ({ route, focused, color }) => (
<Icon name={route.icon} color={color} />
),
}}
options={{
albums: {
labelText: 'Albums',
},
profile: {
labelText: 'Profile',
},
}}
/>
When using a custom tab bar, it will receive the options
in the arguments.
The new API will make it easier for us to improve re-rendering performance of the tab bar items in the library.
See React Native Tab View for usage.
Custom navigators now require more type information
Custom navigators now require more type information to work correctly so that we can provide better type-checking and autocompletion in TypeScript when using the navigator.
export const createMyNavigator = createNavigatorFactory<
MyNavigationState<ParamListBase>,
MyNavigationOptions,
MyNavigationEventMap,
typeof MyNavigator
>(MyNavigator);
export function createMyNavigator<
const ParamList extends ParamListBase,
const NavigatorID extends string | undefined = undefined,
const TypeBag extends NavigatorTypeBagBase = {
ParamList: ParamList;
NavigatorID: NavigatorID;
State: TabNavigationState<ParamList>;
ScreenOptions: TabNavigationOptions;
EventMap: TabNavigationEventMap;
NavigationList: {
[RouteName in keyof ParamList]: TabNavigationProp<
ParamList,
RouteName,
NavigatorID
>;
};
Navigator: typeof TabNavigator;
},
const Config extends StaticConfig<TypeBag> = StaticConfig<TypeBag>,
>(config?: Config): TypedNavigator<TypeBag, Config> {
return createNavigatorFactory(MyNavigator)(config);
}
See Custom navigators for usage.
Packages now use ESM and package exports
All the packages in React Navigation now use ESM exports. While it shouldn't affect most users, there are some changes to be aware of:
- If you are importing internal files from the packages, they might now be restricted by your bundler and it won't be possible to import them directly. You should use the public API instead.
- If you're patching the packages using
patch-package
,yarn patch
etc., you'll need to patch the built files underlib/
folders instead of the source files undersrc/
as the source files are no longer exported. - If you're using TypeScript with the
module
ormoduleResolution
option, it maybe necessary to setmoduleResolution
toBundler
to match Metro's resolution behavior. - If you're using Webpack for bundling code using React Navigation, it maybe necessary to set
resolve.fullySpecified
tofalse
for bundling to work.
New features
Static configuration API
React Navigation 5 introduced a dynamic API to support more flexible use cases. With React Navigation 7, we are re-introducing a static configuration API:
import { createNativeStackNavigator } from 'react-native-screens/native-stack';
const MyStack = createNativeStackNavigator({
screens: {
Home: {
screen: HomeScreen,
options: {
title: 'My App',
},
},
Details: {
screen: DetailsScreen,
linking: 'details/:id',
},
},
});
The static configuration API provides the following benefits:
- Simpler type-checking with TypeScript: It's not necessary to specify screens and their params separately. See Type checking with TypeScript for more details.
- Easier deep linking setup: Paths can be generated automatically. Linking configuration can be defined next to the screen for explicit configuration. See Configuring links for more details.
It's also possible to mix the static and dynamic configuration APIs. For example, you can use the static configuration API for the top-level navigators and the dynamic configuration API for the nested navigators where you need more flexibility.
The static configuration API doesn't replace the dynamic configuration API. Both APIs are equally supported and you can choose the one that fits your use case better.
You can see examples for both the static and dynamic configuration APIs in the documentation by selecting the appropriate tab in the examples.
Go to "Hello React Navigation" to start writing some code with the static API.
Improved TypeScript support
Previously, the navigation
object received in options
and listeners
callbacks were typed as any
and required manual type annotation. Now, the navigation
object has a more accurate type based on the navigator it's used in, and the type annotation is no longer required.
We also export a new XOptionsArgs
(where X
is the navigator name, e.g. StackOptionsArgs
, BottomTabOptionsArgs
etc.) type which can be used to type the arguments of the options
callback. This can be useful if you want to define the options callback separately.
const options = ({
route,
}: StackOptionsArgs<RootStackParamList, 'Details'>) => {
return {
title: route.params.title,
};
};
Improved RTL support
Previously, various UI elements used the I18nManager
API to determine the writing direction. However, this API doesn't work well on the Web as the writing direction can be different for a specific subtree and hence can't be determined globally.
The NavigationContainer
now accepts a direction
prop to specify the direction of the layout instead of relying on the I18nManager
API. It also exposes this value via useLocale
hook for use in your own components.
See the navigation container docs for usage.
The options
callback gets theme
The options
callback now receives the theme
object to allow customizing the UI elements specified in the options:
<Stack.Screen
name="Details"
component={DetailsScreen}
options={({ theme }) => ({
headerRight: () => (
<IconButton
icon="dots-horizontal"
onPress={() => {}}
color={theme.colors.primary}
/>
),
})}
/>
See Screen options for usage.
Top-level path
in linking config
The linking configuration now supports a top-level path
configuration to define the base path for all the screens in the navigator:
const linking = {
prefixes: ['https://mysite.com'],
config: {
path: 'app',
screens: {
Home: 'home',
Details: 'details/:id',
},
},
};
This can be useful if your app lives under a subpath on the web. For example, if your app lives under https://mysite.com/app
, you can define the path
as app
and the Details
screen will be accessible at https://mysite.com/app/details/42
.
See Configuring links for usage.
Improved Web integration
More built-in UI elements that trigger navigation now render a
tags on the Web for better accessibility and SEO. This includes:
- Back button in the header
- The tab buttons in material top tab navigator
UI elements such as the bottom tab bar and drawer items already rendered a
tags on the Web.
New usePreventRemove
hook
Previously, the only way to prevent a screen from being removed from the stack was to use the beforeRemove
event. This didn't work well with the Native Stack Navigator.
The new usePreventRemove
hook is an alternative to beforeRemove
that works with the Native Stack Navigator.
See usePreventRemove
for usage.
New layout
props
For navigators
Navigators now support a layout
prop. It can be useful for augmenting the navigators with additional UI with a wrapper. The difference from adding a regular wrapper is that the code in layout
callback has access to the navigator's state, options etc.:
<Stack.Navigator
layout={({ children, state, descriptors, navigation }) => (
<View style={styles.container}>
<Breadcrumbs />
{children}
</View>
)}
>
{/* ... */}
</Stack.Navigator>
See Navigator layout for usage.
For screens and groups
The layout
prop makes it easier to provide things such as a global error boundary and suspense fallback for a group of screens without having to manually add HOCs for every screen separately.
It can be used for a single screen with layout
:
<Stack.Screen
name="MyScreen"
component={MyScreenComponent}
layout={({ children }) => (
<ErrorBoundary>
<React.Suspense
fallback={
<View style={styles.fallback}>
<Text style={styles.text}>Loading…</Text>
</View>
}
>
{children}
</React.Suspense>
</ErrorBoundary>
)}
/>
Or with a group or navigator with screenLayout
:
<Stack.Group
screenLayout={({ children }) => (
<ErrorBoundary>
<React.Suspense
fallback={
<View style={styles.fallback}>
<Text style={styles.text}>Loading…</Text>
</View>
}
>
{children}
</React.Suspense>
</ErrorBoundary>
)}
>
{/* screens */}
</Stack.Group>
Preloading screens
All built-in navigators now support preloading screens prior to navigating to them. This can be useful to improve the perceived performance of the app by preloading the screens that the user is likely to navigate to next. Preloading a screen will render it off-screen and execute its side-effects such as data fetching.
To preload a screen, you can use the preload
method on the navigation object:
navigation.preload('Details', { id: 42 });
See preload
for usage.
Improvements to navigators
Bottom Tab Navigator can now show tabs on the side and top
The @react-navigation/bottom-tabs
package now supports showing tabs on the side. This will make it easier to build responsive UIs for where you want to show tabs on the bottom on smaller screens and switch to a sidebar on larger screens.
Similarly, showing tabs on the top is also supported which can be useful for Android TV or Apple TV apps.
You can use the tabBarPosition
option to customize the position of the tabs:
<Tab.Navigator
screenOptions={{
tabBarPosition: 'left',
}}
>
{/* ... */}
</Tab.Navigator>
See Bottom Tab Navigator options for usage.
Bottom Tab Navigator now supports animations
The @react-navigation/bottom-tabs
package now supports animations. This was one of the most requested features on our Canny board: TabNavigator Custom Transition.
You can use the animation
option to customize the animations for the tab transitions:
<Tab.Navigator
screenOptions={{
animation: 'fade',
}}
>
{/* ... */}
</Tab.Navigator>
See Bottom Tab Navigator animation for usage.
Stack Navigator now supports an animation
option
The @react-navigation/stack
package now supports an animation
option to customize the animations for the screen transitions:
<Stack.Navigator
screenOptions={{
animation: 'slide_from_right',
}}
>
{/* ... */}
</Stack.Navigator>
The animation
option is an alternative to the TransitionPresets
API, and is intended to make migrating between JS stack and native stack navigators easier.
See Stack Navigator animation for usage.
Native Stack Navigator now exports a useAnimatedHeaderHeight
hook
The @react-navigation/native-stack
package now exports a useAnimatedHeaderHeight
hook. It can be used to animate content based on the header height changes - such as when the large title shrinks to a small title on iOS:
const headerHeight = useAnimatedHeaderHeight();
return (
<Animated.View style={{ transform: { translateY: headerHeight } }}>
{/* ... */}
</Animated.View>
);
All navigators with headers now support headerSearchBarOptions
The Header
component from @react-navigation/elements
now supports a headerSearchBarOptions
prop. This means all navigators that use the Header
component now support a search bar in the header as well on all platforms. Previously, this was only available in the Native Stack Navigator on iOS and Android.
React.useLayoutEffect(() => {
navigation.setOptions({
headerSearchBarOptions: {
placeholder: 'Search',
onChangeText: (text) => {
// Do something
},
},
});
}, [navigation]);
See headerSearchBarOptions
for usage.
New components in elements library
The @react-navigation/elements
package now includes new components that can be used in your app:
Button
The Button
component has built-in support for navigating to screens, and renders an anchor tag on the Web when used for navigation:
<Button screen="Profile" params={{ userId: 'jane' }}>
View Jane's Profile
<Button>
It can also be used as a regular button:
<Button
onPress={() => {
/* do something */
}}
>
Do something
</Button>
The button follows the Material Design 3 guidelines.
See Button
for usage.
HeaderButton
The HeaderButton
component can be used to render buttons in the header with appropriate styling:
headerRight: ({ tintColor }) => (
<HeaderButton
accessibilityLabel="More options"
onPress={() => {
/* do something */
}}
>
<MaterialCommunityIcons
name="dots-horizontal-circle-outline"
size={24}
color={tintColor}
/>
</HeaderButton>
),
See HeaderButton
for usage.
Label
The Label
component can be used to render label text, such as the label in a tab bar button:
<Label>Home</Label>
See Label
for usage.
react-native-drawer-layout
package
The drawer implementation used in @react-navigation/drawer
is now available as a standalone package called react-native-drawer-layout
. This makes it easier to use the drawer implementation even if you're not using React Navigation, or if you want to use it without a navigator.
You can install it with:
- npm
- Yarn
- pnpm
npm install react-native-drawer-layout
yarn add react-native-drawer-layout
pnpm add react-native-drawer-layout
See react-native-drawer-layout
for usage.
useLogger
devtool
The @react-navigation/devtools
package now exports a useLogger
hook. It can be used to log navigation actions to the console:
See useLogger
for usage.