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
>= 49 (if you use Expo)typescript
>= 5.0.0 (if you use TypeScript)
Breaking changes
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.
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.
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.
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.
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.
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.
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.
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.
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
.
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.
Here is the 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
- usegestureHandlerProps
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 insteadrenderIndicator
- usetabBarIndicator
option insteadactiveTintColor
- usetabBarActiveTintColor
option insteadinactiveTintColor
- usetabBarInactiveTintColor
option insteadpressColor
- usetabBarPressColor
option insteadpressOpacity
- usetabBarPressOpacity
option insteadshowLabel
- usetabBarShowLabel
option insteadshowIcon
- usetabBarShowIcon
option insteadallowFontScaling
- usetabBarAllowFontScaling
option insteadbounces
- usetabBarBounces
option insteadscrollEnabled
- usetabBarScrollEnabled
option insteadiconStyle
- usetabBarIconStyle
option insteadlabelStyle
- usetabBarLabelStyle
option insteadtabStyle
- usetabBarItemStyle
option insteadindicatorStyle
- usetabBarIndicatorStyle
option insteadindicatorContainerStyle
- usetabBarIndicatorContainerStyle
option insteadcontentContainerStyle
- usetabBarContentContainerStyle
option insteadstyle
- usetabBarStyle
option instead
- Removed
customAnimationOnGesture
is renamed to animationMatchesGesture
in Native Stack Navigator
TODO
Material Top Tab Navigator no longers 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.
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';
The tabBarTestID
option is renamed to tabBarButtonTestID
in Bottom Tab Navigator
TODO
Drawer Navigator no longer supports Reanimated 1
TODO
New features
Navigators now support a static configuration API
TODO
Support a top-level path
configuration in linking config
TODO
Support custom layout
prop for Navigators
TODO
Add experimental API for handling deep link after authentication
TODO
Add a Button
component to Elements
TODO
Add useAnimatedHeaderHeight
hook to Native Stack Navigator
TODO
Bottom Tab Navigator now supports animations
TODO
Bottom Tab Navigator can now show tabs on the side
TODO
The drawer implementation is now available in react-native-drawer-layout
as a standalone package
TODO