React Navigation 8.0 - June Progress Report
We've continued working on React Navigation 8.0 since the March progress report. Since then, we've released many refinements, improvements and new features.
This post covers the highlights that landed since March 2026.
Minimum requirements
We have updated our navigators to use the newer APIs in react-native-screens and react-native-gesture-handler. So we now require:
react-native-screens4.25.0 or laterreact-native-gesture-handler3.0.0 or later
In addition, we also use newer TypeScript features, so the minimum TypeScript version is now 6.0.0 if you use TypeScript in your app.
Highlights
Typed hooks in dynamic navigators
One of the best features we announced in the alpha was typed hooks. In the initial announcement, we mostly focused on the static configuration API where all of the features worked. Dynamic navigators were supported, but lacked nested navigator awareness.
Since then, we have added support for dynamic navigators as well. This means the useNavigation and useNavigationState hooks now know the type of navigator they are in as well as their parent navigators. So navigation object will have correct type for setOptions, addListener etc. as well as navigator specific actions like push, openDrawer etc. based on where the hook is used.
To achieve this, you need to change the generic that's passed to NavigatorScreenParams in your param list to the type of the child navigator:
type RootStackParamList = {
Home: undefined;
Account: NavigatorScreenParams<AccountTabsParamList>;
Account: NavigatorScreenParams<typeof AccountTabs>;
};
This also means that you no longer need to manually annotate navigation prop. If you use typed hooks everywhere, it'll automatically combined navigation objects from parent and child navigators. So you can remove usage of CompositeNavigationProp and CompositeScreenProps in your app reducing boilerplate:
type ProfileScreenNavigationProp = CompositeNavigationProp<
BottomTabNavigationProp<TabParamList, 'Profile'>,
CompositeNavigationProp<
StackNavigationProp<StackParamList, 'Account'>,
DrawerNavigationProp<DrawerParamList, 'Home'>
>
>;
type Props = {
navigation: ProfileScreenNavigationProp;
};
function ProfileScreen({ navigation }: Props) {
function ProfileScreen() {
const navigation = useNavigation('Profile');
// ...
}
In addition, we have improved the type inference performance as well. So for complex apps, the typechecking, intellisense etc. should be noticeably faster.
Checkout the initial announcement for more details, and the TypeScript documentation for setup guide.
Simpler types for custom navigators
Custom navigators also get a simpler type API. Previously, custom navigator factories needed a long list of generic arguments to describe params, state, options, events, action helpers, and the navigator component along with a wrapper function for the factory.
We have reworked this to be significantly shorter and simpler:
export type MyTabTypeBag<
ParamList extends ParamListBase = ParamListBase,
NavigatorID extends string | undefined = string | undefined,
> = {
ParamList: ParamList;
NavigatorID: NavigatorID;
State: TabNavigationState<ParamList>;
ScreenOptions: MyNavigationOptions;
EventMap: MyNavigationEventMap;
NavigationList: {
[RouteName in keyof ParamList]: MyNavigationProp<
ParamList,
RouteName,
NavigatorID
>;
};
Navigator: typeof TabNavigator;
};
export function createMyNavigator<
const ParamList extends ParamListBase,
const NavigatorID extends string | undefined = string | undefined,
const TypeBag extends NavigatorTypeBagBase = MyTabTypeBag<
ParamList,
NavigatorID
>,
const Config extends StaticConfig<TypeBag> = StaticConfig<TypeBag>,
>(config?: Config): TypedNavigator<TypeBag, Config> {
return createNavigatorFactory(TabNavigator)(config);
}
export interface MyTabTypeBag extends NavigatorTypeBagBase {
State: TabNavigationState<this['ParamList']>;
ScreenOptions: MyNavigationOptions;
EventMap: MyNavigationEventMap;
ActionHelpers: TabActionHelpers<this['ParamList']>;
Navigator: typeof TabNavigator;
}
export const createMyNavigator =
createNavigatorFactory<MyTabTypeBag>(TabNavigator);
export const createMyScreen = createScreenFactory<MyTabTypeBag>();
See Custom navigators documentation for more details.
Shared paths for deep links
It's common pattern to have the same screen appear in multiple navigators. For example, a Profile screen may appear in both a Feed stack and a Search stack under a tab navigator.
Previously, this would require you to define two different paths for the same screen, which is not ideal. Now the same path can be used for the Profile screen in both stacks.
When using static configuration with automatic path generation, React Navigation detects shared paths automatically when the same screen component or navigator reference appears in multiple branches with the same full path pattern:
const Profile = createNativeStackScreen({
screen: ProfileScreen,
linking: 'profile/:id',
});
const FeedStack = createNativeStackNavigator({
screens: {
Feed: FeedScreen,
Profile,
},
});
const SearchStack = createNativeStackNavigator({
screens: {
Search: SearchScreen,
Profile,
},
});
You can also mark a path as shared manually:
const Profile = createNativeStackScreen({
screen: ProfileScreen,
linking: {
path: 'profile/:id',
shared: true,
},
});
This is also supported in dynamic config API's linking configuration:
const linking = {
config: {
screens: {
FeedStack: {
screens: {
Feed: 'feed',
Profile: {
path: 'profile/:id',
shared: true,
},
},
},
SearchStack: {
screens: {
Search: 'search',
Profile: {
path: 'profile/:id',
shared: true,
},
},
},
},
},
};
When the user opens a deep link with a shared path, React Navigation will automatically navigate in the correct navigator based on where the user is in the app.
See Shared paths documentation for more details.
Improved preloading in stack navigators
Previously, preloaded screens in stack weren't treated as regular screens and had several limitations, e.g. they couldn't receive events from parent navigators, update their params, update options, or render a nested navigator in them.
We have completely reworked how they work. They are now rendered as regular screens, so they can do everything a regular screen can do.
The preload method now also updates a matching preloaded screen instead of adding a duplicate if you preload the same screen again.
Retaining screens in stack navigators
Stack navigators now support retaining screens. When a screen is retained, it's kept rendered even after navigating away, preserving its local state.
A screen can be marked for retention by calling retain(true):
navigation.retain(true);
Now, actions such as goBack, pop, popToTop, and replace remove it from history, but still keep it rendered. It will be brought back to focus once you navigate to it again.
The screen can be unmarked for retention by calling retain(false), which will also unmount it if it's currently retained:
navigation.retain(false);
This can be useful for keeping a frequently used heavy screen in memory, or keeping a screen with a video or audio player rendered for background playback or picture-in-picture mode.
Retaining screens is currently only supported in JS-based Stack Navigator. Native Stack Navigator doesn't support retaining screens yet.
See retain for more details.
Dynamic props with static configuration
One of the common use cases in static configuration is to configure options etc. based on some dynamic state, e.g. screen size, context etc. To make this possible, we have added a new with method to navigators created with static configuration.
The component passed to with receives the Navigator component and can render it with any props supported by the navigator, wrapped in providers, etc.:
const MyDrawer = createDrawerNavigator({
screens: {
Home: HomeScreen,
},
}).with(({ Navigator }) => {
const isLargeScreen = useIsLargeScreen();
return (
<Navigator
screenOptions={{
drawerType: isLargeScreen ? 'permanent' : 'front',
}}
/>
);
});
We have also backported this feature to React Navigation 7. So you can start using it in your existing apps as well.
See Static configuration documentation for more details.
Material 3 design for Material top tabs and tab view
We have updated Material Top Tabs and react-native-tab-view to better align with Material Design 3.
By default, it uses the primary variant for the new look. The secondary variant looks closer to the previous design, so you can still use it if you prefer the old look.
The indicator has been completely reworked to support the new design where it has rounded corners. So it now supports borderRadius, borderTopLeftRadius, borderTopRightRadius, etc. as well which has been a requested for a long time.
See Material Top Tab Navigator documentation and Tab View documentation for more details.
Content transitions with SF Symbols
The SFSymbol component now supports contentTransition for transitions when the symbol name or variable value changes on iOS 17 or later. When the contentTransition prop is set, the symbol will animate to the new value using built-in animations instead of replacing it immediately:
<SFSymbol
name={isConnected ? 'wifi' : 'wifi.slash'}
contentTransition={{
type: 'replace',
magic: true,
}}
/>
See Icons documentation for more details.
Streaming server rendering support
So far, React Navigation's server rendering support relied on the old renderToString API, which doesn't support streaming. We have now reworked the server rendering to support the new renderToPipeableStream API, which allows streaming the HTML to the client.
import { ServerContainer } from '@react-navigation/native/server';
import { renderToPipeableStream } from 'react-dom/server';
function handler(request, response) {
const location = new URL(request.url, 'https://example.org/');
const { pipe } = renderToPipeableStream(
<ServerContainer location={location}>
<App />
</ServerContainer>,
{
onShellReady() {
response.statusCode = 200;
response.setHeader('Content-Type', 'text/html');
pipe(response);
},
}
);
}
See Server rendering documentation for more details.
Standard Navigation for library authors
We worked on a new standard navigation API after discussing with Expo Router maintainers to make it easier for custom navigator authors to write navigators that can work with both React Navigation and Expo Router, or any other navigation library that chooses to support it.
It defines a standard agreed-upon interface that library authors can use to implement their navigators, and then pass this standard navigator to helpers from React Navigation or Expo Router which will translate it to the respective library's API:
export const MyTabNavigator = createStandardNavigator<
MyTabOptions,
MyTabEventMap,
MyTabNavigatorProps
>(({ state, descriptors, actions, emitter }) => {
// Render the navigator UI here
});
We have also backported standard navigation support to React Navigation 7. So you don't need to wait for React Navigation 8 to build navigator supporting both React Navigation and Expo Router.
Checkout the standard-navigation package and our Standard navigator documentation for more details on how to integrate with React Navigation.
Agent skills for upgrading and migration
We have published Agent skills for the following:
- Upgrading React Navigation from 6.x to 7.x and from 7.x to 8.x
- Migrating from dynamic configuration to static configuration for 7.x and 8.x
To use them, you can install them with skills.sh:
npx skills add react-navigation/skills
Check them out at react-navigation/skills. Give them a try and let us know if you have any feedback or suggestions for improvements.
Try it out
If you'd like to try the latest changes, install from the next tag:
npm
yarn
pnpm
bun
npm install @react-navigation/native@next @react-navigation/bottom-tabs@next
yarn add @react-navigation/native@next @react-navigation/bottom-tabs@next
pnpm add @react-navigation/native@next @react-navigation/bottom-tabs@next
bun add @react-navigation/native@next @react-navigation/bottom-tabs@next
If you encounter any issues or have feedback, please let us know on GitHub Issues or GitHub Discussions.
Sponsor us
If React Navigation helps you deliver value to your customers, it'd mean a lot if you could sponsor us. Sponsorships help us move faster toward building the best cross-platform navigation library and continue to provide timely support for bug reports in our GitHub issues.