|
| 1 | +--- |
| 2 | +title: React Navigation 8.0 Alpha |
| 3 | +authors: satya |
| 4 | +tags: [announcement] |
| 5 | +--- |
| 6 | + |
| 7 | +We're excited to announce the first alpha release of React Navigation 8.0. |
| 8 | + |
| 9 | +This release focuses on improved TypeScript types for static configuration, native bottom tabs as the default, and various other improvements and new features. There are many more improvements planned for the final release. |
| 10 | + |
| 11 | +<!--truncate--> |
| 12 | + |
| 13 | +You can read the full list of changes in the [upgrade guide](/docs/8.x/upgrading-from-7.x). Here are some highlights: |
| 14 | + |
| 15 | +## Highlights |
| 16 | + |
| 17 | +### Native Bottom Tabs by default |
| 18 | + |
| 19 | +The Bottom Tab Navigator now uses native implementations by default on iOS and Android based on [`react-native-screens`](https://github.com/software-mansion/react-native-screens). |
| 20 | + |
| 21 | +This lets us provide a native look by default, such as the new liquid glass effect on iOS 26. |
| 22 | + |
| 23 | +<video playsInline autoPlay muted loop style={{ width: '500px', aspectRatio: 3 / 1 }}> |
| 24 | + |
| 25 | + <source src="/assets/blog/8.x/native-bottom-tabs-ios.mp4" /> |
| 26 | +</video> |
| 27 | + |
| 28 | +<video playsInline autoPlay muted loop style={{ width: '500px', aspectRatio: 3 / 1 }}> |
| 29 | + |
| 30 | + <source src="/assets/blog/8.x/native-bottom-tabs-android.mp4" /> |
| 31 | +</video> |
| 32 | + |
| 33 | +We made the native implementation the default because we believe that the default experience should be as close to platform conventions as possible. |
| 34 | + |
| 35 | +However, we still include a custom JS based implementation in order to support Web and more customization options. You can switch to the JS implementation by passing the `implementation` prop as `custom` to the navigator. |
| 36 | + |
| 37 | +See [Bottom Tab Navigator docs](/docs/8.x/bottom-tab-navigator) for more details. |
| 38 | + |
| 39 | +### Ability to get `route`, `navigation`, and state for any parent screen |
| 40 | + |
| 41 | +One of the commonly requested features has been for screens to be able to access the params for parent screens, but this had a few problems: |
| 42 | + |
| 43 | +- Passing down params to child screens may lead to unnecessary re-renders when the parent params change, even when they are not needed by the child screen. |
| 44 | +- Since the param types are defined by the screen itself, having additional parent params would not be compatible with the existing type system. |
| 45 | + |
| 46 | +It was necessary to manually setup React Context to pass down parent params, which was cumbersome. |
| 47 | + |
| 48 | +The new screen name parameter in `useRoute` solves these problems. Now, you can access the parent route and its params directly by specifying the screen name: |
| 49 | + |
| 50 | +```js |
| 51 | +const route = useRoute('Profile'); |
| 52 | + |
| 53 | +// Params for the 'Profile' screen |
| 54 | +console.log(route.params); |
| 55 | +``` |
| 56 | + |
| 57 | +Similarly, you can get the `navigation` object for any parent screen by specifying the screen name in `useNavigation`: |
| 58 | + |
| 59 | +```js |
| 60 | +const navigation = useNavigation('Profile'); |
| 61 | + |
| 62 | +// Navigation object for the 'Profile' screen |
| 63 | +console.log(navigation); |
| 64 | +``` |
| 65 | + |
| 66 | +And you can get the navigation state for any parent screen by specifying the screen name in `useNavigationState`: |
| 67 | + |
| 68 | +```js |
| 69 | +const focusedRoute = useNavigationState( |
| 70 | + 'Profile', |
| 71 | + (state) => state.routes[state.index] |
| 72 | +); |
| 73 | + |
| 74 | +// Focused route for the navigator that contains the 'Profile' screen |
| 75 | +console.log(focusedRoute); |
| 76 | +``` |
| 77 | + |
| 78 | +See [`useRoute`](/docs/8.x/use-route), [`useNavigation`](/docs/8.x/use-navigation), and [`useNavigationState`](/docs/8.x/use-navigation-state) for more details. |
| 79 | + |
| 80 | +### Better TypeScript types for static configuration |
| 81 | + |
| 82 | +In React Navigation 7, we introduced a static API to reduce boilerplate for deep linking and add automatic type inference. However, it still required manual type annotations in some cases and didn't express React Navigation's full capabilities.. So we had more work to do to get to a point that we're happy with. |
| 83 | + |
| 84 | +In this release, we've built upon the static API and reworked the type inference to solve many of these issues. |
| 85 | + |
| 86 | +Hooks like `useNavigation`, `useRoute`, and `useNavigationState` now automatically infer types based on the provided screen name: |
| 87 | + |
| 88 | +```js |
| 89 | +const navigation = useNavigation('Profile'); |
| 90 | + |
| 91 | +// navigation is correctly typed as StackNavigationProp<RootStackParamList, 'Profile'> |
| 92 | +``` |
| 93 | + |
| 94 | +The `navigation` object will now have proper types based on navigator nesting, and will include navigator specific methods such as `openDrawer` for drawer navigators or `push` for stack navigators without requiring manual type annotations. |
| 95 | + |
| 96 | +<video playsInline autoPlay muted loop data-landscape> |
| 97 | + <source src="/assets/blog/8.x/use-navigation.mp4" /> |
| 98 | +</video> |
| 99 | + |
| 100 | +The `useRoute` hook now returns an union of all route types in the project when no screen name is provided, so it can be used in reusable components while still providing type safety. |
| 101 | + |
| 102 | +It will return the appropriate route type when a screen name is specified: |
| 103 | + |
| 104 | +```js |
| 105 | +const route = useRoute('Profile'); |
| 106 | + |
| 107 | +// route is correctly typed as RouteProp<RootStackParamList, 'Profile'> |
| 108 | +``` |
| 109 | + |
| 110 | +<video playsInline autoPlay muted loop data-landscape> |
| 111 | + <source src="/assets/blog/8.x/use-route.mp4" /> |
| 112 | +</video> |
| 113 | + |
| 114 | +Similarly, the `useNavigationState` hook will infer the correct state type for the navigator that contains the specified screen: |
| 115 | + |
| 116 | +```js |
| 117 | +const focusedRoute = useNavigationState( |
| 118 | + 'Profile', |
| 119 | + (state) => state.routes[state.index] |
| 120 | +); |
| 121 | + |
| 122 | +// state is correctly typed as StackNavigationState<RootStackParamList> |
| 123 | +``` |
| 124 | + |
| 125 | +In addition, previously, the type of the `route` object couldn't be inferred in screen callbacks, listener callbacks, etc. This made it difficult to use route params in these callbacks. |
| 126 | + |
| 127 | +The new `createXScreen` helper functions address this: |
| 128 | + |
| 129 | +```js |
| 130 | +const Stack = createStackNavigator({ |
| 131 | + screens: { |
| 132 | + Profile: createStackScreen({ |
| 133 | + screen: ProfileScreen, |
| 134 | + options: ({ route }) => { |
| 135 | + const userId = route.params.userId; |
| 136 | + |
| 137 | + return { |
| 138 | + title: `${userId}'s profile` |
| 139 | + }; |
| 140 | + }, |
| 141 | + }); |
| 142 | + } |
| 143 | +}); |
| 144 | +``` |
| 145 | + |
| 146 | +Here, the type of `route.params` is correctly inferred based on the type annotation of `ProfileScreen`. |
| 147 | + |
| 148 | +Not only that, but it also infers types based on the path pattern in the `linking` configuration specified for the screen: |
| 149 | + |
| 150 | +```js |
| 151 | +const Stack = createStackNavigator({ |
| 152 | + screens: { |
| 153 | + Profile: createStackScreen({ |
| 154 | + screen: ProfileScreen, |
| 155 | + linking: { |
| 156 | + path: 'profile/:userId', |
| 157 | + parse: { |
| 158 | + userId: (userId) => Number(userId), |
| 159 | + }, |
| 160 | + }, |
| 161 | + }); |
| 162 | + } |
| 163 | +}); |
| 164 | +``` |
| 165 | + |
| 166 | +In this case, React Navigation can automatically infer that `userId` is a param of type `number` based on `:userId` in the path pattern and the return type of `userId` in the `parse` config. This is inspired by how [TanStack Router infers types based on the URL pattern](https://tanstack.com/router/latest/docs/framework/solid/decisions-on-dx#declaring-the-router-instance-for-type-inference). |
| 167 | + |
| 168 | +<video playsInline autoPlay muted loop data-landscape> |
| 169 | + <source src="/assets/blog/8.x/params-types.mp4" /> |
| 170 | +</video> |
| 171 | + |
| 172 | +Each navigator exports its own helper function, e.g. `createNativeStackScreen` for Native Stack Navigator, `createBottomTabScreen` for Bottom Tab Navigator, `createDrawerScreen` for Drawer Navigator etc. |
| 173 | + |
| 174 | +With all of these improvements, it's technically possible to write an app without any manual type annotations for React Navigation, since we can infer the route type from path pattern and param parsing logic, and return correct type for `navigation` object, `route` object, and navigation state based on the screen name and navigation structure automatically. |
| 175 | + |
| 176 | +See [TypeScript docs](/docs/8.x/typescript) and [Static configuration docs](/docs/8.x/static-configuration) for more details. |
| 177 | + |
| 178 | +### Pushing history entries without pushing new screens |
| 179 | + |
| 180 | +Traditionally, the only way to add a new entry to the history stack was by pushing a new screen. But it's not always desirable, as it adds an entirely new instance of the screen component and shows transition animations. |
| 181 | + |
| 182 | +For many scenarios, we may want to add a new history entry without pushing a new screen. Such as: |
| 183 | + |
| 184 | +- A product listing page with filters, where changing filters should create a new history entry so that users can go back to previous filter states. |
| 185 | +- A screen with a custom modal component, where the modal is not a separate screen in the navigator, but its state should be reflected in the URL and history. |
| 186 | + |
| 187 | +The new `pushParams` API makes this possible. You can now push an entry to the history stack by adding new params without needing to push a new screen. Then the back button will update the screen to the previous params instead of going back a screen. |
| 188 | + |
| 189 | +<video playsInline autoPlay muted loop data-landscape style={{ '--ifm-global-radius': '10px' }}> |
| 190 | + |
| 191 | + <source src="/assets/blog/8.x/push-params.mp4" /> |
| 192 | +</video> |
| 193 | + |
| 194 | +This is especially important on the web, where users expect that changing certain UI states should create a new history entry, so that they can use the browser back and forward buttons to navigate through these states. |
| 195 | + |
| 196 | +See [`pushParams` docs](/docs/8.x/navigation-object#pushparams) for more details. |
| 197 | + |
| 198 | +### Replacing params |
| 199 | + |
| 200 | +Previously, the only way to update screen params was via the `setParams` action. It took an object containing the params and merged them with the existing params - originally inspired by the `this.setState` API in React class components. |
| 201 | + |
| 202 | +But it's not always desirable to merge params. So the new `replaceParams` action lets you replace params entirely. |
| 203 | + |
| 204 | +See [`replaceParams` docs](/docs/8.x/navigation-object#replaceparams) for more details. |
| 205 | + |
| 206 | +### Support for `PlatformColor`, `DynamicColorIOS` and CSS custom properties in theme colors |
| 207 | + |
| 208 | +Previously, React Navigation's theming system only supported string color values. In this release, we've added support for platform-specific dynamic colors such as `PlatformColor` and `DynamicColorIOS` on native, as well as CSS custom properties on the web. |
| 209 | + |
| 210 | +This makes it easier to use system colors as well as share colors across native components and React Navigation components. |
| 211 | + |
| 212 | +```js |
| 213 | +const MyTheme = { |
| 214 | + ...DefaultTheme, |
| 215 | + colors: Platform.select({ |
| 216 | + ios: () => ({ |
| 217 | + primary: PlatformColor('systemRed'), |
| 218 | + background: PlatformColor('systemGroupedBackground'), |
| 219 | + // ... |
| 220 | + }), |
| 221 | + android: () => ({ |
| 222 | + primary: PlatformColor('@android:color/system_primary_light'), |
| 223 | + // ... |
| 224 | + }), |
| 225 | + default: () => DefaultTheme.colors, |
| 226 | + })(), |
| 227 | +}; |
| 228 | +``` |
| 229 | + |
| 230 | +However, there's one limitation: with string colors, React Navigation can automatically adjust colors in some scenarios (e.g. adjust the text color based on background color), which is not possible with dynamic colors. So it will fallback to pre-defined colors according to the theme in these cases. |
| 231 | + |
| 232 | +See [Themes docs](/docs/8.x/themes) for more details. |
| 233 | + |
| 234 | +## Try it out |
| 235 | + |
| 236 | +If you'd like to try it out, add `@alpha` to the package you're installing. For example: |
| 237 | + |
| 238 | +```sh npm2yarn |
| 239 | +npm install @react-navigation/native@alpha @react-navigation/bottom-tabs@alpha |
| 240 | +``` |
| 241 | + |
| 242 | +Your feedback is very important to us to ensure a smooth final release. If you encounter any issues or have any feedback or suggestions, please let us know on [GitHub issues](https://github.com/react-navigation/react-navigation/issues) or our [GitHub Discussions forum](https://github.com/react-navigation/react-navigation/discussions). |
| 243 | + |
| 244 | +## Special thanks |
| 245 | + |
| 246 | +React Navigation 8 would not have been possible without our amazing contributors. |
| 247 | + |
| 248 | +Thanks a lot to [MichaΕ Osadnik](https://x.com/mosdnk), [Kacper Kafara](https://x.com/kafara_kacper), [Krzysztof Ligarski](https://github.com/kligarski), [Tomasz BoroΕ](https://github.com/t0maboro), [Konrad Michalik](https://github.com/kmichalikk), [Oskar KwaΕniewski](https://github.com/okwasniewski) and many others for their contributions to this release. |
| 249 | + |
| 250 | +## Sponsor us |
| 251 | + |
| 252 | +If React Navigation helps you to deliver value to your customers, it'd mean a lot if you could sponsor us. Sponsorships will help us to move more quickly towards our goal of building the best cross-platform navigation library and continue to provide timely support for bug reports in our GitHub issues. |
| 253 | + |
| 254 | +π [Visit our GitHub Sponsors page](https://github.com/sponsors/react-navigation) π |
0 commit comments