diff --git a/blog/2025-12-03-react-navigation-8.0-alpha.md b/blog/2025-12-03-react-navigation-8.0-alpha.md new file mode 100644 index 00000000000..34f61460572 --- /dev/null +++ b/blog/2025-12-03-react-navigation-8.0-alpha.md @@ -0,0 +1,255 @@ +--- +title: React Navigation 8.0 Alpha +authors: satya +tags: [announcement] +--- + +We're excited to announce the first alpha release of React Navigation 8.0. + +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. + + + +You can read the full list of changes in the [upgrade guide](/docs/8.x/upgrading-from-7.x). Here are some highlights: + +## Highlights + +### Native Bottom Tabs by default + +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). + +This lets us provide a native look by default, such as the new liquid glass effect on iOS 26. + + + + + +We made the native implementation the default because we believe that the default experience should be as close to platform conventions as possible. + +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. + +See [Bottom Tab Navigator docs](/docs/8.x/bottom-tab-navigator) for more details. + +### Ability to get `route`, `navigation`, and state for any parent screen + +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: + +- 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. +- Since the param types are defined by the screen itself, having additional parent params would not be compatible with the existing type system. + +It was necessary to manually setup React Context to pass down parent params, which was cumbersome. + +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: + +```js +const route = useRoute('Profile'); + +// Params for the 'Profile' screen +console.log(route.params); +``` + +Similarly, you can get the `navigation` object for any parent screen by specifying the screen name in `useNavigation`: + +```js +const navigation = useNavigation('Profile'); + +// Navigation object for the 'Profile' screen +console.log(navigation); +``` + +And you can get the navigation state for any parent screen by specifying the screen name in `useNavigationState`: + +```js +const focusedRoute = useNavigationState( + 'Profile', + (state) => state.routes[state.index] +); + +// Focused route for the navigator that contains the 'Profile' screen +console.log(focusedRoute); +``` + +See [`useRoute`](/docs/8.x/use-route), [`useNavigation`](/docs/8.x/use-navigation), and [`useNavigationState`](/docs/8.x/use-navigation-state) for more details. + +### Better TypeScript types for static configuration + +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. + +In this release, we've built upon the static API and reworked the type inference to solve many of these issues. + +Hooks like `useNavigation`, `useRoute`, and `useNavigationState` now automatically infer types based on the provided screen name: + +```js +const navigation = useNavigation('Profile'); + +// navigation is correctly typed as StackNavigationProp +``` + +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. + + + +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. + +It will return the appropriate route type when a screen name is specified: + +```js +const route = useRoute('Profile'); + +// route is correctly typed as RouteProp +``` + + + +Similarly, the `useNavigationState` hook will infer the correct state type for the navigator that contains the specified screen: + +```js +const focusedRoute = useNavigationState( + 'Profile', + (state) => state.routes[state.index] +); + +// state is correctly typed as StackNavigationState +``` + +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. + +The new `createXScreen` helper functions address this: + +```js +const Stack = createStackNavigator({ + screens: { + Profile: createStackScreen({ + screen: ProfileScreen, + options: ({ route }) => { + const userId = route.params.userId; + + return { + title: `${userId}'s profile` + }; + }, + }); + } +}); +``` + +Here, the type of `route.params` is correctly inferred based on the type annotation of `ProfileScreen`. + +Not only that, but it also infers types based on the path pattern in the `linking` configuration specified for the screen: + +```js +const Stack = createStackNavigator({ + screens: { + Profile: createStackScreen({ + screen: ProfileScreen, + linking: { + path: 'profile/:userId', + parse: { + userId: (userId) => Number(userId), + }, + }, + }); + } +}); +``` + +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). + + + +Each navigator exports its own helper function, e.g. `createNativeStackScreen` for Native Stack Navigator, `createBottomTabScreen` for Bottom Tab Navigator, `createDrawerScreen` for Drawer Navigator etc. + +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. + +See [TypeScript docs](/docs/8.x/typescript) and [Static configuration docs](/docs/8.x/static-configuration) for more details. + +### Pushing history entries without pushing new screens + +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. + +For many scenarios, we may want to add a new history entry without pushing a new screen. Such as: + +- 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. +- 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. + +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. + + + +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. + +See [`pushParams` docs](/docs/8.x/navigation-object#pushparams) for more details. + +### Support for `PlatformColor`, `DynamicColorIOS` and CSS custom properties in theme colors + +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. + +This makes it easier to use system colors as well as share colors across native components and React Navigation components. + +```js +const MyTheme = { + ...DefaultTheme, + colors: Platform.select({ + ios: () => ({ + primary: PlatformColor('systemRed'), + background: PlatformColor('systemGroupedBackground'), + // ... + }), + android: () => ({ + primary: PlatformColor('@android:color/system_primary_light'), + // ... + }), + default: () => DefaultTheme.colors, + })(), +}; +``` + +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. + +See [Themes docs](/docs/8.x/themes) for more details. + +## Plans for the final release + +This is just the first alpha release of React Navigation 8.0. Some of the plans for the final release include: + +- API to handle insets for navigation elements such as headers and tab bars +- Navigation events in React Navigation Devtools +- Accessibility improvements on Web by utilizing [`inert`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/inert) +- [`Activity`](https://react.dev/reference/react/Activity) integration for inactive screens + +## Try it out + +If you'd like to try it out, add `@alpha` to the package you're installing. For example: + +```sh npm2yarn +npm install @react-navigation/native@alpha @react-navigation/bottom-tabs@alpha +``` + +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). + +## Special thanks + +React Navigation 8 would not have been possible without our amazing contributors. + +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. + +## Sponsor us + +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. + +👉 [Visit our GitHub Sponsors page](https://github.com/sponsors/react-navigation) 👈 diff --git a/src/components/Pre.js b/src/components/Pre.js index 70bf2be0de5..5d8faf8a65f 100644 --- a/src/components/Pre.js +++ b/src/components/Pre.js @@ -152,7 +152,11 @@ export default function Pre({ const version = activeVersion?.name; if (version == null || versions[version] == null) { - throw new Error(`Invalid version: ${version}`); + console.warn( + `No version information found for version "${version}", cannot resolve Snack dependencies automatically.` + ); + + return {children}; } Object.assign( diff --git a/src/css/custom.css b/src/css/custom.css index 04f72074d69..ad288872fc2 100755 --- a/src/css/custom.css +++ b/src/css/custom.css @@ -607,10 +607,6 @@ samp { display: none; } -.markdown li > p { - margin-bottom: 0; -} - .video-player { position: relative; display: inline-block; diff --git a/static/assets/blog/8.x/native-bottom-tabs-android.mp4 b/static/assets/blog/8.x/native-bottom-tabs-android.mp4 new file mode 100644 index 00000000000..c3f7999db7a Binary files /dev/null and b/static/assets/blog/8.x/native-bottom-tabs-android.mp4 differ diff --git a/static/assets/blog/8.x/native-bottom-tabs-ios.mp4 b/static/assets/blog/8.x/native-bottom-tabs-ios.mp4 new file mode 100644 index 00000000000..61532fcf9a7 Binary files /dev/null and b/static/assets/blog/8.x/native-bottom-tabs-ios.mp4 differ diff --git a/static/assets/blog/8.x/params-types.mp4 b/static/assets/blog/8.x/params-types.mp4 new file mode 100644 index 00000000000..a34fd05b107 Binary files /dev/null and b/static/assets/blog/8.x/params-types.mp4 differ diff --git a/static/assets/blog/8.x/push-params.mp4 b/static/assets/blog/8.x/push-params.mp4 new file mode 100644 index 00000000000..d6e95f119a1 Binary files /dev/null and b/static/assets/blog/8.x/push-params.mp4 differ diff --git a/static/assets/blog/8.x/use-navigation.mp4 b/static/assets/blog/8.x/use-navigation.mp4 new file mode 100644 index 00000000000..ded618ee723 Binary files /dev/null and b/static/assets/blog/8.x/use-navigation.mp4 differ diff --git a/static/assets/blog/8.x/use-route.mp4 b/static/assets/blog/8.x/use-route.mp4 new file mode 100644 index 00000000000..5efebbfc791 Binary files /dev/null and b/static/assets/blog/8.x/use-route.mp4 differ diff --git a/versioned_docs/version-8.x/bottom-tab-navigator.md b/versioned_docs/version-8.x/bottom-tab-navigator.md index cfaf4782454..2d4c0d24481 100755 --- a/versioned_docs/version-8.x/bottom-tab-navigator.md +++ b/versioned_docs/version-8.x/bottom-tab-navigator.md @@ -4,10 +4,14 @@ title: Bottom Tabs Navigator sidebar_label: Bottom Tabs --- -A simple tab bar on the bottom of the screen that lets you switch between different routes. Routes are lazily initialized -- their screen components are not mounted until they are first focused. +Bottom Tab Navigator displays a set of screens with a tab bar to switch between them. + + ## Installation @@ -80,6 +84,52 @@ export default function App() { In addition to the [common props](navigator.md#configuration) shared by all navigators, the bottom tab navigator accepts the following additional props: +#### `implementation` + +The implementation to use for rendering the tab bar. Possible values: + +- `'native'` (default) - Uses native bottom tabs on iOS and Android. This allows matching the native design such as liquid glass effect on iOS 26. +- `'custom'` - Uses a JavaScript-based implementation for the tab bar. Use this if you need more customization than what's possible with native tabs. + + + + +```js +createBottomTabNavigator({ + implementation: 'custom', + // ... +}); +``` + + + + +```js +{/* ... */} +``` + + + + +When using native tabs, some options behave differently: + +- `tabBarShowLabel` is replaced with `tabBarLabelVisibilityMode` which accepts: + - `"auto"` (default) + - `"selected"` + - `"labeled"` - same as `tabBarShowLabel: true` + - `"unlabeled"` - same as `tabBarShowLabel: false` +- `tabBarLabel` only accepts a `string` +- `tabBarIcon` accepts a function that returns an icon object + +:::note + +- The `native` implementation uses `UITabBarController` on iOS and `BottomNavigationView` on Android. +- Liquid Glass effect on iOS 26+ requires your app to be built with Xcode 26 or above. +- On Android, at most 5 tabs are supported with `native` implementation. This is a limitation of the underlying native component. +- The `native` implementation requires React Native 0.79 or above. If you're using [Expo](https://expo.dev/), it requires SDK 53 or above. + +::: + #### `backBehavior` This controls what happens when `goBack` is called in the navigator. This includes pressing the device's back button or back gesture on Android. @@ -97,10 +147,14 @@ It supports the following values: Boolean used to indicate whether inactive screens should be detached from the view hierarchy to save memory. This enables integration with [react-native-screens](https://github.com/software-mansion/react-native-screens). Defaults to `true`. +Only supported with `custom` implementation. + #### `tabBar` Function that returns a React element to display as the tab bar. +Only supported with `custom` implementation. + The function receives an object containing the following properties as the argument: - `state` - The state object for the tab navigator. @@ -272,11 +326,61 @@ Generic title that can be used as a fallback for `headerTitle` and `tabBarLabel` #### `tabBarLabel` -Title string of a tab displayed in the tab bar or a function that given `{ focused: boolean, color: string }` returns a React.Node, to display in tab bar. When undefined, scene `title` is used. To hide, see `tabBarShowLabel`. +Title string of a tab displayed in the tab bar. When undefined, scene `title` is used. To hide, see [`tabBarLabelVisibilityMode`](#tabbarlabelvisibilitymode). + +Overrides the label provided by [`tabBarSystemItem`](#tabbarsystemitem) on iOS. + +#### `tabBarSystemItem` + +Uses iOS built-in tab bar items with standard iOS styling and localized titles. Supported values: + +- `bookmarks` +- `contacts` +- `downloads` +- `favorites` +- `featured` +- `history` +- `more` +- `mostRecent` +- `mostViewed` +- `recents` +- `search` +- `topRated` + +Only supported with `native` implementation on iOS. + +The [`tabBarIcon`](#tabbaricon) and [`tabBarLabel`](#tabbarlabel) options will override the icon and label from the system item. If you want to keep the system behavior on iOS, but need to provide icon and label for other platforms, use `Platform.OS` or `Platform.select` to conditionally set `undefined` for `tabBarIcon` and `tabBarLabel` on iOS. + +##### Search tab on iOS 26+ + +The `tabBarSystemItem` option has special styling and behavior when set to `search` on iOS 26+. + +Additionally, when the `search` tab is selected, the tab bar transforms into a search field if the screen in the tab navigator or a nested [native stack navigator](native-stack-navigator.md) has [`headerSearchBarOptions`](native-stack-navigator.md#headersearchbaroptions) configured and the native header is shown with [`headerShown: true`](native-stack-navigator.md#headershown). This won't work if a custom header is provided with the `header` option. + +Example: + +```js +tabBarSystemItem: 'search', +headerShown: true, +headerSearchBarOptions: { + placeholder: 'Search', +}, +``` + + -#### `tabBarShowLabel` +#### `tabBarLabelVisibilityMode` -Whether the tab label should be visible. Defaults to `true`. +The label visibility mode for the tab bar items. Supported values: + +- `auto` - decided based on platform and implementation (default) +- `labeled` - labels are always shown +- `unlabeled` - labels are never shown +- `selected` - labels shown only for selected tab (only supported on Android with `native` implementation) + +Supported on all platforms with `custom` implementation. Only supported on Android with `native` implementation. #### `tabBarLabelPosition` @@ -284,15 +388,29 @@ Whether the label is shown below the icon or beside the icon. By default, the position is chosen automatically based on device width. +Only supported with `custom` implementation. + - `below-icon`: the label is shown below the icon (typical for iPhones) Tab bar label position - below - `beside-icon` the label is shown next to the icon (typical for iPad) Tab bar label position - beside +#### `tabBarAllowFontScaling` + +Whether label font should scale to respect Text Size accessibility settings. Defaults to `true`. + +Only supported with `custom` implementation. + #### `tabBarLabelStyle` -Style object for the tab label. +Style object for the tab label. Supported properties: + +- `fontFamily` +- `fontSize` +- `fontWeight` +- `fontStyle` + Tab bar label style Example: @@ -309,10 +427,85 @@ Example: Function that given `{ focused: boolean, color: string, size: number }` returns a React.Node, to display in the tab bar. +With `native` implementation, you can pass an icon object directly instead of a function. A React element is only supported with `custom` implementation. + +It overrides the icon provided by [`tabBarSystemItem`](#tabbarsystemitem) on iOS. + +The icon can be of following types with `native` implementation: + +- Local image - Supported on iOS and Android + + ```js + tabBarIcon: { + type: 'image', + source: require('./path/to/icon.png'), + } + ``` + + On iOS, you can additionally pass a `tinted` property to control whether the icon should be tinted with the active/inactive color: + + ```js + tabBarIcon: { + type: 'image', + source: require('./path/to/icon.png'), + tinted: false, + } + ``` + + The image is tinted by default. + +- [SF Symbols](https://developer.apple.com/sf-symbols/) name - Supported on iOS + + ```js + tabBarIcon: { + type: 'sfSymbol', + name: 'heart', + } + ``` + +- [Drawable resource](https://developer.android.com/guide/topics/resources/drawable-resource) name - Supported on Android + + ```js + tabBarIcon: { + type: 'drawableResource', + name: 'sunny', + } + ``` + +To render different icons for active and inactive states with `native` implementation, you can use a function: + +```js +tabBarIcon: ({ focused }) => { + return { + type: 'sfSymbol', + name: focused ? 'heart.fill' : 'heart', + }; +}, +``` + +This is only supported on iOS. On Android, the icon specified for inactive state will be used for both active and inactive states. + +To provide different icons for different platforms, you can use [`Platform.select`](https://reactnative.dev/docs/platform-specific-code): + +```js +tabBarIcon: Platform.select({ + ios: { + type: 'sfSymbol', + name: 'heart', + }, + android: { + type: 'drawableResource', + name: 'heart_icon', + }, +}); +``` + #### `tabBarIconStyle` Style object for the tab icon. +Only supported with `custom` implementation. + #### `tabBarBadge` Text to show in a badge on the tab icon. Accepts a `string` or a `number`. @@ -321,7 +514,12 @@ Text to show in a badge on the tab icon. Accepts a `string` or a `number`. #### `tabBarBadgeStyle` -Style for the badge on the tab icon. You can specify a background color or text color here. +Style for the badge on the tab icon. Supported properties: + +- `backgroundColor` +- `color` + +Supported on all platforms with `custom` implementation. Only supported with `native` implementation on Android. Tab bar badge style @@ -338,10 +536,14 @@ Example: Accessibility label for the tab button. This is read by the screen reader when the user taps the tab. It's recommended to set this if you don't have a label for the tab. +Only supported with `custom` implementation. + #### `tabBarButton` Function which returns a React element to render as the tab bar button. It wraps the icon and label. Renders `Pressable` by default. +Only supported with `custom` implementation. + You can specify a custom implementation here: ```js @@ -352,6 +554,8 @@ tabBarButton: (props) => ; ID to locate this tab button in tests. +Only supported with `custom` implementation. + #### `tabBarActiveTintColor` Color for the icon and label in the active tab. @@ -362,27 +566,80 @@ Color for the icon and label in the active tab. Color for the icon and label in the inactive tabs. Tab bar inactive tint color +#### `tabBarActiveIndicatorColor` + +Background color of the active indicator. + +Only supported with `native` implementation on Android. + +#### `tabBarActiveIndicatorEnabled` + +Whether the active indicator should be used. Defaults to `true`. + +Only supported with `native` implementation on Android. + +#### `tabBarRippleColor` + +Color of the ripple effect when pressing a tab. + +Only supported with `native` implementation on Android. + #### `tabBarActiveBackgroundColor` Background color for the active tab. +Only supported with `custom` implementation. + #### `tabBarInactiveBackgroundColor` Background color for the inactive tabs. +Only supported with `custom` implementation. + #### `tabBarHideOnKeyboard` Whether the tab bar is hidden when the keyboard opens. Defaults to `false`. +Only supported with `custom` implementation. + +#### `tabBarVisibilityAnimationConfig` + +Animation config for showing and hiding the tab bar when the keyboard is shown/hidden. + +Only supported with `custom` implementation. + +Example: + +```js +tabBarVisibilityAnimationConfig: { + show: { + animation: 'timing', + config: { + duration: 200, + }, + }, + hide: { + animation: 'timing', + config: { + duration: 100, + }, + }, +}, +``` + #### `tabBarItemStyle` Style object for the tab item container. +Only supported with `custom` implementation. + #### `tabBarStyle` Style object for the tab bar. You can configure styles such as background color here. -To show your screen under the tab bar, you can set the `position` style to absolute: +With `custom` implementation, this accepts any style properties. With `native` implementation, only `backgroundColor` and `shadowColor` (iOS 18 and below) are supported. + +To show your screen under the tab bar, you can set the `position` style to absolute (only with `custom` implementation): ```js @@ -498,10 +761,77 @@ Variant of the tab bar. Available values are: - `uikit` (Default) - The tab bar will be styled according to the iOS UIKit guidelines. - `material` - The tab bar will be styled according to the Material Design guidelines. +Only supported with `custom` implementation. + The `material` variant is currently only supported when the [`tabBarPosition`](#tabbarposition) is set to `left` or `right`. ![Material sidebar](/assets/7.x/bottom-tabs-side-material.png) +#### `tabBarBlurEffect` + +Blur effect applied to the tab bar on iOS 18 and lower when tab screen is selected. + +Supported values: + +- `none` - no blur effect +- `systemDefault` - default blur effect applied by the system +- `extraLight` +- `light` +- `dark` +- `regular` +- `prominent` +- `systemUltraThinMaterial` +- `systemThinMaterial` +- `systemMaterial` +- `systemThickMaterial` +- `systemChromeMaterial` +- `systemUltraThinMaterialLight` +- `systemThinMaterialLight` +- `systemMaterialLight` +- `systemThickMaterialLight` +- `systemChromeMaterialLight` +- `systemUltraThinMaterialDark` +- `systemThinMaterialDark` +- `systemMaterialDark` +- `systemThickMaterialDark` +- `systemChromeMaterialDark` + +Defaults to `systemDefault`. + +Only supported with `native` implementation on iOS 18 and below. + +#### `tabBarControllerMode` + +The display mode for the tab bar. Supported values: + +- `auto` - the system sets the display mode based on the tab's content +- `tabBar` - the system displays the content only as a tab bar +- `tabSidebar` - the tab bar is displayed as a sidebar + +Supported on all platforms with `custom` implementation. By default: + +- `tabBar` is positioned at the bottom +- `tabSidebar` is positioned on the left (LTR) or right (RTL) + +The [`tabBarPosition`](#tabbarposition) option can be used to override this in `custom` implementation. + +Supported on iOS 18 and above with `native` implementation. Not supported on tvOS. + +#### `tabBarMinimizeBehavior` + +The minimize behavior for the tab bar. Supported values: + +- `auto` - resolves to the system default minimize behavior +- `never` - the tab bar does not minimize +- `onScrollDown` - the tab bar minimizes when scrolling down and expands when scrolling back up +- `onScrollUp` - the tab bar minimizes when scrolling up and expands when scrolling back down + +Only supported with `native` implementation on iOS 26 and above. + + + #### `lazy` Whether this screen should render only after the first time it's accessed. Defaults to `true`. Set it to `false` if you want to render the screen on the initial render of the navigator. @@ -525,7 +855,9 @@ Style object for the component wrapping the screen content. ### Header related options -You can find the list of header related options [here](elements.md#header). These [options](screen-options.md) can be specified under `screenOptions` prop of `Tab.Navigator` or `options` prop of `Tab.Screen`. You don't have to be using `@react-navigation/elements` directly to use these options, they are just documented in that page. +The navigator does not show a header by default. It renders a native stack header if `headerShown` is set to `true` in the screen options explicitly, or if a custom header is provided with the `header` option. + +It supports all of the [header related options supported in `@react-navigation/native-stack`](native-stack-navigator.md#header-related-options) apart from the options related to the back button (prefixed with `headerBack`). In addition to those, the following options are also supported in bottom tabs: @@ -587,7 +919,13 @@ This event is fired when the user presses the tab button for the current screen - If the screen for the tab renders a scroll view, you can use [`useScrollToTop`](use-scroll-to-top.md) to scroll it to top - If the screen for the tab renders a stack navigator, a `popToTop` action is performed on the stack -To prevent the default behavior, you can call `event.preventDefault`: +To prevent the default behavior, you can call `event.preventDefault`. + +:::note + +Calling `event.preventDefault` is only supported with the `custom` implementation. The default behavior cannot be prevented with the `native` implementation. + +::: ```js name="Tab Press Event" snack static2dynamic import * as React from 'react'; @@ -659,6 +997,8 @@ By default, tabs are rendered lazily. So if you add a listener inside a screen c This event is fired when the user presses the tab button for the current screen in the tab bar for an extended period. If you have a custom tab bar, make sure to emit this event. +Only supported with the `custom` implementation. + Example: ```js @@ -671,6 +1011,38 @@ React.useEffect(() => { }, [navigation]); ``` +#### `transitionStart` + +This event is fired when a transition animation starts when switching tabs. + +Example: + +```js +React.useEffect(() => { + const unsubscribe = navigation.addListener('transitionStart', (e) => { + // Do something + }); + + return unsubscribe; +}, [navigation]); +``` + +#### `transitionEnd` + +This event is fired when a transition animation ends when switching tabs. + +Example: + +```js +React.useEffect(() => { + const unsubscribe = navigation.addListener('transitionEnd', (e) => { + // Do something + }); + + return unsubscribe; +}, [navigation]); +``` + ### Helpers The tab navigator adds the following methods to the navigation object: @@ -779,6 +1151,12 @@ import { BottomTabBarHeightContext } from '@react-navigation/bottom-tabs'; By default, switching between tabs doesn't have any animation. You can specify the `animation` option to customize the transition animation. +:::note + +Customizing animations are only supported with the `custom` implementation. + +::: + Supported values for `animation` are: - `fade` - Cross-fade animation for the screen transition where the new screen fades in and the old screen fades out. diff --git a/versioned_docs/version-8.x/configuring-links.md b/versioned_docs/version-8.x/configuring-links.md index 7b98cbee23c..835cf623fb2 100644 --- a/versioned_docs/version-8.x/configuring-links.md +++ b/versioned_docs/version-8.x/configuring-links.md @@ -13,7 +13,7 @@ In this guide, we will configure React Navigation to handle external links. This 2. Enable URL integration in browser when using on web 3. Use [``](link.md) or [`useLinkTo`](use-link-to.md) to navigate using paths. -Make sure that you have [configured deep links](deep-linking.md) in your app before proceeding. If you have an Android or iOS app, remember to specify the [`prefixes`](navigation-container.md#linkingprefixes) option. +Make sure that you have [configured deep links](deep-linking.md) in your app before proceeding. @@ -23,20 +23,14 @@ The [`Navigation`](static-configuration.md#createstaticnavigation) component acc ```js import { createStaticNavigation } from '@react-navigation/native'; -// highlight-start -const linking = { - enabled: 'auto' /* Automatically generate paths for all screens */, - prefixes: [ - /* your linking prefixes */ - ], -}; -// highlight-end - function App() { return ( Loading...} /> ); @@ -55,9 +49,6 @@ import { NavigationContainer } from '@react-navigation/native'; // highlight-start const linking = { - prefixes: [ - /* your linking prefixes */ - ], config: { /* configuration for matching screens with paths */ }, @@ -80,19 +71,15 @@ function App() { -When you specify the `linking` prop, React Navigation will handle incoming links automatically. On Android and iOS, it'll use React Native's [`Linking` module](https://reactnative.dev/docs/linking) to handle incoming links, both when the app was opened with the link, and when new links are received when the app is open. On the Web, it'll use the [History API](https://developer.mozilla.org/en-US/docs/Web/API/History_API) to sync the URL with the browser. - -:::warning - -Currently there seems to be bug ([facebook/react-native#25675](https://github.com/facebook/react-native/issues/25675)) which results in it never resolving on Android. We add a timeout to avoid getting stuck forever, but it means that the link might not be handled in some cases. +When you specify the `linking` prop, React Navigation will handle incoming links automatically. -::: +On Android and iOS, it'll use React Native's [`Linking` module](https://reactnative.dev/docs/linking) to handle incoming deep links and universal links, both when the app was opened with the link, and when new links are received when the app is open. On the Web, it'll use the [History API](https://developer.mozilla.org/en-US/docs/Web/API/History_API) to sync the URL with the browser. You can also pass a [`fallback`](navigation-container.md#fallback) prop that controls what's displayed when React Navigation is trying to resolve the initial deep link URL. ## Prefixes -The `prefixes` option can be used to specify custom schemes (e.g. `example://`) as well as host & domain names (e.g. `https://example.com`) if you have configured [Universal Links](https://developer.apple.com/ios/universal-links/) or [Android App Links](https://developer.android.com/training/app-links). +The `prefixes` option can be optionally used to specify custom schemes (e.g. `example://`) as well as host & domain names (e.g. `https://example.com`) if you have configured [Universal Links](https://developer.apple.com/ios/universal-links/) or [Android App Links](https://developer.android.com/training/app-links). For example: @@ -102,7 +89,9 @@ const linking = { }; ``` -Note that the `prefixes` option is not supported on Web. The host & domain names will be automatically determined from the Website URL in the browser. If your app runs only on Web, then you can omit this option from the config. +If not specified, it defaults to `['*']`, which will match any host starting with `http`, `https`, and custom schemes such as `myapp://`. You only need to specify `prefixes` if you're using **Expo Go** or want to restrict the URLs your app handles. + +Note that the `prefixes` option has no effect on Web. The host & domain names will be automatically determined from the Website URL in the browser. ### Multiple subdomains​ diff --git a/versioned_docs/version-8.x/custom-navigators.md b/versioned_docs/version-8.x/custom-navigators.md index e50e11714d4..55d758c3bd7 100755 --- a/versioned_docs/version-8.x/custom-navigators.md +++ b/versioned_docs/version-8.x/custom-navigators.md @@ -4,6 +4,9 @@ title: Custom navigators sidebar_label: Custom navigators --- +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + In essence, a navigator is a React component that takes a set of screens and options, and renders them based on its [navigation state](navigation-state.md), generally with additional UI such as headers, tab bars, or drawers. React Navigation provides a few built-in navigators, but they might not always fit your needs if you want a very custom behavior or UI. In such cases, you can build your own custom navigators using React Navigation's APIs. @@ -19,7 +22,7 @@ The navigator component then uses this state to layout the screens appropriately A very basic example looks like this: ```js -function MyStackNavigator(props) { +function MyNavigator(props) { const { state, descriptors, NavigationContent } = useNavigationBuilder( StackRouter, props @@ -31,14 +34,14 @@ function MyStackNavigator(props) { return {descriptor.render()}; } -export const createMyStackNavigator = createNavigatorFactory(MyStackNavigator); +export const createMyNavigator = createNavigatorFactory(MyNavigator); ``` Now, we have an already working navigator, even though it doesn't do anything special yet. Let's break this down: -- We define a `MyNavigator` component that contains our navigator logic. This is the component that's rendered when you render `` in your app with the `createMyStackNavigator` factory function. +- We define a `MyNavigator` component that contains our navigator logic. This is the component that's rendered when you render the navigator in your app with the `createMyNavigator` factory function. - We use the `useNavigationBuilder` hook and pass it [`StackRouter`](custom-routers.md#built-in-routers), which would make our navigator behave like a stack navigator. Any other router such as `TabRouter`, `DrawerRouter`, or a custom router can be used here as well. - The hook returns the [navigation state](navigation-state.md) in the `state` property. This is the current state of the navigator. There's also a `descriptors` object which contains the data and helpers for each screen in the navigator. - We get the focused route from the state with `state.routes[state.index]` - as `state.index` is the index of the currently focused route in the `state.routes` array. @@ -180,24 +183,27 @@ We can also do `export const createMyNavigator = createNavigatorFactory(MyNaviga Then it can be used like this: -```js +```js static2dynamic +import { createStaticNavigation } from '@react-navigation/native'; import { createMyNavigator } from './myNavigator'; -const My = createMyNavigator(); +const MyTabs = createMyNavigator({ + screens: { + Home: HomeScreen, + Feed: FeedScreen, + }, +}); + +const Navigation = createStaticNavigation(MyTabs); function App() { - return ( - - - - - ); + return ; } ``` ## Type-checking navigators -To type-check navigators, we need to provide 3 types: +To type-check navigators, we need to provide few types: - Type of the props accepted by the view - Type of supported screen options @@ -222,6 +228,7 @@ import { type NavigatorTypeBagBase, type ParamListBase, type StaticConfig, + type StaticScreenConfig, type TabActionHelpers, type TabNavigationState, TabRouter, @@ -337,29 +344,55 @@ function TabNavigator({ tabBarStyle, contentStyle, ...rest }: Props) { ); } -// The factory function with generic types for type-inference +// Types required for type-checking the navigator +type MyTabTypeBag = { + ParamList: ParamList; + State: TabNavigationState; + ScreenOptions: TabNavigationOptions; + EventMap: TabNavigationEventMap; + NavigationList: { + [RouteName in keyof ParamList]: TabNavigationProp; + }; + Navigator: typeof TabNavigator; +}; + +// The factory function with overloads for static and dynamic configuration export function createMyNavigator< const ParamList extends ParamListBase, - const NavigatorID extends string | undefined = undefined, - const TypeBag extends NavigatorTypeBagBase = { - ParamList: ParamList; - NavigatorID: NavigatorID; - State: TabNavigationState; - ScreenOptions: TabNavigationOptions; - EventMap: TabNavigationEventMap; - NavigationList: { - [RouteName in keyof ParamList]: TabNavigationProp< - ParamList, - RouteName, - NavigatorID - >; - }; - Navigator: typeof TabNavigator; - }, - const Config extends StaticConfig = StaticConfig, ->(config?: Config): TypedNavigator { +>(): TypedNavigator, undefined>; +export function createMyNavigator< + const Config extends StaticConfig>, +>( + config: Config +): TypedNavigator>, Config>; +export function createMyNavigator(config?: unknown) { return createNavigatorFactory(TabNavigator)(config); } + +// Helper function for creating screen config with proper types for static configuration +export function createMyScreen< + const Linking extends StaticScreenConfigLinking, + const Screen extends StaticScreenConfigScreen, +>( + config: StaticScreenConfigInput< + Linking, + Screen, + TabNavigationState, + MyNavigationOptions, + MyNavigationEventMap, + MyNavigationProp + > +): StaticScreenConfigResult< + Linking, + Screen, + TabNavigationState, + MyNavigationOptions, + MyNavigationEventMap, + MyNavigationProp +> { + // @ts-expect-error: there is some issue with the generic inference here + return config; +} ``` ## Extending Navigators @@ -376,7 +409,6 @@ import { import { BottomTabView } from '@react-navigation/bottom-tabs'; function BottomTabNavigator({ - id, initialRouteName, children, layout, @@ -388,7 +420,6 @@ function BottomTabNavigator({ }) { const { state, descriptors, navigation, NavigationContent } = useNavigationBuilder(TabRouter, { - id, initialRouteName, children, layout, @@ -442,7 +473,7 @@ const { state, descriptors, navigation, NavigationContent } = Customizing built-in navigators like this is an advanced use case and generally not necessary. Consider alternatives such as: - [`layout`](navigator.md#layout) prop on navigators to add a wrapper around the navigator -- [`UNSTABLE_router`](navigator.md#router) prop on navigators to customize the router behavior +- [`router`](navigator.md#router) prop on navigators to customize the router behavior Also refer to the navigator's documentation to see if any existing API meets your needs. diff --git a/versioned_docs/version-8.x/drawer-navigator.md b/versioned_docs/version-8.x/drawer-navigator.md index 594f49b0c16..0feb5e44dbb 100644 --- a/versioned_docs/version-8.x/drawer-navigator.md +++ b/versioned_docs/version-8.x/drawer-navigator.md @@ -450,7 +450,7 @@ function MyDrawer() { screenOptions={{ drawerType: isLargeScreen ? 'permanent' : 'back', drawerStyle: isLargeScreen ? null : { width: '100%' }, - overlayColor: 'transparent', + overlayStyle: { backgroundColor: 'transparent' }, }} > {/* Screens */} @@ -483,9 +483,15 @@ Supported values: - `none` -#### `overlayColor` +#### `overlayStyle` -Color overlay to be displayed on top of the content view when drawer gets open. The opacity is animated from `0` to `1` when the drawer opens. +Style for the overlay on top of the content view when drawer gets open. You can use this to customize the overlay color, opacity, and other properties: + +```js +overlayStyle: { + backgroundColor: 'rgba(0, 0, 0, 0.5)', +} +```