diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index f81b36a2e25..b39b448ff43 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -17,5 +17,8 @@ jobs:
- name: Lint markdown
run: yarn markdownlint-cli2
+ - name: Run tests
+ run: yarn test
+
- name: Build pages
run: yarn build
diff --git a/__tests__/rehype-static-to-dynamic.test.mjs b/__tests__/rehype-static-to-dynamic.test.mjs
new file mode 100644
index 00000000000..5f9a30afaae
--- /dev/null
+++ b/__tests__/rehype-static-to-dynamic.test.mjs
@@ -0,0 +1,1002 @@
+import dedent from 'dedent';
+import assert from 'node:assert';
+import { describe, test } from 'node:test';
+import rehypeStaticToDynamic from '../src/plugins/rehype-static-to-dynamic.mjs';
+
+/**
+ * Helper function to create a test tree structure
+ */
+function createTestTree(code) {
+ return {
+ type: 'root',
+ children: [
+ {
+ type: 'element',
+ tagName: 'pre',
+ children: [
+ {
+ type: 'element',
+ tagName: 'code',
+ data: { meta: 'static2dynamic' },
+ children: [
+ {
+ type: 'text',
+ value: code,
+ },
+ ],
+ },
+ ],
+ },
+ ],
+ };
+}
+
+/**
+ * Helper function to extract the transformed code from the tree
+ */
+function extractTransformedCode(tree) {
+ // After transformation, the tree should have TabItem elements
+ const tabsElement = tree.children[0];
+
+ if (!tabsElement || tabsElement.tagName !== 'Tabs') {
+ throw new Error('Expected Tabs element not found');
+ }
+
+ // Find the "Dynamic" tab
+ const dynamicTab = tabsElement.children.find(
+ (child) =>
+ child.type === 'element' &&
+ child.tagName === 'TabItem' &&
+ child.properties?.value === 'dynamic'
+ );
+
+ if (!dynamicTab) {
+ throw new Error('Dynamic tab not found');
+ }
+
+ // Extract the code from the dynamic tab
+ const preElement = dynamicTab.children.find(
+ (child) => child.type === 'element' && child.tagName === 'pre'
+ );
+
+ if (!preElement) {
+ throw new Error('Pre element not found in dynamic tab');
+ }
+
+ const codeElement = preElement.children.find(
+ (child) => child.type === 'element' && child.tagName === 'code'
+ );
+
+ if (!codeElement) {
+ throw new Error('Code element not found');
+ }
+
+ const textNode = codeElement.children.find((child) => child.type === 'text');
+
+ return textNode?.value || '';
+}
+
+describe('rehype-static-to-dynamic', () => {
+ test('basic screen transformation', async () => {
+ const input = dedent /* javascript */ `
+ import { createNativeStackNavigator } from '@react-navigation/native-stack';
+ import { createStaticNavigation } from '@react-navigation/native';
+
+ const RootStack = createNativeStackNavigator({
+ screens: {
+ Home: HomeScreen,
+ Details: DetailsScreen,
+ },
+ });
+
+ const Navigation = createStaticNavigation(RootStack);
+
+ export default function App() {
+ return ;
+ }
+ `;
+
+ const tree = createTestTree(input);
+ const plugin = rehypeStaticToDynamic();
+ await plugin(tree);
+
+ const output = extractTransformedCode(tree);
+
+ const expected = dedent /* javascript */ `
+ import { createNativeStackNavigator } from '@react-navigation/native-stack';
+ import { NavigationContainer } from '@react-navigation/native';
+
+ const Stack = createNativeStackNavigator();
+
+ function RootStack() {
+ return (
+
+
+
+
+ );
+ }
+
+ export default function App() {
+ return (
+
+
+
+ );
+ }
+ `;
+
+ assert.strictEqual(output, expected);
+ });
+
+ test('screen with options', async () => {
+ const input = dedent /* javascript */ `
+ import { createNativeStackNavigator, createNativeStackScreen } from '@react-navigation/native-stack';
+ import { createStaticNavigation } from '@react-navigation/native';
+
+ const RootStack = createNativeStackNavigator({
+ screens: {
+ Home: createNativeStackScreen({
+ screen: HomeScreen,
+ options: { title: 'My Home' },
+ }),
+ },
+ });
+
+ const Navigation = createStaticNavigation(RootStack);
+
+ export default function App() {
+ return ;
+ }
+ `;
+
+ const tree = createTestTree(input);
+ const plugin = rehypeStaticToDynamic();
+ await plugin(tree);
+
+ const output = extractTransformedCode(tree);
+
+ const expected = dedent /* javascript */ `
+ import { createNativeStackNavigator } from '@react-navigation/native-stack';
+ import { NavigationContainer } from '@react-navigation/native';
+
+ const Stack = createNativeStackNavigator();
+
+ function RootStack() {
+ return (
+
+
+
+ );
+ }
+
+ export default function App() {
+ return (
+
+
+
+ );
+ }
+ `;
+
+ assert.strictEqual(output, expected);
+ });
+
+ test('highlight comment on screen property', async () => {
+ const input = dedent /* javascript */ `
+ import { createNativeStackNavigator, createNativeStackScreen } from '@react-navigation/native-stack';
+ import { createStaticNavigation } from '@react-navigation/native';
+
+ const MyStack = createNativeStackNavigator({
+ screens: {
+ Home: createNativeStackScreen({
+ // highlight-next-line
+ screen: HomeScreen,
+ options: { title: 'My Home' },
+ }),
+ Details: createNativeStackScreen({
+ screen: DetailsScreen,
+ }),
+ },
+ });
+
+ const Navigation = createStaticNavigation(MyStack);
+
+ export default function App() {
+ return ;
+ }
+ `;
+
+ const tree = createTestTree(input);
+ const plugin = rehypeStaticToDynamic();
+ await plugin(tree);
+
+ const output = extractTransformedCode(tree);
+
+ const expected = dedent /* javascript */ `
+ import { createNativeStackNavigator } from '@react-navigation/native-stack';
+ import { NavigationContainer } from '@react-navigation/native';
+
+ const Stack = createNativeStackNavigator();
+
+ function MyStack() {
+ return (
+
+
+
+
+ );
+ }
+
+ export default function App() {
+ return (
+
+
+
+ );
+ }
+ `;
+
+ assert.strictEqual(output, expected);
+ });
+
+ test('highlight comment on screen options', async () => {
+ const input = dedent /* javascript */ `
+ import { createNativeStackNavigator, createNativeStackScreen } from '@react-navigation/native-stack';
+ import { createStaticNavigation } from '@react-navigation/native';
+
+ const MyStack = createNativeStackNavigator({
+ screens: {
+ Home: createNativeStackScreen({
+ screen: HomeScreen,
+ // highlight-start
+ options: {
+ title: 'My Home'
+ },
+ // highlight-end
+ }),
+ },
+ });
+
+ const Navigation = createStaticNavigation(MyStack);
+
+ export default function App() {
+ return ;
+ }
+ `;
+
+ const tree = createTestTree(input);
+ const plugin = rehypeStaticToDynamic();
+ await plugin(tree);
+
+ const output = extractTransformedCode(tree);
+
+ const expected = dedent /* javascript */ `
+ import { createNativeStackNavigator } from '@react-navigation/native-stack';
+ import { NavigationContainer } from '@react-navigation/native';
+
+ const Stack = createNativeStackNavigator();
+
+ function MyStack() {
+ return (
+
+
+
+ );
+ }
+
+ export default function App() {
+ return (
+
+
+
+ );
+ }
+ `;
+
+ assert.strictEqual(output, expected);
+ });
+
+ test('highlight-start and highlight-end comments', async () => {
+ const input = dedent /* javascript */ `
+ import { createNativeStackNavigator, createNativeStackScreen } from '@react-navigation/native-stack';
+ import { createStaticNavigation } from '@react-navigation/native';
+
+ const MyStack = createNativeStackNavigator({
+ screens: {
+ // highlight-start
+ Home: createNativeStackScreen({
+ screen: HomeScreen,
+ options: { title: 'My Home' },
+ }),
+ // highlight-end
+ Details: createNativeStackScreen({
+ screen: DetailsScreen,
+ }),
+ },
+ });
+
+ const Navigation = createStaticNavigation(MyStack);
+
+ export default function App() {
+ return ;
+ }
+ `;
+
+ const tree = createTestTree(input);
+ const plugin = rehypeStaticToDynamic();
+ await plugin(tree);
+
+ const output = extractTransformedCode(tree);
+
+ const expected = dedent /* javascript */ `
+ import { createNativeStackNavigator } from '@react-navigation/native-stack';
+ import { NavigationContainer } from '@react-navigation/native';
+
+ const Stack = createNativeStackNavigator();
+
+ function MyStack() {
+ return (
+
+ // highlight-start
+
+ // highlight-end
+
+
+ );
+ }
+
+ export default function App() {
+ return (
+
+
+
+ );
+ }
+ `;
+
+ assert.strictEqual(output, expected);
+ });
+
+ test('navigator props with highlight', async () => {
+ const input = dedent /* javascript */ `
+ import { createNativeStackNavigator, createNativeStackScreen } from '@react-navigation/native-stack';
+ import { createStaticNavigation } from '@react-navigation/native';
+
+ const RootStack = createNativeStackNavigator({
+ // highlight-next-line
+ initialRouteName: 'Home',
+ screens: {
+ Home: createNativeStackScreen({
+ screen: HomeScreen,
+ }),
+ Details: createNativeStackScreen({
+ screen: DetailsScreen,
+ }),
+ },
+ });
+
+ const Navigation = createStaticNavigation(RootStack);
+
+ export default function App() {
+ return ;
+ }
+ `;
+
+ const tree = createTestTree(input);
+ const plugin = rehypeStaticToDynamic();
+ await plugin(tree);
+
+ const output = extractTransformedCode(tree);
+
+ const expected = dedent /* javascript */ `
+ import { createNativeStackNavigator } from '@react-navigation/native-stack';
+ import { NavigationContainer } from '@react-navigation/native';
+
+ const Stack = createNativeStackNavigator();
+
+ function RootStack() {
+ return (
+ // highlight-next-line
+
+
+
+
+ );
+ }
+
+ export default function App() {
+ return (
+
+
+
+ );
+ }
+ `;
+
+ assert.strictEqual(output, expected);
+ });
+
+ test('multiple screens with mixed comments', async () => {
+ const input = dedent /* javascript */ `
+ import { createNativeStackNavigator, createNativeStackScreen } from '@react-navigation/native-stack';
+ import { createStaticNavigation } from '@react-navigation/native';
+
+ const MyStack = createNativeStackNavigator({
+ screens: {
+ // highlight-start
+ Home: createNativeStackScreen({
+ screen: HomeScreen,
+ }),
+ Profile: createNativeStackScreen({
+ screen: ProfileScreen,
+ options: { title: 'User Profile' },
+ }),
+ // highlight-end
+ Details: createNativeStackScreen({
+ screen: DetailsScreen,
+ }),
+ },
+ });
+
+ const Navigation = createStaticNavigation(MyStack);
+
+ export default function App() {
+ return ;
+ }
+ `;
+
+ const tree = createTestTree(input);
+ const plugin = rehypeStaticToDynamic();
+ await plugin(tree);
+
+ const output = extractTransformedCode(tree);
+
+ const expected = dedent /* javascript */ `
+ import { createNativeStackNavigator } from '@react-navigation/native-stack';
+ import { NavigationContainer } from '@react-navigation/native';
+
+ const Stack = createNativeStackNavigator();
+
+ function MyStack() {
+ return (
+
+ // highlight-start
+
+
+ // highlight-end
+
+
+ );
+ }
+
+ export default function App() {
+ return (
+
+
+
+ );
+ }
+ `;
+
+ assert.strictEqual(output, expected);
+ });
+
+ test('groups with screens', async () => {
+ const input = dedent /* javascript */ `
+ import { createNativeStackNavigator, createNativeStackScreen } from '@react-navigation/native-stack';
+ import { createStaticNavigation } from '@react-navigation/native';
+
+ const RootStack = createNativeStackNavigator({
+ groups: {
+ App: {
+ screens: {
+ Home: HomeScreen,
+ Profile: ProfileScreen,
+ },
+ },
+ Modal: {
+ screenOptions: { presentation: 'modal' },
+ screens: {
+ Settings: SettingsScreen,
+ },
+ },
+ },
+ });
+
+ const Navigation = createStaticNavigation(RootStack);
+
+ export default function App() {
+ return ;
+ }
+ `;
+
+ const tree = createTestTree(input);
+ const plugin = rehypeStaticToDynamic();
+ await plugin(tree);
+
+ const output = extractTransformedCode(tree);
+
+ const expected = dedent /* javascript */ `
+ import { createNativeStackNavigator } from '@react-navigation/native-stack';
+ import { NavigationContainer } from '@react-navigation/native';
+
+ const Stack = createNativeStackNavigator();
+
+ function RootStack() {
+ return (
+
+
+
+
+
+
+
+
+
+ );
+ }
+
+ export default function App() {
+ return (
+
+
+
+ );
+ }
+ `;
+
+ assert.strictEqual(output, expected);
+ });
+
+ test('listeners property with highlight', async () => {
+ const input = dedent /* javascript */ `
+ import { createNativeStackNavigator, createNativeStackScreen } from '@react-navigation/native-stack';
+ import { createStaticNavigation } from '@react-navigation/native';
+
+ const MyStack = createNativeStackNavigator({
+ screens: {
+ Home: createNativeStackScreen({
+ screen: HomeScreen,
+ // highlight-start
+ listeners: {
+ focus: () => console.log('focused'),
+ },
+ // highlight-end
+ }),
+ },
+ });
+
+ const Navigation = createStaticNavigation(MyStack);
+
+ export default function App() {
+ return ;
+ }
+ `;
+
+ const tree = createTestTree(input);
+ const plugin = rehypeStaticToDynamic();
+ await plugin(tree);
+
+ const output = extractTransformedCode(tree);
+
+ const expected = dedent /* javascript */ `
+ import { createNativeStackNavigator } from '@react-navigation/native-stack';
+ import { NavigationContainer } from '@react-navigation/native';
+
+ const Stack = createNativeStackNavigator();
+
+ function MyStack() {
+ return (
+
+ console.log('focused'),
+ }}
+ // highlight-end
+ />
+
+ );
+ }
+
+ export default function App() {
+ return (
+
+
+
+ );
+ }
+ `;
+
+ assert.strictEqual(output, expected);
+ });
+
+ test('block comments', async () => {
+ const input = dedent /* javascript */ `
+ import { createNativeStackNavigator, createNativeStackScreen } from '@react-navigation/native-stack';
+ import { createStaticNavigation } from '@react-navigation/native';
+
+ const MyStack = createNativeStackNavigator({
+ screens: {
+ /* some comment */
+ Home: createNativeStackScreen({
+ screen: HomeScreen,
+ }),
+ },
+ });
+
+ const Navigation = createStaticNavigation(MyStack);
+
+ export default function App() {
+ return ;
+ }
+ `;
+
+ const tree = createTestTree(input);
+ const plugin = rehypeStaticToDynamic();
+ await plugin(tree);
+
+ const output = extractTransformedCode(tree);
+
+ const expected = dedent /* javascript */ `
+ import { createNativeStackNavigator } from '@react-navigation/native-stack';
+ import { NavigationContainer } from '@react-navigation/native';
+
+ const Stack = createNativeStackNavigator();
+
+ function MyStack() {
+ return (
+
+ {/* some comment */}
+
+
+ );
+ }
+
+ export default function App() {
+ return (
+
+
+
+ );
+ }
+ `;
+
+ assert.strictEqual(output, expected);
+ });
+
+ test('screenOptions on navigator', async () => {
+ const input = dedent /* javascript */ `
+ import { createNativeStackNavigator } from '@react-navigation/native-stack';
+ import { createStaticNavigation } from '@react-navigation/native';
+
+ const RootStack = createNativeStackNavigator({
+ // highlight-start
+ screenOptions: {
+ headerShown: false,
+ },
+ // highlight-end
+ screens: {
+ Home: HomeScreen,
+ },
+ });
+
+ const Navigation = createStaticNavigation(RootStack);
+
+ export default function App() {
+ return ;
+ }
+ `;
+
+ const tree = createTestTree(input);
+ const plugin = rehypeStaticToDynamic();
+ await plugin(tree);
+
+ const output = extractTransformedCode(tree);
+
+ const expected = dedent /* javascript */ `
+ import { createNativeStackNavigator } from '@react-navigation/native-stack';
+ import { NavigationContainer } from '@react-navigation/native';
+
+ const Stack = createNativeStackNavigator();
+
+ function RootStack() {
+ return (
+
+
+
+ );
+ }
+
+ export default function App() {
+ return (
+
+
+
+ );
+ }
+ `;
+
+ assert.strictEqual(output, expected);
+ });
+
+ test('options callback', async () => {
+ const input = dedent /* javascript */ `
+ import { createNativeStackNavigator, createNativeStackScreen } from '@react-navigation/native-stack';
+ import { createStaticNavigation } from '@react-navigation/native';
+
+ const MyStack = createNativeStackNavigator({
+ screens: {
+ Home: {
+ screen: HomeScreen,
+ // highlight-start
+ options: ({ route }) => ({
+ title: route.params?.name,
+ }),
+ // highlight-end
+ },
+ },
+ });
+
+ const Navigation = createStaticNavigation(MyStack);
+
+ export default function App() {
+ return ;
+ }
+ `;
+
+ const tree = createTestTree(input);
+ const plugin = rehypeStaticToDynamic();
+ await plugin(tree);
+
+ const output = extractTransformedCode(tree);
+
+ const expected = dedent /* javascript */ `
+ import { createNativeStackNavigator } from '@react-navigation/native-stack';
+ import { NavigationContainer } from '@react-navigation/native';
+
+ const Stack = createNativeStackNavigator();
+
+ function MyStack() {
+ return (
+
+ ({
+ title: route.params?.name,
+ })}
+ // highlight-end
+ />
+
+ );
+ }
+
+ export default function App() {
+ return (
+
+
+
+ );
+ }
+ `;
+
+ assert.strictEqual(output, expected);
+ });
+
+ test('options callback with createNativeStackScreen', async () => {
+ const input = dedent /* javascript */ `
+ import { createNativeStackNavigator, createNativeStackScreen } from '@react-navigation/native-stack';
+ import { createStaticNavigation } from '@react-navigation/native';
+
+ const MyStack = createNativeStackNavigator({
+ screens: {
+ Home: createNativeStackScreen({
+ screen: HomeScreen,
+ // highlight-start
+ options: ({ route }) => ({
+ title: route.params?.name,
+ }),
+ // highlight-end
+ }),
+ },
+ });
+
+ const Navigation = createStaticNavigation(MyStack);
+
+ export default function App() {
+ return ;
+ }
+ `;
+
+ const tree = createTestTree(input);
+ const plugin = rehypeStaticToDynamic();
+ await plugin(tree);
+
+ const output = extractTransformedCode(tree);
+
+ const expected = dedent /* javascript */ `
+ import { createNativeStackNavigator } from '@react-navigation/native-stack';
+ import { NavigationContainer } from '@react-navigation/native';
+
+ const Stack = createNativeStackNavigator();
+
+ function MyStack() {
+ return (
+
+ ({
+ title: route.params?.name,
+ })}
+ // highlight-end
+ />
+
+ );
+ }
+
+ export default function App() {
+ return (
+
+
+
+ );
+ }
+ `;
+
+ assert.strictEqual(output, expected);
+ });
+
+ test('drawer navigator', async () => {
+ const input = dedent /* javascript */ `
+ import { createDrawerNavigator } from '@react-navigation/drawer';
+ import { createStaticNavigation } from '@react-navigation/native';
+
+ const Drawer = createDrawerNavigator({
+ screens: {
+ Home: HomeScreen,
+ Profile: ProfileScreen,
+ },
+ });
+
+ const Navigation = createStaticNavigation(Drawer);
+
+ export default function App() {
+ return ;
+ }
+ `;
+
+ const tree = createTestTree(input);
+ const plugin = rehypeStaticToDynamic();
+ await plugin(tree);
+
+ const output = extractTransformedCode(tree);
+
+ const expected = dedent /* javascript */ `
+ import { createDrawerNavigator } from '@react-navigation/drawer';
+ import { NavigationContainer } from '@react-navigation/native';
+
+ const Drawer = createDrawerNavigator();
+
+ function Drawer() {
+ return (
+
+
+
+
+ );
+ }
+
+ export default function App() {
+ return (
+
+
+
+ );
+ }
+ `;
+
+ assert.strictEqual(output, expected);
+ });
+
+ test('mixed screen definitions', async () => {
+ const input = dedent /* javascript */ `
+ import { createNativeStackNavigator, createNativeStackScreen } from '@react-navigation/native-stack';
+ import { createStaticNavigation } from '@react-navigation/native';
+
+ const MyStack = createNativeStackNavigator({
+ screens: {
+ Home: HomeScreen,
+ Profile: createNativeStackScreen({
+ screen: ProfileScreen,
+ options: { title: 'User Profile' },
+ }),
+ Settings: SettingsScreen,
+ },
+ });
+
+ const Navigation = createStaticNavigation(MyStack);
+
+ export default function App() {
+ return ;
+ }
+ `;
+
+ const tree = createTestTree(input);
+ const plugin = rehypeStaticToDynamic();
+ await plugin(tree);
+
+ const output = extractTransformedCode(tree);
+
+ const expected = dedent /* javascript */ `
+ import { createNativeStackNavigator } from '@react-navigation/native-stack';
+ import { NavigationContainer } from '@react-navigation/native';
+
+ const Stack = createNativeStackNavigator();
+
+ function MyStack() {
+ return (
+
+
+
+
+
+ );
+ }
+
+ export default function App() {
+ return (
+
+
+
+ );
+ }
+ `;
+
+ assert.strictEqual(output, expected);
+ });
+});
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..afeee5c1661
--- /dev/null
+++ b/blog/2025-12-03-react-navigation-8.0-alpha.md
@@ -0,0 +1,224 @@
+---
+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. And 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 to support the new liquid glass effect on iOS 26 and provide a more native feel by default. Making it default also helps with adoption.
+
+
+
+
+
+We also 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 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
+
+Hooks such as `useRoute`, `useNavigation`, and `useNavigationState` now accept a screen to get the corresponding `route`, `navigation`, and state respectively for any parent screen The `navigation` object already had a `getParent` method, so this is not a new capability for `useNavigation`, but it was not possible for `useRoute` and `useNavigationState` before.
+
+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 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');
+
+console.log(route.params); // Params for the 'Profile' screen
+```
+
+Similarly, you can get the `navigation` object for any parent screen:
+
+```js
+const navigation = useNavigation('Profile');
+
+console.log(navigation); // Navigation object for the 'Profile' screen
+```
+
+And you can get the navigation state for any parent screen:
+
+```js
+const focusedRoute = useNavigationState(
+ 'Profile',
+ (state) => state.routes[state.index]
+);
+
+console.log(focusedRoute); // Focused route for the navigator which contains the 'Profile' screen
+```
+
+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 for configuring navigators in order to improve deep linking and add automatic type inference. However, the type inference was limited and did not express the full capabilities of React Navigation without manual type annotations in some cases. We've reworked the types 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.
+
+Similarly, `useRoute` will return the correct route type with properly typed params:
+
+```js
+const route = useRoute('Profile');
+
+// route is correctly typed as RouteProp
+```
+
+And `useNavigationState` will infer the correct state type for 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 `route` object can't be inferred in screen callback, listeners callback etc. This made it difficult to use route params in these callbacks.
+
+The new `createXScreen` helper functions addresses 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.
+
+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
+
+One of the things React Navigation lacked was to add a new history entry without pushing a new screen. But this is not always desirable, as pushing a new screen adds an entirely new instance of the screen component, and shows transition animations.
+
+This is useful for many scenarios:
+
+- 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.
+
+Especially on the web, 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.
+
+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.
+
+See [`pushParams` docs](/docs/8.x/navigation-object#pushparams) for more details.
+
+### Replacing params
+
+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. But removing params required setting them to `undefined`, which was not very intuitive.
+
+The new `replaceParams` action replaces params entirely instead of merging them. This makes it easier to use a new set of params without needing to worry about removing old params.
+
+See [`replaceParams` docs](/docs/8.x/navigation-object#replaceparams) for more details.
+
+### Support for `PlatformColor`, `DynamicColorIOS` and CSS custom properties in theme colors
+
+React Navigation has its own theming system to change styling for the built-in components. Previously, it 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,
+ })(),
+};
+```
+
+This comes with 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.
+
+## 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), [Krzystof 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/docusaurus.config.js b/docusaurus.config.js
index d115dc6962f..4cecc4974f9 100755
--- a/docusaurus.config.js
+++ b/docusaurus.config.js
@@ -4,9 +4,6 @@ import rehypeStaticToDynamic from './src/plugins/rehype-static-to-dynamic.mjs';
import rehypeVideoAspectRatio from './src/plugins/rehype-video-aspect-ratio.mjs';
export default {
- future: {
- experimental_faster: true,
- },
title: 'React Navigation',
tagline: 'Routing and navigation for your React Native apps',
url: 'https://reactnavigation.org/',
@@ -14,13 +11,14 @@ export default {
favicon: 'img/favicon.ico',
organizationName: 'react-navigation',
projectName: 'react-navigation.github.io',
+ onBrokenLinks: 'throw',
onBrokenAnchors: 'throw',
- onBrokenMarkdownLinks: 'throw',
- scripts: [
- '/js/snack-helpers.js',
- '/js/toc-fixes.js',
- '/js/video-playback.js',
- ],
+ onDuplicateRoutes: 'throw',
+ markdown: {
+ hooks: {
+ onBrokenMarkdownLinks: 'throw',
+ },
+ },
themeConfig: {
colorMode: {
defaultMode: 'light',
@@ -146,6 +144,11 @@ export default {
'https://github.com/react-navigation/react-navigation.github.io/edit/main/',
includeCurrentVersion: false,
lastVersion: '7.x',
+ versions: {
+ '7.x': {
+ badge: false,
+ },
+ },
breadcrumbs: false,
sidebarCollapsed: false,
remarkPlugins: [[remarkNpm2Yarn, { sync: true }]],
@@ -173,4 +176,9 @@ export default {
},
],
],
+ scripts: [
+ '/js/snack-helpers.js',
+ '/js/toc-fixes.js',
+ '/js/video-playback.js',
+ ],
};
diff --git a/package.json b/package.json
index dc1b26442d5..9a66d24a6c2 100755
--- a/package.json
+++ b/package.json
@@ -9,33 +9,36 @@
"serve": "docusaurus serve",
"swizzle": "docusaurus swizzle",
"deploy": "DEPLOYMENT_BRANCH=gh-pages docusaurus deploy",
+ "test": "node --test --test-reporter=@voxpelli/node-test-pretty-reporter",
"crowdin-upload": "crowdin upload sources --auto-update -b main",
"crowdin-download": "crowdin download -b main",
"fetch-sponsors": "node scripts/fetch-sponsors.js && prettier --write src/data/sponsors.js"
},
"dependencies": {
- "@docusaurus/core": "3.6.1",
- "@docusaurus/faster": "3.6.1",
- "@docusaurus/plugin-client-redirects": "3.6.1",
- "@docusaurus/plugin-google-analytics": "3.6.1",
- "@docusaurus/preset-classic": "3.6.1",
- "@docusaurus/remark-plugin-npm2yarn": "3.6.1",
- "@octokit/graphql": "^7.1.0",
- "@react-navigation/core": "^7.0.4",
+ "@docusaurus/core": "3.9.2",
+ "@docusaurus/faster": "3.9.2",
+ "@docusaurus/plugin-client-redirects": "3.9.2",
+ "@docusaurus/plugin-google-analytics": "3.9.2",
+ "@docusaurus/preset-classic": "3.9.2",
+ "@docusaurus/remark-plugin-npm2yarn": "3.9.2",
+ "@octokit/graphql": "^9.0.3",
+ "@react-navigation/core": "^7.13.5",
+ "dedent": "^1.7.0",
"escape-html": "^1.0.3",
"mkdirp": "^3.0.1",
"netlify-plugin-cache": "^1.0.3",
- "prism-react-renderer": "^2.4.0",
- "react": "^18.3.1",
- "react-dom": "^18.3.1",
+ "prism-react-renderer": "^2.4.1",
+ "react": "^19.2.1",
+ "react-dom": "^19.2.1",
"react-simple-code-editor": "^0.14.1"
},
"devDependencies": {
"@babel/types": "^7.28.5",
"@ffprobe-installer/ffprobe": "^2.1.2",
- "markdownlint": "^0.36.1",
- "markdownlint-cli2": "^0.14.0",
- "prettier": "^3.6.2",
+ "@voxpelli/node-test-pretty-reporter": "^1.1.2",
+ "markdownlint": "^0.40.0",
+ "markdownlint-cli2": "^0.19.1",
+ "prettier": "^3.7.4",
"recast": "^0.23.11"
},
"resolutions": {
diff --git a/src/components/Pre.js b/src/components/Pre.js
index 227c15c070b..aaeb07c9c5a 100644
--- a/src/components/Pre.js
+++ b/src/components/Pre.js
@@ -2,9 +2,9 @@ import { useActiveVersion } from '@docusaurus/plugin-content-docs/client';
import { useColorMode } from '@docusaurus/theme-common';
import { usePluginData } from '@docusaurus/useGlobalData';
import MDXPre from '@theme-original/MDXComponents/Pre';
-import React from 'react';
-import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
+import Tabs from '@theme/Tabs';
+import React from 'react';
const SUPPORTED_TABS = {
config: [
@@ -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 271a01d95d1..9305af354f5 100755
--- a/src/css/custom.css
+++ b/src/css/custom.css
@@ -14,14 +14,18 @@
ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono,
Courier New, monospace;
--ifm-font-family-base:
- ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, Segoe UI,
- Roboto, Helvetica Neue, Arial, Noto Sans, sans-serif, Apple Color Emoji,
- Segoe UI Emoji, Segoe UI Symbol, Noto Color Emoji;
-
+ -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue',
+ Arial, sans-serif;
+ --ifm-heading-font-family:
+ -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue',
+ Arial, sans-serif;
+
+ --ifm-font-size-base: 100%;
+ --ifm-line-height-base: 1.75;
--ifm-code-font-size: 85%;
--ifm-heading-font-weight: 700;
- --ifm-heading-line-height: 1.618;
+ --ifm-heading-line-height: 1.25;
--ifm-heading-margin-top: 1.618rem;
--ifm-color-primary: hsl(
@@ -78,14 +82,18 @@
--ifm-toc-border-color: var(--ifm-color-gray-200);
--ifm-menu-color-background-active: rgba(107, 82, 174, 0.1);
- --docusaurus-highlighted-code-line-bg: rgba(107, 82, 174, 0.1);
--ifm-footer-padding-horizontal: var(--ifm-spacing-horizontal);
--ifm-footer-padding-vertical: var(--ifm-spacing-vertical);
--ifm-tabs-padding-vertical: 0.375rem;
--ifm-alert-shadow: none;
- --ifm-alert-border-left-width: 0;
+ --ifm-alert-border-width: 1px;
+ --ifm-alert-border-left-width: 1px;
+
+ --ifm-code-padding-horizontal: 0.3em;
+
+ --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.07);
--codeblock-background-color: #f6f8fa;
}
@@ -143,51 +151,101 @@
}
h1 {
- font-size: 2.618rem;
- letter-spacing: -0.02em;
+ font-family: var(--ifm-heading-font-family);
+ font-size: 2.75rem;
+ letter-spacing: -0.022em;
margin-top: 0;
- margin-left: -0.09rem;
+ margin-bottom: 0.5rem;
+ margin-left: -0.05rem;
+ font-weight: 700;
+ line-height: 1.15;
}
h2 {
- font-size: 1.618rem;
- letter-spacing: -0.02em;
- margin-left: -0.09rem;
+ font-family: var(--ifm-heading-font-family);
+ font-size: 2rem;
+ letter-spacing: -0.018em;
+ margin-top: 2.25rem;
+ margin-bottom: 0.5rem;
+ margin-left: -0.04rem;
+ font-weight: 700;
+ line-height: 1.22;
+ border-bottom: none;
+ padding-bottom: 0;
}
h3 {
- font-size: 1.382rem;
- letter-spacing: -0.01em;
+ font-family: var(--ifm-heading-font-family);
+ font-size: 1.5rem;
+ letter-spacing: -0.014em;
+ margin-top: 2rem;
+ margin-bottom: 0.5rem;
+ font-weight: 700;
+ line-height: 1.3;
}
h4 {
- font-size: 1.145rem;
+ font-family: var(--ifm-heading-font-family);
+ font-size: 1.25rem;
+ letter-spacing: -0.012em;
+ margin-top: 1.75rem;
+ margin-bottom: 0.5rem;
+ font-weight: 700;
+ line-height: 1.35;
}
h5 {
- font-size: 1rem;
+ font-family: var(--ifm-heading-font-family);
+ font-size: 1.125rem;
+ letter-spacing: -0.01em;
+ margin-top: 1.5rem;
+ margin-bottom: 0.5rem;
+ font-weight: 700;
+ line-height: 1.4;
}
h6 {
- font-size: 0.85rem;
+ font-family: var(--ifm-heading-font-family);
+ font-size: 1rem;
+ letter-spacing: -0.008em;
+ margin-top: 1.25rem;
+ margin-bottom: 0.5rem;
+ font-weight: 700;
+ line-height: 1.45;
}
p {
font-size: 1rem;
line-height: 1.75;
+ margin-bottom: 1.5rem;
+ letter-spacing: -0.003em;
+}
+
+article p {
+ font-size: 1rem;
+ line-height: 1.75;
+ color: var(--ifm-font-color-base);
+ margin-bottom: 1.5rem;
+ letter-spacing: -0.003em;
+}
+
+.blog-wrapper .container {
+ max-width: 100%;
+ padding: 0 1.25rem;
}
@media (min-width: 90rem) {
.main-wrapper:not(.full-width),
.navbar__inner,
- .footer .container {
+ .footer .container,
+ .blog-wrapper .container {
max-width: 90rem;
margin: auto;
}
}
@media (min-width: 85rem) {
- .main-wrapper:not(.full-width) main {
+ .main-wrapper:not(.full-width) main:not(.blog-list-page main) {
padding: 2rem 0 2rem 2rem;
}
@@ -197,7 +255,7 @@ p {
}
@media (min-width: 95rem) {
- .main-wrapper:not(.full-width) main {
+ .main-wrapper:not(.full-width) main:not(.blog-list-page main) {
padding: 3rem 0 3rem 4rem;
}
@@ -212,6 +270,19 @@ p {
.navbar {
padding: 0;
+ background-color: transparent;
+}
+
+.navbar::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ height: 100%;
+ backdrop-filter: blur(12px) saturate(180%);
+ background-color: rgba(255, 255, 255, 0.7);
+ z-index: -1;
}
.navbar__inner {
@@ -416,7 +487,11 @@ p {
}
& > .tabs {
- box-shadow: inset 0 -2px 0 var(--ifm-toc-border-color);
+ border-width: 1px 1px 0 1px;
+ border-style: solid;
+ border-color: var(--ifm-toc-border-color);
+ border-top-left-radius: var(--ifm-code-border-radius);
+ border-top-right-radius: var(--ifm-code-border-radius);
}
& > .tabs > .tabs__item {
@@ -428,6 +503,11 @@ p {
}
}
+[role='tabpanel'] > .theme-code-block:only-child {
+ border-top-left-radius: 0;
+ border-top-right-radius: 0;
+}
+
.col:has(.table-of-contents) {
padding-left: 0;
}
@@ -455,10 +535,28 @@ p {
transform: scale(0.8);
}
+.theme-code-block {
+ border: 1px solid var(--ifm-toc-border-color);
+}
+
article {
text-wrap: pretty;
}
+article > div > * + h2 {
+ margin-top: 3.5rem;
+}
+
+article > div > * + h3 {
+ margin-top: 3rem;
+}
+
+article h2 + p,
+article h3 + p,
+article h4 + p {
+ margin-top: 1rem;
+}
+
article img,
article video:not(.video-player video),
.video-player {
@@ -558,9 +656,9 @@ samp {
margin-bottom: var(--ifm-leading);
padding: calc(var(--ifm-pre-padding) / 2) var(--ifm-pre-padding);
background-color: var(--codeblock-background-color);
- border-top-width: 1px;
- border-top-style: solid;
- border-top-color: var(--ifm-color-gray-200);
+ border-width: 0 1px 1px 1px;
+ border-style: solid;
+ border-color: var(--ifm-toc-border-color);
border-bottom-left-radius: var(--ifm-pre-border-radius);
border-bottom-right-radius: var(--ifm-pre-border-radius);
}
@@ -619,3 +717,56 @@ pre code:has(.code-block-diff-add-line) {
pre code:has(.code-block-diff-remove-line) {
padding-left: 2em !important;
}
+
+code {
+ border-width: 1px;
+}
+
+.blog-wrapper .container aside nav {
+ margin-top: 2.25rem;
+}
+
+.blog-wrapper article h2 {
+ font-size: 2rem;
+ line-height: 1.3;
+ letter-spacing: -0.02em;
+}
+
+.blog-wrapper article h2 a {
+ color: var(--ifm-font-color-base);
+ text-decoration: none;
+}
+
+.blog-wrapper article h2 a:hover {
+ color: var(--ifm-color-primary);
+}
+
+.blog-wrapper .avatar__photo {
+ margin: calc(var(--ifm-leading) / 2) 0;
+}
+
+.blog-wrapper .avatar__intro {
+ display: grid;
+ grid-template-areas:
+ 'name socials'
+ 'subtitle .';
+ justify-content: start;
+ align-items: center;
+ gap: 0;
+ font-size: 0.875rem;
+ margin: calc(var(--ifm-leading) / 2) 0;
+}
+
+.blog-wrapper .avatar__intro > .avatar__name {
+ font-weight: 500;
+ grid-area: name;
+}
+
+.blog-wrapper .avatar__intro > .avatar__name + small {
+ grid-area: subtitle;
+}
+
+.blog-wrapper .avatar__intro > .avatar__name + small + div {
+ grid-area: socials;
+ margin-left: var(--ifm-leading);
+}
diff --git a/src/plugins/rehype-static-to-dynamic.mjs b/src/plugins/rehype-static-to-dynamic.mjs
index 46ca91ca70c..ffb855a3d9c 100644
--- a/src/plugins/rehype-static-to-dynamic.mjs
+++ b/src/plugins/rehype-static-to-dynamic.mjs
@@ -1,6 +1,17 @@
import * as t from '@babel/types';
+import { readFileSync } from 'fs';
+import { dirname, join } from 'path';
+import * as prettier from 'prettier';
import * as recast from 'recast';
+import * as babelParser from 'recast/parsers/babel-ts.js';
import { visit } from 'unist-util-visit';
+import { fileURLToPath } from 'url';
+
+const __filename = fileURLToPath(import.meta.url);
+const __dirname = dirname(__filename);
+const prettierConfig = JSON.parse(
+ readFileSync(join(__dirname, '..', '..', '.prettierrc.json'), 'utf-8')
+);
/**
* Plugin to automatically convert static config examples to dynamic config
@@ -9,7 +20,10 @@ import { visit } from 'unist-util-visit';
* corresponding dynamic configuration examples wrapped in tabs.
*/
export default function rehypeStaticToDynamic() {
- return (tree) => {
+ return async (tree) => {
+ const promises = [];
+ const replacements = [];
+
visit(tree, 'element', (node, index, parent) => {
// Look for code blocks with static2dynamic in meta
if (
@@ -34,29 +48,69 @@ export default function rehypeStaticToDynamic() {
);
}
- const dynamicCode = convertStaticToDynamic(code);
- const tabsElement = createTabsWithBothConfigs(code, dynamicCode, node);
-
- // Replace the current pre element with the tabs
- parent.children[index] = tabsElement;
+ // Queue async conversion
+ const promise = convertStaticToDynamic(code).then((dynamicCode) => {
+ const tabsElement = createTabsWithBothConfigs(
+ code,
+ dynamicCode,
+ node
+ );
+ replacements.push({ parent, index, tabsElement });
+ });
+ promises.push(promise);
}
});
+
+ // Wait for all conversions to complete
+ await Promise.all(promises);
+
+ // Apply all replacements
+ replacements.forEach(({ parent, index, tabsElement }) => {
+ parent.children[index] = tabsElement;
+ });
};
}
/**
- * Convert static config code to dynamic config code
+ * Convert static config code to dynamic config code.
+ *
+ * Strategy:
+ * 1. Parse code to AST with comment attachment
+ * 2. First pass: Transform imports and collect navigator information
+ * - Remove createStaticNavigation and createXScreen imports
+ * - Add NavigationContainer import if needed
+ * - Collect navigator declarations and their comments
+ * 3. Second pass: Transform navigator declarations
+ * - Create const Stack = createStackNavigator() declarations
+ * - Create function components with JSX (Stack.Navigator, Stack.Screen)
+ * - Track comments for later injection
+ * 4. Format with Prettier
+ * 5. Post-process: Inject comments into formatted code
+ * - Comments are injected as strings since Prettier may move/remove AST comments
*/
-function convertStaticToDynamic(code) {
- // Parse the code into AST using recast
+async function convertStaticToDynamic(code) {
+ // Parse the code into AST using recast with comment attachment enabled
const ast = recast.parse(code, {
- parser: require('recast/parsers/babel-ts'),
+ parser: {
+ parse(source, options) {
+ return babelParser.parse(source, {
+ ...options,
+ tokens: true,
+ attachComment: true,
+ });
+ },
+ },
});
let navigatorInfos = [];
let staticNavigationIndices = [];
- // First pass: collect information and transform imports
+ // Track comments throughout the transformation
+ // We collect comments from the AST during transformation, then inject them
+ // after Prettier formatting (since Prettier may reformat/move AST comments)
+ const commentTracking = new Set();
+
+ // First pass: Collect navigator info and transform imports
recast.visit(ast, {
visitImportDeclaration(path) {
const source = path.node.source.value;
@@ -104,6 +158,22 @@ function convertStaticToDynamic(code) {
}
}
+ // Remove createXScreen imports from navigator packages
+ // e.g., createNativeStackScreen from @react-navigation/native-stack
+ if (source.startsWith('@react-navigation/')) {
+ path.node.specifiers = path.node.specifiers.filter((spec) => {
+ if (t.isImportSpecifier(spec)) {
+ const importedName = spec.imported.name;
+ // Remove imports that match createXScreen pattern
+ return !(
+ importedName.startsWith('create') &&
+ importedName.endsWith('Screen')
+ );
+ }
+ return true;
+ });
+ }
+
this.traverse(path);
},
@@ -160,7 +230,7 @@ function convertStaticToDynamic(code) {
navigatorInfos.length > 0
) {
// Preserve any props passed to Navigation
- const navigationProps = path.node.openingElement.attributes || [];
+ const navigationProps = path.node.openingElement.attributes;
// Use the last navigator (which is passed to createStaticNavigation)
const mainNavigator = navigatorInfos[navigatorInfos.length - 1];
@@ -197,8 +267,10 @@ function convertStaticToDynamic(code) {
},
});
- // Second pass: manually transform the AST body
- // Process all navigators
+ // Second pass: Transform navigator declarations into const + function components
+ // Example: const MyStack = createStackNavigator({ screens: {...} })
+ // becomes: const Stack = createStackNavigator();
+ // function MyStack() { return ... }
if (navigatorInfos.length > 0) {
const replacements = [];
const navigatorConstNames = new Map(); // Track usage of navigator constant names
@@ -214,32 +286,14 @@ function convertStaticToDynamic(code) {
index,
} = navigatorInfo;
- // Extract navigator constant name from the type
- // Get the last word before "Navigator"
- // e.g., "createStackNavigator" -> "Stack"
- // e.g., "createNativeStackNavigator" -> "Stack"
- // e.g., "createBottomTabNavigator" -> "Tab"
- // e.g., "createMaterialTopTabNavigator" -> "Tab"
- const withoutCreate = type.replace(/^create/, ''); // "StackNavigator"
- const withoutNavigator = withoutCreate.replace(/Navigator$/, ''); // "Stack"
- // Find the last capitalized word (e.g., "NativeStack" -> "Stack", "MaterialTopTab" -> "Tab")
- const match = withoutNavigator.match(/([A-Z][a-z]+)$/);
- const baseNavigatorConstName = match ? match[1] : withoutNavigator;
-
- // Handle multiple navigators of the same type by adding suffixes (A, B, C, etc.)
- let navigatorConstName = baseNavigatorConstName;
- const currentCount = navigatorConstNames.get(baseNavigatorConstName) || 0;
-
- if (currentCount > 0) {
- // Add suffix: A for second occurrence, B for third, etc.
- const suffix = String.fromCharCode(65 + currentCount - 1); // 65 is 'A'
- navigatorConstName = baseNavigatorConstName + suffix;
- }
-
- navigatorConstNames.set(baseNavigatorConstName, currentCount + 1);
+ const baseNavigatorConstName = deriveNavigatorConstName(type);
+ const navigatorConstName = getUniqueNavigatorConstName(
+ baseNavigatorConstName,
+ navigatorConstNames
+ );
// Parse the config object
- const parsedConfig = parseNavigatorConfig(config);
+ const parsedConfig = parseNavigatorConfig(config, commentTracking);
// Create: const Stack = createStackNavigator();
const navigatorConstDeclaration = t.variableDeclaration('const', [
@@ -258,12 +312,11 @@ function convertStaticToDynamic(code) {
// Preserve all comments from the original node
if (originalNode.comments && originalNode.comments.length > 0) {
- // Separate leading and trailing comments
+ // Separate leading and trailing comments based on recast markers
const leadingComments = [];
const trailingCommentsFromNode = [];
originalNode.comments.forEach((comment) => {
- // Recast marks comments with leading/trailing properties
if (comment.trailing) {
trailingCommentsFromNode.push(comment);
} else {
@@ -271,37 +324,17 @@ function convertStaticToDynamic(code) {
}
});
- // Attach leading comments to the const declaration
- if (leadingComments.length > 0) {
- // Mark as leading comments for proper placement
- leadingComments.forEach((c) => {
- c.leading = true;
- c.trailing = false;
- });
- navigatorConstDeclaration.comments = leadingComments;
- }
-
- // Attach trailing comments to the function component (after the function body)
- if (trailingCommentsFromNode.length > 0) {
- // Mark as trailing comments for proper placement
- trailingCommentsFromNode.forEach((c) => {
- c.leading = false;
- c.trailing = true;
- });
- navigatorComponent.comments = trailingCommentsFromNode;
- }
+ attachCommentsToNode(navigatorConstDeclaration, leadingComments, false);
+ attachCommentsToNode(
+ navigatorComponent,
+ trailingCommentsFromNode,
+ true
+ );
}
- // Also check for trailingComments property
+ // Attach any additional trailing comments
if (trailingComments && trailingComments.length > 0) {
- trailingComments.forEach((c) => {
- c.leading = false;
- c.trailing = true;
- });
- navigatorComponent.comments = [
- ...(navigatorComponent.comments || []),
- ...trailingComments,
- ];
+ attachCommentsToNode(navigatorComponent, trailingComments, true);
}
// Store the replacement info
@@ -316,7 +349,6 @@ function convertStaticToDynamic(code) {
replacements.sort((a, b) => b.index - a.index);
const programBody = ast.program.body;
- let indexShift = 0;
replacements.forEach(
({ index, navigatorConstDeclaration, navigatorComponent }) => {
@@ -327,14 +359,12 @@ function convertStaticToDynamic(code) {
navigatorConstDeclaration,
navigatorComponent
);
-
- // Track the shift for adjusting staticNavigation indices
- indexShift++;
}
);
// Adjust indices for createStaticNavigation declarations
// Account for the fact that we replaced each navigator (1 node) with 2 nodes
+ // So indices after each replacement need to be shifted by 1
staticNavigationIndices = staticNavigationIndices.map((idx) => {
let shift = 0;
replacements.forEach(({ index }) => {
@@ -344,7 +374,7 @@ function convertStaticToDynamic(code) {
});
}
- // Remove createStaticNavigation declarations (in reverse order to maintain indices)
+ // Remove createStaticNavigation declarations (in reverse to maintain correct indices)
staticNavigationIndices.sort((a, b) => b - a);
staticNavigationIndices.forEach((index) => {
ast.program.body.splice(index, 1);
@@ -357,35 +387,593 @@ function convertStaticToDynamic(code) {
trailingComma: true,
});
- // Parse with Babel to verify syntax
- recast.parse(output.code, {
- parser: require('recast/parsers/babel-ts'),
+ // Format with prettier first
+ let formattedCode = await prettier.format(output.code, {
+ ...prettierConfig,
+ parser: 'babel',
+ singleQuote: true,
+ });
+
+ // Remove trailing newline that prettier adds
+ formattedCode = formattedCode.trimEnd();
+
+ // Post-process: Inject tracked comments into the formatted code
+ // We do this after Prettier to ensure comments aren't moved/removed during formatting
+ // Comments are injected by searching for patterns in the string output
+ commentTracking.forEach((commentObj) => {
+ const {
+ screenName,
+ leadingComments,
+ trailingComments,
+ navigatorProp,
+ screenConfigProperty,
+ } = commentObj;
+
+ // Handle navigator property comments (e.g., screenOptions on Navigator)
+ if (navigatorProp) {
+ const lines = formattedCode.split('\n');
+
+ // Find the property line within a Navigator element context
+ const propLineIndex = findPropertyLine(
+ lines,
+ (line) =>
+ line.includes(navigatorProp) &&
+ (line.includes('=') || line.includes(':')),
+ (line) => line.includes('.Navigator'),
+ 5 // Search 5 lines before/after for context
+ );
+
+ if (propLineIndex !== -1) {
+ const indent = getIndentation(lines[propLineIndex]);
+
+ // Inject leading comments before the property
+ const updatedLineIndex = injectComments(
+ lines,
+ leadingComments,
+ propLineIndex,
+ indent,
+ true // JSX context
+ );
+
+ // Find where the property value ends (}} for objects)
+ const closingIndex = findPropertyClosingLine(lines, updatedLineIndex);
+
+ // Inject trailing comments after the closing
+ injectComments(lines, trailingComments, closingIndex + 1, indent, true);
+
+ formattedCode = lines.join('\n');
+ }
+ return;
+ }
+
+ // Handle screen config property comments (options, listeners, etc.)
+ if (screenConfigProperty) {
+ const lines = formattedCode.split('\n');
+
+ // Map 'screen' property to 'component' in JSX
+ const jsxPropName =
+ screenConfigProperty === 'screen' ? 'component' : screenConfigProperty;
+
+ // Find the property line within the correct Screen element
+ const propLineIndex = findPropertyLine(
+ lines,
+ (line) =>
+ line.includes(`${jsxPropName}=`) || line.includes(`${jsxPropName}:{`),
+ (line) => lineMatchesScreenName(line, screenName),
+ 10 // Search 10 lines before for screen context
+ );
+
+ if (propLineIndex !== -1) {
+ const indent = getIndentation(lines[propLineIndex], ' ');
+
+ // Inject leading comments before the property
+ const updatedLineIndex = injectComments(
+ lines,
+ leadingComments,
+ propLineIndex,
+ indent
+ );
+
+ // Find where the property value ends (}} or })})
+ const closingIndex = findPropertyClosingLine(lines, updatedLineIndex);
+
+ // Inject trailing comments after the closing
+ injectComments(lines, trailingComments, closingIndex + 1, indent);
+
+ formattedCode = lines.join('\n');
+ }
+ return;
+ }
+
+ // Process leading comments for screen elements (highlight-next-line, highlight-start)
+ if (leadingComments.length > 0) {
+ const lines = formattedCode.split('\n');
+ const screenLineIndex = findScreenElementLine(lines, screenName);
+
+ if (screenLineIndex !== -1) {
+ const indent = getIndentation(lines[screenLineIndex]);
+ injectComments(lines, leadingComments, screenLineIndex, indent, true);
+ formattedCode = lines.join('\n');
+ }
+ }
+
+ // Process trailing comments for screen elements (highlight-end)
+ if (trailingComments.length > 0) {
+ const lines = formattedCode.split('\n');
+ const screenLineIndex = findScreenClosingLine(lines, screenName);
+
+ if (screenLineIndex !== -1) {
+ const indent = getIndentation(lines[screenLineIndex]);
+ // Inject after the closing tag
+ injectComments(
+ lines,
+ trailingComments,
+ screenLineIndex + 1,
+ indent,
+ true
+ );
+ formattedCode = lines.join('\n');
+ }
+ }
+ });
+
+ return formattedCode;
+}
+
+/**
+ * Extract screen config from a screen value node.
+ * Handles both direct object expressions and createXScreen function calls.
+ */
+function extractScreenConfig(screenValue) {
+ // Handle createXScreen function calls
+ // e.g., createNativeStackScreen({ screen: ProfileScreen, ... })
+ if (
+ t.isCallExpression(screenValue) &&
+ t.isIdentifier(screenValue.callee) &&
+ screenValue.callee.name.startsWith('create') &&
+ screenValue.callee.name.endsWith('Screen') &&
+ screenValue.arguments.length > 0 &&
+ t.isObjectExpression(screenValue.arguments[0])
+ ) {
+ // Extract the object argument from the createXScreen call
+ return screenValue.arguments[0];
+ }
+
+ // Return the value as-is for other cases
+ return screenValue;
+}
+
+/**
+ * Track comments on screen config properties (options, screen, listeners, etc.)
+ */
+function trackScreenConfigComments(screenValue, screenName, commentTracking) {
+ const configObject = extractScreenConfig(screenValue);
+
+ if (t.isObjectExpression(configObject)) {
+ configObject.properties.forEach((configProp) => {
+ if (
+ t.isObjectProperty(configProp) &&
+ (configProp.leadingComments || configProp.trailingComments)
+ ) {
+ const propName = configProp.key.name || configProp.key.value;
+ commentTracking.add({
+ originalNode: configProp,
+ screenName,
+ screenConfigProperty: propName,
+ leadingComments:
+ configProp.leadingComments?.map((c) => ({
+ value: c.value,
+ type: c.type,
+ })) || [],
+ trailingComments:
+ configProp.trailingComments?.map((c) => ({
+ value: c.value,
+ type: c.type,
+ })) || [],
+ });
+ }
+ });
+ }
+}
+
+/**
+ * Track comments on screen elements themselves
+ */
+function trackScreenComments(screenProp, screenName, commentTracking) {
+ if (screenProp.leadingComments || screenProp.trailingComments) {
+ commentTracking.add({
+ originalNode: screenProp,
+ screenName,
+ leadingComments:
+ screenProp.leadingComments?.map((c) => ({
+ value: c.value,
+ type: c.type,
+ })) || [],
+ trailingComments:
+ screenProp.trailingComments?.map((c) => ({
+ value: c.value,
+ type: c.type,
+ })) || [],
+ targetNode: null,
+ });
+ }
+}
+
+/**
+ * Find the line index of a Screen element's opening tag.
+ * Handles both single-line and multi-line Screen elements.
+ */
+function findScreenElementLine(lines, screenName) {
+ for (let i = 0; i < lines.length; i++) {
+ // Check if line contains Screen opening tag with the name attribute
+ if (
+ lines[i].includes('<') &&
+ lines[i].includes('.Screen') &&
+ lineMatchesScreenName(lines[i], screenName)
+ ) {
+ return i;
+ }
+
+ // For multiline Screen elements, check if opening tag is on this line
+ // and name attribute is on a subsequent line
+ if (lines[i].includes('<') && lines[i].includes('.Screen')) {
+ for (let j = i; j < Math.min(i + 5, lines.length); j++) {
+ if (lineMatchesScreenName(lines[j], screenName)) {
+ return i; // Return opening tag line, not name line
+ }
+ }
+ }
+ }
+ return -1;
+}
+
+/**
+ * Find the line index of a Screen element's closing tag.
+ * Looks for either self-closing /> or closing tag.
+ */
+function findScreenClosingLine(lines, screenName) {
+ for (let i = 0; i < lines.length; i++) {
+ if (
+ (lines[i].includes('.Screen') && lines[i].includes('/>')) ||
+ (lines[i].includes('.Screen') && lines[i].includes(''))
+ ) {
+ // Verify this is the correct screen by checking backward for the name
+ for (let j = Math.max(0, i - 10); j <= i; j++) {
+ if (lineMatchesScreenName(lines[j], screenName)) {
+ return i;
+ }
+ }
+ }
+ }
+ return -1;
+}
+
+/**
+ * Format a comment for injection into code
+ */
+function formatComment(comment, indent, isJSXContext = false) {
+ const commentValue = comment.value.trim();
+ if (comment.type === 'CommentLine') {
+ return `${indent}// ${commentValue}`;
+ }
+ // Block comments in JSX need to be wrapped in braces
+ return isJSXContext
+ ? `${indent}{/* ${commentValue} */}`
+ : `${indent}/* ${commentValue} */`;
+}
+
+/**
+ * Inject comments into lines array at specified position
+ */
+function injectComments(
+ lines,
+ comments,
+ lineIndex,
+ indent,
+ isJSXContext = false
+) {
+ let currentIndex = lineIndex;
+ comments.forEach((comment) => {
+ const commentText = formatComment(comment, indent, isJSXContext);
+ lines.splice(currentIndex, 0, commentText);
+ currentIndex++;
+ });
+ return currentIndex;
+}
+
+/**
+ * Check if a line contains a screen name attribute (with either quote style)
+ */
+function lineMatchesScreenName(line, screenName) {
+ return (
+ line.includes(`name='${screenName}'`) ||
+ line.includes(`name="${screenName}"`)
+ );
+}
+
+/**
+ * Extract indentation from a line
+ */
+function getIndentation(line, defaultIndent = ' ') {
+ return line.match(/^(\s*)/)?.[1] || defaultIndent;
+}
+
+/**
+ * Derive navigator constant name from navigator type string.
+ * Examples:
+ * - "createStackNavigator" -> "Stack"
+ * - "createNativeStackNavigator" -> "Stack"
+ * - "createBottomTabNavigator" -> "Tab"
+ * - "createMaterialTopTabNavigator" -> "Tab"
+ */
+function deriveNavigatorConstName(navigatorType) {
+ // Remove "create" prefix and "Navigator" suffix
+ const withoutCreate = navigatorType.replace(/^create/, '');
+ const withoutNavigator = withoutCreate.replace(/Navigator$/, '');
+ // Extract the last capitalized word (e.g., "NativeStack" -> "Stack", "MaterialTopTab" -> "Tab")
+ const match = withoutNavigator.match(/([A-Z][a-z]+)$/);
+ return match ? match[1] : withoutNavigator;
+}
+
+/**
+ * Generate unique navigator constant name by adding suffix if needed.
+ * Second occurrence gets 'A', third gets 'B', etc.
+ */
+function getUniqueNavigatorConstName(
+ baseNavigatorConstName,
+ navigatorConstNames
+) {
+ const currentCount = navigatorConstNames.get(baseNavigatorConstName) || 0;
+ navigatorConstNames.set(baseNavigatorConstName, currentCount + 1);
+
+ if (currentCount === 0) {
+ return baseNavigatorConstName;
+ }
+ // Add suffix: A for second occurrence, B for third, etc.
+ const suffix = String.fromCharCode(65 + currentCount - 1); // 65 is 'A'
+ return baseNavigatorConstName + suffix;
+}
+
+/**
+ * Attach comments to AST nodes with proper leading/trailing markers
+ */
+function attachCommentsToNode(node, comments, isTrailing = false) {
+ if (comments.length === 0) return;
+
+ comments.forEach((c) => {
+ c.leading = !isTrailing;
+ c.trailing = isTrailing;
});
+ node.comments = [...(node.comments || []), ...comments];
+}
- return output.code;
+/**
+ * Find a property line within a context (searches nearby lines for context marker)
+ */
+function findPropertyLine(lines, propMatcher, contextMatcher, searchRange) {
+ for (let i = 0; i < lines.length; i++) {
+ if (propMatcher(lines[i])) {
+ // Check if this is within the right context by searching nearby lines
+ const startIdx = Math.max(0, i - searchRange);
+ const endIdx = Math.min(i + searchRange, lines.length);
+
+ for (let j = startIdx; j < endIdx; j++) {
+ if (contextMatcher(lines[j])) {
+ return i;
+ }
+ }
+ }
+ }
+ return -1;
}
/**
- * Parse navigator configuration object
+ * Find the closing line for a JSX property value (looks for }} or })})
*/
-function parseNavigatorConfig(configNode) {
+function findPropertyClosingLine(lines, startLine, maxSearchLines = 10) {
+ for (
+ let i = startLine;
+ i < Math.min(startLine + maxSearchLines, lines.length);
+ i++
+ ) {
+ const line = lines[i];
+ // Look for closing patterns but not the Screen element closing
+ if (line.includes('}}') && !line.includes('/>')) {
+ return i;
+ }
+ // Also check for arrow function returning object: })}
+ if (line.includes('})}')) {
+ return i;
+ }
+ }
+ return startLine;
+}
+
+/**
+ * Create a JSX member expression (e.g., Stack.Navigator, Stack.Screen)
+ */
+function createJsxMemberExpression(componentName, memberName) {
+ return t.jsxMemberExpression(
+ t.jsxIdentifier(componentName),
+ t.jsxIdentifier(memberName)
+ );
+}
+
+/**
+ * Create JSX attributes from propInfo objects
+ */
+function createJsxAttributesFromProps(propsObject) {
+ return Object.values(propsObject).map((propInfo) => {
+ if (propInfo.isStringLiteral) {
+ return t.jsxAttribute(
+ t.jsxIdentifier(propInfo.key),
+ t.stringLiteral(propInfo.value.value)
+ );
+ }
+ return t.jsxAttribute(
+ t.jsxIdentifier(propInfo.key),
+ t.jsxExpressionContainer(propInfo.value)
+ );
+ });
+}
+
+/**
+ * Create a Screen JSX element
+ */
+function createScreenElement(componentName, screenName, screenConfig) {
+ const screenProps = [
+ t.jsxAttribute(t.jsxIdentifier('name'), t.stringLiteral(screenName)),
+ t.jsxAttribute(
+ t.jsxIdentifier('component'),
+ t.jsxExpressionContainer(t.identifier(screenConfig.component))
+ ),
+ ];
+
+ // Add all screen-level props
+ Object.entries(screenConfig.screenProps).forEach(([key, value]) => {
+ screenProps.push(
+ t.jsxAttribute(t.jsxIdentifier(key), t.jsxExpressionContainer(value))
+ );
+ });
+
+ return t.jsxElement(
+ t.jsxOpeningElement(
+ createJsxMemberExpression(componentName, 'Screen'),
+ screenProps,
+ true
+ ),
+ null,
+ [],
+ true
+ );
+}
+
+/**
+ * Parse a screen value and return component and screenProps.
+ * Handles identifiers, object expressions, and createXScreen calls.
+ */
+function parseScreenValue(screenValue) {
+ if (t.isIdentifier(screenValue)) {
+ // Simple screen: Home: HomeScreen
+ return {
+ component: screenValue.name,
+ screenProps: {},
+ };
+ }
+
+ // Extract config from createXScreen calls if present
+ const configNode = extractScreenConfig(screenValue);
+
+ if (t.isObjectExpression(configNode)) {
+ // Screen with config: Home: { screen: HomeScreen, options: {...}, listeners: {...} }
+ let component = null;
+ const screenProps = {};
+
+ configNode.properties.forEach((screenConfigProp) => {
+ if (!t.isObjectProperty(screenConfigProp)) return;
+
+ const configKey = screenConfigProp.key.name || screenConfigProp.key.value;
+
+ if (configKey === 'screen' && t.isIdentifier(screenConfigProp.value)) {
+ component = screenConfigProp.value.name;
+ } else {
+ // Store all other props (options, listeners, getId, linking, etc.)
+ // But skip 'linking' as it's only for static config
+ if (configKey !== 'linking') {
+ screenProps[configKey] = screenConfigProp.value;
+ }
+ }
+ });
+
+ return { component, screenProps };
+ }
+
+ return null;
+}
+
+/**
+ * Parse navigator configuration object.
+ * Extracts screens, groups, and navigator-level properties.
+ * Also tracks comments for later injection into the dynamic code.
+ */
+function parseNavigatorConfig(configNode, commentTracking) {
const result = {
- screens: {},
- groups: {}, // Store groups
- navigatorProps: {}, // Store all navigator-level props
+ screens: {}, // Standalone screens (not in groups)
+ groups: {}, // Screen groups with their own screens and props
+ navigatorProps: {}, // Navigator-level props (screenOptions, initialRouteName, etc.)
};
if (!t.isObjectExpression(configNode)) {
return result;
}
- configNode.properties.forEach((prop) => {
- if (!t.isObjectProperty(prop) && !t.isObjectMethod(prop)) {
- return;
- }
+ // Get all properties from the navigator config object
+ const props = configNode.properties.filter(
+ (prop) => t.isObjectProperty(prop) || t.isObjectMethod(prop)
+ );
+ props.forEach((prop, index) => {
const keyName = prop.key.name || prop.key.value;
+ // Track comments on navigator-level properties (but not on screens/groups)
+ if (keyName !== 'screens' && keyName !== 'groups') {
+ const leadingComments =
+ prop.leadingComments?.map((c) => ({
+ value: c.value,
+ type: c.type,
+ })) || [];
+
+ // Collect trailing comments from both the property and its value
+ let trailingComments = [];
+ if (prop.trailingComments) {
+ trailingComments.push(
+ ...prop.trailingComments.map((c) => ({
+ value: c.value,
+ type: c.type,
+ }))
+ );
+ }
+ if (prop.value?.trailingComments) {
+ trailingComments.push(
+ ...prop.value.trailingComments.map((c) => ({
+ value: c.value,
+ type: c.type,
+ }))
+ );
+ }
+
+ // Heuristic: Check if the next property has leading comments that are actually
+ // trailing comments for this property (detected by -end or end suffix)
+ // This handles cases where Babel attaches multiline trailing comments as leading
+ const nextProp = props[index + 1];
+
+ if (nextProp?.leadingComments) {
+ nextProp.leadingComments.forEach((c) => {
+ // Only treat as trailing if the comment ends with -end or similar markers
+ if (
+ c.value.trim().endsWith('-end') ||
+ c.value.trim().endsWith('end')
+ ) {
+ trailingComments.push({
+ value: c.value,
+ type: c.type,
+ });
+ }
+ });
+ }
+
+ if (leadingComments.length > 0 || trailingComments.length > 0) {
+ const commentObj = {
+ originalNode: prop,
+ navigatorProp: keyName,
+ leadingComments,
+ trailingComments,
+ };
+ commentTracking.add(commentObj);
+ }
+ }
+
+ // Parse groups object (e.g., groups: { modal: { screens: {...}, screenOptions: {...} } })
if (keyName === 'groups' && t.isObjectExpression(prop.value)) {
// Parse groups object
prop.value.properties.forEach((groupProp) => {
@@ -417,32 +1005,20 @@ function parseNavigatorConfig(configNode) {
const screenName = screenProp.key.name || screenProp.key.value;
const screenValue = screenProp.value;
- if (t.isIdentifier(screenValue)) {
- groupConfig.screens[screenName] = {
- component: screenValue.name,
- screenProps: {},
- };
- } else if (t.isObjectExpression(screenValue)) {
- let component = null;
- const screenProps = {};
-
- screenValue.properties.forEach((screenConfigProp) => {
- if (!t.isObjectProperty(screenConfigProp)) return;
-
- const key =
- screenConfigProp.key.name || screenConfigProp.key.value;
-
- if (
- key === 'screen' &&
- t.isIdentifier(screenConfigProp.value)
- ) {
- component = screenConfigProp.value.name;
- } else {
- screenProps[key] = screenConfigProp.value;
- }
- });
-
- groupConfig.screens[screenName] = { component, screenProps };
+ // Track comments on any property inside the screen config
+ trackScreenConfigComments(
+ screenValue,
+ screenName,
+ commentTracking
+ );
+
+ // Track comments on the screen element itself
+ trackScreenComments(screenProp, screenName, commentTracking);
+
+ const parsed = parseScreenValue(screenValue);
+
+ if (parsed) {
+ groupConfig.screens[screenName] = parsed;
}
});
} else {
@@ -458,6 +1034,7 @@ function parseNavigatorConfig(configNode) {
result.groups[groupKey] = groupConfig;
}
});
+ // Parse top-level screens object (e.g., screens: { Home: HomeScreen, Profile: {...} })
} else if (keyName === 'screens' && t.isObjectExpression(prop.value)) {
// Parse screens object
prop.value.properties.forEach((screenProp) => {
@@ -466,40 +1043,21 @@ function parseNavigatorConfig(configNode) {
const screenName = screenProp.key.name || screenProp.key.value;
const screenValue = screenProp.value;
- if (t.isIdentifier(screenValue)) {
- // Simple screen: Home: HomeScreen
- result.screens[screenName] = {
- component: screenValue.name,
- screenProps: {}, // No additional props
- };
- } else if (t.isObjectExpression(screenValue)) {
- // Screen with config: Home: { screen: HomeScreen, options: {...}, listeners: {...} }
- let component = null;
- const screenProps = {};
+ // Track comments on any property inside the screen config
+ trackScreenConfigComments(screenValue, screenName, commentTracking);
- screenValue.properties.forEach((screenConfigProp) => {
- if (!t.isObjectProperty(screenConfigProp)) return;
+ // Track comments on the screen element itself
+ trackScreenComments(screenProp, screenName, commentTracking);
- const configKey =
- screenConfigProp.key.name || screenConfigProp.key.value;
+ const parsed = parseScreenValue(screenValue);
- if (
- configKey === 'screen' &&
- t.isIdentifier(screenConfigProp.value)
- ) {
- component = screenConfigProp.value.name;
- } else {
- // Store all other props (options, listeners, getId, etc.)
- screenProps[configKey] = screenConfigProp.value;
- }
- });
-
- result.screens[screenName] = { component, screenProps };
+ if (parsed) {
+ result.screens[screenName] = parsed;
}
});
} else {
- // All other props are navigator-level props
- // Store both the key name and the AST node value
+ // Store all other navigator-level props (screenOptions, initialRouteName, etc.)
+ // Keep track of whether the value is a string literal to determine JSX attribute format
result.navigatorProps[keyName] = {
key: keyName,
value: prop.value,
@@ -515,28 +1073,8 @@ function parseNavigatorConfig(configNode) {
* Create navigator component function
*/
function createNavigatorComponent(functionName, componentName, config) {
- const navigatorProps = [];
-
// Add all navigator-level props dynamically
- Object.values(config.navigatorProps).forEach((propInfo) => {
- if (propInfo.isStringLiteral) {
- // String literals can be used directly as JSX string attributes
- navigatorProps.push(
- t.jsxAttribute(
- t.jsxIdentifier(propInfo.key),
- t.stringLiteral(propInfo.value.value)
- )
- );
- } else {
- // All other values need to be wrapped in JSX expression containers
- navigatorProps.push(
- t.jsxAttribute(
- t.jsxIdentifier(propInfo.key),
- t.jsxExpressionContainer(propInfo.value)
- )
- );
- }
- });
+ const navigatorProps = createJsxAttributesFromProps(config.navigatorProps);
// Create screen elements
const screenElements = [];
@@ -549,69 +1087,18 @@ function createNavigatorComponent(functionName, componentName, config) {
t.jsxIdentifier('navigationKey'),
t.stringLiteral(groupKey)
),
+ ...createJsxAttributesFromProps(groupConfig.groupProps),
];
- // Add group-level props (screenOptions, screenLayout, etc.)
- Object.values(groupConfig.groupProps).forEach((propInfo) => {
- if (propInfo.isStringLiteral) {
- groupProps.push(
- t.jsxAttribute(
- t.jsxIdentifier(propInfo.key),
- t.stringLiteral(propInfo.value.value)
- )
- );
- } else {
- groupProps.push(
- t.jsxAttribute(
- t.jsxIdentifier(propInfo.key),
- t.jsxExpressionContainer(propInfo.value)
- )
- );
- }
- });
-
// Create screens for this group
const groupScreenElements = [];
Object.entries(groupConfig.screens).forEach(
([screenName, screenConfig]) => {
- const screenProps = [
- t.jsxAttribute(
- t.jsxIdentifier('name'),
- t.stringLiteral(screenName)
- ),
- t.jsxAttribute(
- t.jsxIdentifier('component'),
- t.jsxExpressionContainer(t.identifier(screenConfig.component))
- ),
- ];
-
- // Add all screen-level props
- Object.entries(screenConfig.screenProps).forEach(([key, value]) => {
- screenProps.push(
- t.jsxAttribute(
- t.jsxIdentifier(key),
- t.jsxExpressionContainer(value)
- )
- );
- });
-
- const screenElement = t.jsxElement(
- t.jsxOpeningElement(
- t.jsxMemberExpression(
- t.jsxIdentifier(componentName),
- t.jsxIdentifier('Screen')
- ),
- screenProps,
- true
- ),
- null,
- [],
- true
- );
-
groupScreenElements.push(t.jsxText('\n '));
- groupScreenElements.push(screenElement);
+ groupScreenElements.push(
+ createScreenElement(componentName, screenName, screenConfig)
+ );
}
);
@@ -620,18 +1107,10 @@ function createNavigatorComponent(functionName, componentName, config) {
// Create the Group element
const groupElement = t.jsxElement(
t.jsxOpeningElement(
- t.jsxMemberExpression(
- t.jsxIdentifier(componentName),
- t.jsxIdentifier('Group')
- ),
+ createJsxMemberExpression(componentName, 'Group'),
groupProps
),
- t.jsxClosingElement(
- t.jsxMemberExpression(
- t.jsxIdentifier(componentName),
- t.jsxIdentifier('Group')
- )
- ),
+ t.jsxClosingElement(createJsxMemberExpression(componentName, 'Group')),
groupScreenElements,
false
);
@@ -643,37 +1122,10 @@ function createNavigatorComponent(functionName, componentName, config) {
// Handle standalone screens (not in groups)
Object.entries(config.screens).forEach(([screenName, screenConfig]) => {
- const screenProps = [
- t.jsxAttribute(t.jsxIdentifier('name'), t.stringLiteral(screenName)),
- t.jsxAttribute(
- t.jsxIdentifier('component'),
- t.jsxExpressionContainer(t.identifier(screenConfig.component))
- ),
- ];
-
- // Add all screen-level props dynamically (options, listeners, getId, etc.)
- Object.entries(screenConfig.screenProps).forEach(([key, value]) => {
- screenProps.push(
- t.jsxAttribute(t.jsxIdentifier(key), t.jsxExpressionContainer(value))
- );
- });
-
- const screenElement = t.jsxElement(
- t.jsxOpeningElement(
- t.jsxMemberExpression(
- t.jsxIdentifier(componentName),
- t.jsxIdentifier('Screen')
- ),
- screenProps,
- true
- ),
- null,
- [],
- true
- );
-
screenElements.push(t.jsxText('\n '));
- screenElements.push(screenElement);
+ screenElements.push(
+ createScreenElement(componentName, screenName, screenConfig)
+ );
});
screenElements.push(t.jsxText('\n'));
@@ -681,18 +1133,10 @@ function createNavigatorComponent(functionName, componentName, config) {
// Create Navigator element
const navigatorElement = t.jsxElement(
t.jsxOpeningElement(
- t.jsxMemberExpression(
- t.jsxIdentifier(componentName),
- t.jsxIdentifier('Navigator')
- ),
+ createJsxMemberExpression(componentName, 'Navigator'),
navigatorProps
),
- t.jsxClosingElement(
- t.jsxMemberExpression(
- t.jsxIdentifier(componentName),
- t.jsxIdentifier('Navigator')
- )
- ),
+ t.jsxClosingElement(createJsxMemberExpression(componentName, 'Navigator')),
screenElements,
false
);
@@ -710,6 +1154,47 @@ function createNavigatorComponent(functionName, componentName, config) {
return functionDeclaration;
}
+/**
+ * Create a TabItem element with code block
+ */
+function createTabItem(
+ value,
+ label,
+ code,
+ codeNode,
+ originalCodeBlock,
+ cleanData,
+ isDefault = false
+) {
+ return {
+ type: 'element',
+ tagName: 'TabItem',
+ properties: {
+ value,
+ label,
+ ...(isDefault && { default: true }),
+ },
+ children: [
+ { type: 'text', value: '\n\n' },
+ {
+ type: 'element',
+ tagName: 'pre',
+ properties: originalCodeBlock.properties || {},
+ children: [
+ {
+ type: 'element',
+ tagName: 'code',
+ properties: codeNode.properties || {},
+ data: cleanData,
+ children: [{ type: 'text', value: code }],
+ },
+ ],
+ },
+ { type: 'text', value: '\n\n' },
+ ],
+ };
+}
+
/**
* Create a Tabs element with both static and dynamic TabItems
*/
@@ -721,6 +1206,26 @@ function createTabsWithBothConfigs(staticCode, dynamicCode, originalCodeBlock) {
codeNode.data?.meta?.replace(/\bstatic2dynamic\b\s*/g, '').trim() || '';
const cleanData = { ...codeNode.data, meta: cleanMeta };
+ const tabItems = [
+ { value: 'static', label: 'Static', code: staticCode, isDefault: true },
+ { value: 'dynamic', label: 'Dynamic', code: dynamicCode, isDefault: false },
+ ];
+
+ const children = tabItems.flatMap((item) => [
+ { type: 'text', value: '\n' },
+ createTabItem(
+ item.value,
+ item.label,
+ item.code,
+ codeNode,
+ originalCodeBlock,
+ cleanData,
+ item.isDefault
+ ),
+ ]);
+
+ children.push({ type: 'text', value: '\n' });
+
return {
type: 'element',
tagName: 'Tabs',
@@ -728,96 +1233,6 @@ function createTabsWithBothConfigs(staticCode, dynamicCode, originalCodeBlock) {
groupId: 'config',
queryString: 'config',
},
- children: [
- {
- type: 'text',
- value: '\n',
- },
- // Static TabItem
- {
- type: 'element',
- tagName: 'TabItem',
- properties: {
- value: 'static',
- label: 'Static',
- default: true,
- },
- children: [
- {
- type: 'text',
- value: '\n\n',
- },
- {
- type: 'element',
- tagName: 'pre',
- properties: originalCodeBlock.properties || {},
- children: [
- {
- type: 'element',
- tagName: 'code',
- properties: codeNode.properties || {},
- data: cleanData,
- children: [
- {
- type: 'text',
- value: staticCode,
- },
- ],
- },
- ],
- },
- {
- type: 'text',
- value: '\n\n',
- },
- ],
- },
- {
- type: 'text',
- value: '\n',
- },
- // Dynamic TabItem
- {
- type: 'element',
- tagName: 'TabItem',
- properties: {
- value: 'dynamic',
- label: 'Dynamic',
- },
- children: [
- {
- type: 'text',
- value: '\n\n',
- },
- {
- type: 'element',
- tagName: 'pre',
- properties: originalCodeBlock.properties || {},
- children: [
- {
- type: 'element',
- tagName: 'code',
- properties: codeNode.properties || {},
- data: cleanData,
- children: [
- {
- type: 'text',
- value: dynamicCode,
- },
- ],
- },
- ],
- },
- {
- type: 'text',
- value: '\n\n',
- },
- ],
- },
- {
- type: 'text',
- value: '\n',
- },
- ],
+ children,
};
}
diff --git a/versioned_docs/version-7.x/headers.md b/versioned_docs/version-7.x/headers.md
index 04f18811603..2372bf9d682 100755
--- a/versioned_docs/version-7.x/headers.md
+++ b/versioned_docs/version-7.x/headers.md
@@ -13,10 +13,7 @@ We've seen how to configure the header title already, but let's go over that aga
Each screen has `options` which is either an object or a function that returns an object, that contains various configuration options. The one we use for the header title is `title`, as shown in the following example.
-
-
-
-```js name="Setting header title" snack
+```js name="Setting header title" snack static2dynamic
import * as React from 'react';
import { Text, View } from 'react-native';
import { createStaticNavigation } from '@react-navigation/native';
@@ -52,65 +49,13 @@ export default function App() {
}
```
-
-
-
-```js name="Setting header title" snack
-import * as React from 'react';
-import { Text, View } from 'react-native';
-import { NavigationContainer } from '@react-navigation/native';
-import { createNativeStackNavigator } from '@react-navigation/native-stack';
-
-function HomeScreen() {
- return (
-
- Home Screen
-
- );
-}
-
-const Stack = createNativeStackNavigator();
-
-// codeblock-focus-start
-function MyStack() {
- return (
-
-
-
- );
-}
-// codeblock-focus-end
-
-export default function App() {
- return (
-
-
-
- );
-}
-```
-
-
-
-

## Using params in the title
In order to use params in the title, we need to make `options` for the screen a function that returns a configuration object. If we make `options` a function then React Navigation will call it with an object containing `{ navigation, route }` - in this case, all we care about is `route`, which is the same object that is passed to your screen props as `route` prop. You may recall that we can get the params through `route.params`, and so we do this below to extract a param and use it as a title.
-
-
-
-```js name="Using params in the title" snack
+```js name="Using params in the title" snack static2dynamic
import * as React from 'react';
import { Text, View } from 'react-native';
import {
@@ -174,79 +119,6 @@ export default function App() {
}
```
-
-
-
-```js name="Using params in the title" snack
-import * as React from 'react';
-import { Text, View } from 'react-native';
-import { NavigationContainer, useNavigation } from '@react-navigation/native';
-import { createNativeStackNavigator } from '@react-navigation/native-stack';
-import { Button } from '@react-navigation/elements';
-
-function HomeScreen() {
- const navigation = useNavigation();
-
- return (
-
-
-
- );
-}
-
-function ProfileScreen() {
- return (
-
- Profile Screen
-
- );
-}
-
-const Stack = createNativeStackNavigator();
-
-// codeblock-focus-start
-function MyStack() {
- return (
-
-
- ({
- title: route.params.name,
- })}
- // highlight-end
- />
-
- );
-}
-// codeblock-focus-end
-
-export default function App() {
- return (
-
-
-
- );
-}
-```
-
-
-
-
The argument that is passed in to the `options` function is an object with the following properties:
- `navigation` - The [navigation object](navigation-object.md) for the screen.
@@ -258,7 +130,7 @@ We only needed the `route` object in the above example but you may in some cases
It's often necessary to update the `options` configuration for the active screen from the mounted screen component itself. We can do this using `navigation.setOptions`
-```js name="Updating options" snack
+```js name="Updating options" snack static2dynamic
import * as React from 'react';
import { Text, View } from 'react-native';
import {
@@ -314,10 +186,7 @@ There are three key properties to use when customizing the style of your header:
- `headerTintColor`: the back button and title both use this property as their color. In the example below, we set the tint color to white (`#fff`) so the back button and the header title would be white.
- `headerTitleStyle`: if we want to customize the `fontFamily`, `fontWeight` and other `Text` style properties for the title, we can use this to do it.
-
-
-
-```js name="Header styles" snack
+```js name="Header styles" snack static2dynamic
import * as React from 'react';
import { Text, View } from 'react-native';
import { createStaticNavigation } from '@react-navigation/native';
@@ -360,62 +229,6 @@ export default function App() {
}
```
-
-
-
-```js name="Header styles" snack
-import * as React from 'react';
-import { Text, View } from 'react-native';
-import { NavigationContainer } from '@react-navigation/native';
-import { createNativeStackNavigator } from '@react-navigation/native-stack';
-
-function HomeScreen() {
- return (
-
- Home Screen
-
- );
-}
-
-const Stack = createNativeStackNavigator();
-
-// codeblock-focus-start
-function MyStack() {
- return (
-
-
-
- );
-}
-// codeblock-focus-end
-
-export default function App() {
- return (
-
-
-
- );
-}
-```
-
-
-
-

There are a couple of things to notice here:
@@ -427,10 +240,7 @@ There are a couple of things to notice here:
It is common to want to configure the header in a similar way across many screens. For example, your company brand color might be red and so you want the header background color to be red and the tint color to be white. Conveniently, these are the colors we're using in our running example, and you'll notice that when you navigate to the `DetailsScreen` the colors go back to the defaults. Wouldn't it be awful if we had to copy the `options` header style properties from `Home` to `Details`, and for every single screen we use in our app? Thankfully, we do not. We can instead move the configuration up to the native stack navigator under `screenOptions`:
-
-
-
-```js name="Common screen options" snack
+```js name="Common screen options" snack static2dynamic
import * as React from 'react';
import { Text, View } from 'react-native';
import {
@@ -492,88 +302,13 @@ export default function App() {
}
```
-
-
-
-```js name="Common screen options" snack
-import * as React from 'react';
-import { Text, View } from 'react-native';
-import { NavigationContainer, useNavigation } from '@react-navigation/native';
-import { createNativeStackNavigator } from '@react-navigation/native-stack';
-import { Button } from '@react-navigation/elements';
-
-function HomeScreen() {
- const navigation = useNavigation();
-
- return (
-
- Home Screen
-
-
- );
-}
-
-function DetailsScreen() {
- return (
-
- Details Screen
-
- );
-}
-
-const Stack = createNativeStackNavigator();
-
-// codeblock-focus-start
-function MyStack() {
- return (
-
-
-
-
- );
-}
-// codeblock-focus-end
-
-export default function App() {
- return (
-
-
-
- );
-}
-```
-
-
-
-
Now, any screen that belongs to this navigator will have our wonderful branded styles. Surely though, there must be a way to override these options if we need to?
## Replacing the title with a custom component
Sometimes you need more control than just changing the text and styles of your title -- for example, you may want to render an image in place of the title, or make the title into a button. In these cases you can completely override the component used for the title and provide your own.
-
-
-
-```js name="Custom title" snack
+```js name="Custom title" snack static2dynamic
import * as React from 'react';
import { Text, View, Image } from 'react-native';
import { createStaticNavigation } from '@react-navigation/native';
@@ -617,66 +352,8 @@ export default function App() {
}
```
-
-
-
-```js name="Custom title" snack
-import * as React from 'react';
-import { Text, View, Image } from 'react-native';
-import { NavigationContainer } from '@react-navigation/native';
-import { createNativeStackNavigator } from '@react-navigation/native-stack';
-
-function HomeScreen() {
- return (
-
- Home Screen
-
- );
-}
-
-const Stack = createNativeStackNavigator();
-
-// codeblock-focus-start
-function LogoTitle() {
- return (
-
- );
-}
-
-function MyStack() {
- return (
-
- ,
- }}
- />
-
- );
-}
-// codeblock-focus-end
-
-export default function App() {
- return (
-
-
-
- );
-}
-```
-

-You might be wondering, why `headerTitle` when we provide a component and not `title`, like before? The reason is that `headerTitle` is a property that is specific to stack navigators, the `headerTitle` defaults to a `Text` component that displays the `title`.
-
-
-
:::note
You might be wondering, why `headerTitle` when we provide a component and not `title`, like before? The reason is that `headerTitle` is a property that is specific to headers, whereas `title` will be used for tab bars, drawers etc. as well. The `headerTitle` defaults to a `Text` component that displays the `title`.
diff --git a/versioned_docs/version-7.x/hello-react-navigation.md b/versioned_docs/version-7.x/hello-react-navigation.md
index 74e4e98f602..1d885f73a6f 100755
--- a/versioned_docs/version-7.x/hello-react-navigation.md
+++ b/versioned_docs/version-7.x/hello-react-navigation.md
@@ -45,8 +45,6 @@ npm install @react-navigation/elements
`createStaticNavigation` is a function that takes the navigator defined earlier and returns a component that can be rendered in the app. It's only called once in the app. Usually, we'd render the returned component at the root of our app, which is usually the component exported from `App.js`, `App.tsx` etc., or used with `AppRegistry.registerComponent`, `Expo.registerRootComponent` etc.
```js name="Native Stack Example" snack
-// In App.js in a new project
-
import * as React from 'react';
import { View, Text } from 'react-native';
import { createStaticNavigation } from '@react-navigation/native';
@@ -87,8 +85,6 @@ In a typical React Native app, the `createStaticNavigation` function should be o
`NavigationContainer` is a component that manages our navigation tree and contains the [navigation state](navigation-state.md). This component must wrap all the navigators in the app. Usually, we'd render this component at the root of our app, which is usually the component exported from `App.js`, `App.tsx` etc., or used with `AppRegistry.registerComponent`, `Expo.registerRootComponent` etc.
```js name="Native Stack Example" snack
-// In App.js in a new project
-
import * as React from 'react';
import { View, Text } from 'react-native';
import { NavigationContainer } from '@react-navigation/native';
diff --git a/versioned_docs/version-7.x/navigating.md b/versioned_docs/version-7.x/navigating.md
index 73057ef5193..e4b09581d7e 100755
--- a/versioned_docs/version-7.x/navigating.md
+++ b/versioned_docs/version-7.x/navigating.md
@@ -28,7 +28,7 @@ We'll do something similar to the latter, but rather than using a `window.locati
## Navigating to a new screen
-```js name="Navigating to a new screen" snack
+```js name="Navigating to a new screen" snack static2dynamic
// codeblock-focus-start
import * as React from 'react';
import { View, Text } from 'react-native';
@@ -100,7 +100,7 @@ So we now have a stack with two routes: 1) the `Home` route 2) the `Details` rou
## Navigate to a screen multiple times
-```js name="Navigate to a screen multiple times" snack
+```js name="Navigate to a screen multiple times" snack static2dynamic
import * as React from 'react';
import { View, Text } from 'react-native';
import {
@@ -159,7 +159,7 @@ If you run this code, you'll notice that when you tap "Go to Details... again",
Let's suppose that we actually _want_ to add another details screen. This is pretty common in cases where you pass in some unique data to each route (more on that later when we talk about `params`!). To do this, we can change `navigate` to `push`. This allows us to express the intent to add another route regardless of the existing navigation history.
-```js name="Navigate to a screen multiple times" snack
+```js name="Navigate to a screen multiple times" snack static2dynamic
import * as React from 'react';
import { View, Text } from 'react-native';
import {
@@ -224,7 +224,7 @@ The header provided by the native stack navigator will automatically include a b
Sometimes you'll want to be able to programmatically trigger this behavior, and for that, you can use `navigation.goBack()`.
-```js name="Going back" snack
+```js name="Going back" snack static2dynamic
import * as React from 'react';
import { View, Text } from 'react-native';
import {
@@ -292,7 +292,7 @@ On Android, React Navigation hooks in to the hardware back button and fires the
Another common requirement is to be able to go back _multiple_ screens -- for example, if you are several screens deep in a stack and want to dismiss all of them to go back to the first screen. In this case, we know that we want to go back to `Home` so we can use `popTo('Home')`. Another alternative would be `navigation.popToTop()`, which goes back to the first screen in the stack.
-```js name="Going back to specific screen" snack
+```js name="Going back to specific screen" snack static2dynamic
import * as React from 'react';
import { View, Text } from 'react-native';
import {
diff --git a/versioned_docs/version-7.x/params.md b/versioned_docs/version-7.x/params.md
index 9bd5d6fb88a..4bd6aa840f4 100755
--- a/versioned_docs/version-7.x/params.md
+++ b/versioned_docs/version-7.x/params.md
@@ -22,7 +22,7 @@ We recommend that the params you pass are JSON-serializable. That way, you'll be
:::
-```js name="Passing params" snack
+```js name="Passing params" snack static2dynamic
import * as React from 'react';
import { View, Text } from 'react-native';
import {
@@ -144,7 +144,7 @@ Screens can also update their params, like they can update their state. The `nav
Basic usage:
-```js name="Updating params" snack
+```js name="Updating params" snack static2dynamic
import * as React from 'react';
import { Text, View } from 'react-native';
import {
@@ -208,7 +208,7 @@ Params aren't only useful for passing some data to a new screen, but they can al
To achieve this, you can use the `popTo` method to go back to the previous screen as well as pass params to it:
-```js name="Passing params back" snack
+```js name="Passing params back" snack static2dynamic
import * as React from 'react';
import { Text, View, TextInput } from 'react-native';
import {
@@ -294,7 +294,7 @@ Here, after you press "Done", the home screen's `route.params` will be updated t
If you have nested navigators, you need to pass params a bit differently. For example, say you have a navigator inside the `More` screen and want to pass params to the `Settings` screen inside that navigator. Then you can pass params as the following:
-```js name="Passing params to nested screen" snack
+```js name="Passing params to nested screen" snack static2dynamic
import * as React from 'react';
import { Text, View, TextInput } from 'react-native';
import {
diff --git a/versioned_docs/version-8.x/auth-flow.md b/versioned_docs/version-8.x/auth-flow.md
new file mode 100755
index 00000000000..c3d4e71772a
--- /dev/null
+++ b/versioned_docs/version-8.x/auth-flow.md
@@ -0,0 +1,1002 @@
+---
+id: auth-flow
+title: Authentication flows
+sidebar_label: Authentication flows
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+Most apps require that a user authenticates in some way to have access to data associated with a user or other private content. Typically the flow will look like this:
+
+- The user opens the app.
+- The app loads some authentication state from encrypted persistent storage (for example, [`SecureStore`](https://docs.expo.io/versions/latest/sdk/securestore/)).
+- When the state has loaded, the user is presented with either authentication screens or the main app, depending on whether valid authentication state was loaded.
+- When the user signs out, we clear the authentication state and send them back to authentication screens.
+
+:::note
+
+We say "authentication screens" because usually there is more than one. You may have a main screen with a username and password field, another for "forgot password", and another set for sign up.
+
+:::
+
+## What we need
+
+We want the following behavior from our authentication flow:
+
+- When the user is signed in, we want to show the main app screens and not the authentication-related screens.
+- When the user is signed out, we want to show the authentication screens and not the main app screens.
+- After the user goes through the authentication flow and signs in, we want to unmount all of the screens related to authentication, and when we press the hardware back button, we expect to not be able to go back to the authentication flow.
+
+## How it will work
+
+We can configure different screens to be available based on some condition. For example, if the user is signed in, we want `Home` to be available. If the user is not signed in, we want `SignIn` to be available.
+
+
+
+
+```js name="Authentication flow" snack
+import * as React from 'react';
+import { View } from 'react-native';
+import { createStaticNavigation } from '@react-navigation/native';
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+
+const useIsSignedIn = () => {
+ return true;
+};
+
+const useIsSignedOut = () => {
+ return !useIsSignedIn();
+};
+
+// codeblock-focus-start
+const RootStack = createNativeStackNavigator({
+ screens: {
+ Home: {
+ if: useIsSignedIn,
+ screen: HomeScreen,
+ },
+ SignIn: {
+ if: useIsSignedOut,
+ screen: SignInScreen,
+ },
+ },
+});
+// codeblock-focus-end
+
+const Navigation = createStaticNavigation(RootStack);
+
+export default function App() {
+ return ;
+}
+
+function HomeScreen() {
+ return ;
+}
+
+function SignInScreen() {
+ return ;
+}
+```
+
+Here, for each screen, we have defined a condition using the `if` property which takes a hook. The hook returns a boolean value indicating whether the user is signed in or not. If the hook returns `true`, the screen will be available, otherwise it won't.
+
+This means:
+
+- When `useIsSignedIn` returns `true`, React Navigation will only use the `Home` screen, since it's the only screen matching the condition.
+- Similarly, when `useIsSignedOut` returns `true`, React Navigation will use the `SignIn` screen.
+
+This makes it impossible to navigate to the `Home` when the user is not signed in, and to `SignIn` when the user is signed in.
+
+When the values returned by `useIsSignedin` and `useIsSignedOut` change, the screens matching the condition will change:
+
+- Let's say, initially `useIsSignedOut` returns `true`. This means that `SignIn` screens is shown.
+- After the user signs in, the return value of `useIsSignedIn` will change to `true` and `useIsSignedOut` will change to `false`, which means:
+ - React Navigation will see that the `SignIn` screen is no longer matches the condition, so it will remove the screen.
+ - Then it'll show the `Home` screen automatically because that's the first screen available when `useIsSignedIn` returns `true`.
+
+The order of the screens matters when there are multiple screens matching the condition. For example, if there are two screens matching `useIsSignedIn`, the first screen will be shown when the condition is `true`.
+
+## Define the hooks
+
+To implement the `useIsSignedIn` and `useIsSignedOut` hooks, we can start by creating a context to store the authentication state. Let's call it `SignInContext`:
+
+```js
+import * as React from 'react';
+
+const SignInContext = React.createContext();
+```
+
+Then we can implement the `useIsSignedIn` and `useIsSignedOut` hooks as follows:
+
+```js
+function useIsSignedIn() {
+ const isSignedIn = React.useContext(SignInContext);
+ return isSignedIn;
+}
+
+function useIsSignedOut() {
+ return !useIsSignedIn();
+}
+```
+
+We'll discuss how to provide the context value later.
+
+
+
+
+
+```js name="Authentication flow" snack
+import * as React from 'react';
+import { View } from 'react-native';
+import { NavigationContainer } from '@react-navigation/native';
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+
+const Stack = createNativeStackNavigator();
+
+export default function App() {
+ const isSignedIn = true;
+
+ return (
+
+
+ // codeblock-focus-start
+ {isSignedIn ? (
+
+ ) : (
+
+ )}
+ // codeblock-focus-end
+
+
+ );
+}
+
+function HomeScreen() {
+ return ;
+}
+
+function SignInScreen() {
+ return ;
+}
+```
+
+Here, we have conditionally defined the screens based on the value of `isSignedIn`.
+
+This means:
+
+- When `isSignedIn` is `true`, React Navigation will only see the `Home` screen, since it's the only screen defined based on the condition.
+- Similarly, when `isSignedIn` is `false`, React Navigation will only see the `SignIn` screen.
+
+This makes it impossible to navigate to the `Home` when the user is not signed in, and to `SignIn` when the user is signed in.
+
+When the value of `isSignedin` changes, the screens defined based on the condition will change:
+
+- Let's say, initially `isSignedin` is `false`. This means that `SignIn` screens is shown.
+- After the user signs in, the value of `isSignedin` will change to `true`, which means:
+ - React Navigation will see that the `SignIn` screen is no longer defined, so it will remove the screen.
+ - Then it'll show the `Home` screen automatically because that's the first screen defined when `isSignedin` returns `true`.
+
+The order of the screens matters when there are multiple screens matching the condition. For example, if there are two screens defined based on `isSignedin`, the first screen will be shown when the condition is `true`.
+
+
+
+
+## Add more screens
+
+For our case, let's say we have 3 screens:
+
+- `SplashScreen` - This will show a splash or loading screen when we're restoring the token.
+- `SignIn` - This is the screen we show if the user isn't signed in already (we couldn't find a token).
+- `Home` - This is the screen we show if the user is already signed in.
+
+So our navigator will look like:
+
+
+
+
+```js
+const RootStack = createNativeStackNavigator({
+ screens: {
+ Home: {
+ if: useIsSignedIn,
+ screen: HomeScreen,
+ },
+ SignIn: {
+ if: useIsSignedOut,
+ screen: SignInScreen,
+ options: {
+ title: 'Sign in',
+ },
+ },
+ },
+});
+
+const Navigation = createStaticNavigation(RootStack);
+```
+
+
+
+
+```js
+const Stack = createNativeStackNavigator();
+
+export default function App() {
+ const isSignedIn = true;
+
+ return (
+
+
+ {isSignedIn ? (
+
+ ) : (
+
+ )}
+
+
+ );
+}
+```
+
+
+
+
+Notice how we have only defined the `Home` and `SignIn` screens here, and not the `SplashScreen`. The `SplashScreen` should be rendered before we render any navigators so that we don't render incorrect screens before we know whether the user is signed in or not.
+
+When we use this in our component, it'd look something like this:
+
+
+
+
+```js
+if (isLoading) {
+ // We haven't finished checking for the token yet
+ return ;
+}
+
+const isSignedIn = userToken != null;
+
+return (
+
+
+
+);
+```
+
+
+
+
+```js
+if (isLoading) {
+ // We haven't finished checking for the token yet
+ return ;
+}
+
+const isSignedIn = userToken != null;
+
+return (
+
+
+ {isSignedIn ? (
+
+ ) : (
+
+ )}
+
+
+);
+```
+
+
+
+
+In the above snippet, `isLoading` means that we're still checking if we have a token. This can usually be done by checking if we have a token in `SecureStore` and validating the token.
+
+Next, we're exposing the sign in status via the `SignInContext` so that it's available to the `useIsSignedIn` and `useIsSignedOut` hooks.
+
+In the above example, we have one screen for each case. But you could also define multiple screens. For example, you probably want to define password reset, signup, etc screens as well when the user isn't signed in. Similarly for the screens accessible after sign in, you probably have more than one screen.
+
+
+
+
+We can use [`groups`](static-configuration.md#groups) to define multiple screens:
+
+```js
+const RootStack = createNativeStackNavigator({
+ screens: {
+ // Common screens
+ },
+ groups: {
+ SignedIn: {
+ if: useIsSignedIn,
+ screens: {
+ Home: HomeScreen,
+ Profile: ProfileScreen,
+ },
+ },
+ SignedOut: {
+ if: useIsSignedOut,
+ screens: {
+ SignIn: SignInScreen,
+ SignUp: SignUpScreen,
+ ResetPassword: ResetPasswordScreen,
+ },
+ },
+ },
+});
+```
+
+
+
+
+We can use [`React.Fragment`](https://react.dev/reference/react/Fragment) or [`Group`](group.md) to define multiple screens:
+
+```js
+isSignedIn ? (
+ <>
+
+
+
+ >
+) : (
+ <>
+
+
+ >
+);
+```
+
+:::tip
+
+Instead of having your login-related screens and rest of the screens in two different Stack navigators and render them conditionally, we recommend to use a single Stack navigator and place the conditional inside. This makes it possible to have a proper transition animation during login/logout.
+
+:::
+
+
+
+
+## Implement the logic for restoring the token
+
+:::note
+
+The following is just an example of how you might implement the logic for authentication in your app. You don't need to follow it as is.
+
+:::
+
+From the previous snippet, we can see that we need 3 state variables:
+
+- `isLoading` - We set this to `true` when we're trying to check if we already have a token saved in `SecureStore`.
+- `isSignout` - We set this to `true` when user is signing out, otherwise set it to `false`. This can be used to customize the animation when signing out.
+- `userToken` - The token for the user. If it's non-null, we assume the user is logged in, otherwise not.
+
+So we need to:
+
+- Add some logic for restoring token, signing in and signing out
+- Expose methods for signing in and signing out to other components
+
+We'll use `React.useReducer` and `React.useContext` in this guide. But if you're using a state management library such as Redux or Mobx, you can use them for this functionality instead. In fact, in bigger apps, a global state management library is more suitable for storing authentication tokens. You can adapt the same approach to your state management library.
+
+First we'll need to create a context for auth where we can expose the necessary methods:
+
+```js
+import * as React from 'react';
+
+const AuthContext = React.createContext();
+```
+
+In our component, we will:
+
+- Store the token and loading state in `useReducer`
+- Persist it to `SecureStore` and read it from there on app launch
+- Expose the methods for sign in and sign out to child components using `AuthContext`
+
+So our component will look like this:
+
+
+
+```js name="Signing in and signing out with AuthContext" snack dependencies=expo-secure-store
+// codeblock-focus-start
+import * as React from 'react';
+import * as SecureStore from 'expo-secure-store';
+
+// codeblock-focus-end
+import { Text, TextInput, View } from 'react-native';
+import { createStaticNavigation } from '@react-navigation/native';
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+import { Button } from '@react-navigation/elements';
+
+const AuthContext = React.createContext();
+
+const SignInContext = React.createContext();
+
+function useIsSignedIn() {
+ const isSignedIn = React.useContext(SignInContext);
+ return isSignedIn;
+}
+
+function useIsSignedOut() {
+ return !useIsSignedIn();
+}
+
+function SplashScreen() {
+ return (
+
+ Loading...
+
+ );
+}
+
+function HomeScreen() {
+ const { signOut } = React.useContext(AuthContext);
+
+ return (
+
+ Signed in!
+
+
+ );
+}
+
+function SignInScreen() {
+ const [username, setUsername] = React.useState('');
+ const [password, setPassword] = React.useState('');
+
+ const { signIn } = React.useContext(AuthContext);
+
+ return (
+
+
+
+
+
+ );
+}
+
+// codeblock-focus-start
+export default function App() {
+ const [state, dispatch] = React.useReducer(
+ (prevState, action) => {
+ switch (action.type) {
+ case 'RESTORE_TOKEN':
+ return {
+ ...prevState,
+ userToken: action.token,
+ isLoading: false,
+ };
+ case 'SIGN_IN':
+ return {
+ ...prevState,
+ isSignout: false,
+ userToken: action.token,
+ };
+ case 'SIGN_OUT':
+ return {
+ ...prevState,
+ isSignout: true,
+ userToken: null,
+ };
+ }
+ },
+ {
+ isLoading: true,
+ isSignout: false,
+ userToken: null,
+ }
+ );
+
+ React.useEffect(() => {
+ // Fetch the token from storage then navigate to our appropriate place
+ const bootstrapAsync = async () => {
+ let userToken;
+
+ try {
+ // Restore token stored in `SecureStore` or any other encrypted storage
+ userToken = await SecureStore.getItemAsync('userToken');
+ } catch (e) {
+ // Restoring token failed
+ }
+
+ // After restoring token, we may need to validate it in production apps
+
+ // This will switch to the App screen or Auth screen and this loading
+ // screen will be unmounted and thrown away.
+ dispatch({ type: 'RESTORE_TOKEN', token: userToken });
+ };
+
+ bootstrapAsync();
+ }, []);
+
+ const authContext = React.useMemo(
+ () => ({
+ signIn: async (data) => {
+ // In a production app, we need to send some data (usually username, password) to server and get a token
+ // We will also need to handle errors if sign in failed
+ // After getting token, we need to persist the token using `SecureStore` or any other encrypted storage
+ // In the example, we'll use a dummy token
+
+ dispatch({ type: 'SIGN_IN', token: 'dummy-auth-token' });
+ },
+ signOut: () => dispatch({ type: 'SIGN_OUT' }),
+ signUp: async (data) => {
+ // In a production app, we need to send user data to server and get a token
+ // We will also need to handle errors if sign up failed
+ // After getting token, we need to persist the token using `SecureStore` or any other encrypted storage
+ // In the example, we'll use a dummy token
+
+ dispatch({ type: 'SIGN_IN', token: 'dummy-auth-token' });
+ },
+ }),
+ []
+ );
+
+ if (state.isLoading) {
+ // We haven't finished checking for the token yet
+ return ;
+ }
+
+ const isSignedIn = state.userToken != null;
+
+ return (
+
+
+
+
+
+ );
+}
+
+const RootStack = createNativeStackNavigator({
+ screens: {
+ Home: {
+ if: useIsSignedIn,
+ screen: HomeScreen,
+ },
+ SignIn: {
+ if: useIsSignedOut,
+ screen: SignInScreen,
+ options: {
+ title: 'Sign in',
+ },
+ },
+ },
+});
+
+const Navigation = createStaticNavigation(RootStack);
+// codeblock-focus-end
+```
+
+
+
+
+```js name="Signing in and signing out with AuthContext" snack dependencies=expo-secure-store
+// codeblock-focus-start
+import * as React from 'react';
+import * as SecureStore from 'expo-secure-store';
+
+// codeblock-focus-end
+import { Text, TextInput, View } from 'react-native';
+import { NavigationContainer } from '@react-navigation/native';
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+import { Button } from '@react-navigation/elements';
+
+const AuthContext = React.createContext();
+
+function SplashScreen() {
+ return (
+
+ Loading...
+
+ );
+}
+
+function HomeScreen() {
+ const { signOut } = React.useContext(AuthContext);
+
+ return (
+
+ Signed in!
+
+
+ );
+}
+
+function SignInScreen() {
+ const [username, setUsername] = React.useState('');
+ const [password, setPassword] = React.useState('');
+
+ const { signIn } = React.useContext(AuthContext);
+
+ return (
+
+
+
+
+
+ );
+}
+
+const Stack = createNativeStackNavigator();
+
+// codeblock-focus-start
+export default function App() {
+ const [state, dispatch] = React.useReducer(
+ (prevState, action) => {
+ switch (action.type) {
+ case 'RESTORE_TOKEN':
+ return {
+ ...prevState,
+ userToken: action.token,
+ isLoading: false,
+ };
+ case 'SIGN_IN':
+ return {
+ ...prevState,
+ isSignout: false,
+ userToken: action.token,
+ };
+ case 'SIGN_OUT':
+ return {
+ ...prevState,
+ isSignout: true,
+ userToken: null,
+ };
+ }
+ },
+ {
+ isLoading: true,
+ isSignout: false,
+ userToken: null,
+ }
+ );
+
+ React.useEffect(() => {
+ // Fetch the token from storage then navigate to our appropriate place
+ const bootstrapAsync = async () => {
+ let userToken;
+
+ try {
+ // Restore token stored in `SecureStore` or any other encrypted storage
+ userToken = await SecureStore.getItemAsync('userToken');
+ } catch (e) {
+ // Restoring token failed
+ }
+
+ // After restoring token, we may need to validate it in production apps
+
+ // This will switch to the App screen or Auth screen and this loading
+ // screen will be unmounted and thrown away.
+ dispatch({ type: 'RESTORE_TOKEN', token: userToken });
+ };
+
+ bootstrapAsync();
+ }, []);
+
+ const authContext = React.useMemo(
+ () => ({
+ signIn: async (data) => {
+ // In a production app, we need to send some data (usually username, password) to server and get a token
+ // We will also need to handle errors if sign in failed
+ // After getting token, we need to persist the token using `SecureStore` or any other encrypted storage
+ // In the example, we'll use a dummy token
+
+ dispatch({ type: 'SIGN_IN', token: 'dummy-auth-token' });
+ },
+ signOut: () => dispatch({ type: 'SIGN_OUT' }),
+ signUp: async (data) => {
+ // In a production app, we need to send user data to server and get a token
+ // We will also need to handle errors if sign up failed
+ // After getting token, we need to persist the token using `SecureStore` or any other encrypted storage
+ // In the example, we'll use a dummy token
+
+ dispatch({ type: 'SIGN_IN', token: 'dummy-auth-token' });
+ },
+ }),
+ []
+ );
+
+ return (
+
+
+
+ {state.isLoading ? (
+ // We haven't finished checking for the token yet
+
+ ) : state.userToken == null ? (
+ // No token found, user isn't signed in
+
+ ) : (
+ // User is signed in
+
+ )}
+
+
+
+ );
+}
+// codeblock-focus-end
+```
+
+
+
+
+## Fill in other components
+
+We won't talk about how to implement the text inputs and buttons for the authentication screen, that is outside of the scope of navigation. We'll just fill in some placeholder content.
+
+```js
+function SignInScreen() {
+ const [username, setUsername] = React.useState('');
+ const [password, setPassword] = React.useState('');
+
+ const { signIn } = React.useContext(AuthContext);
+
+ return (
+
+
+
+
+
+ );
+}
+```
+
+You can similarly fill in the other screens according to your requirements.
+
+## Removing shared screens when auth state changes
+
+Consider the following example:
+
+
+
+
+```js
+const RootStack = createNativeStackNavigator({
+ groups: {
+ LoggedIn: {
+ if: useIsSignedIn,
+ screens: {
+ Home: HomeScreen,
+ Profile: ProfileScreen,
+ },
+ },
+ LoggedOut: {
+ if: useIsSignedOut,
+ screens: {
+ SignIn: SignInScreen,
+ SignUp: SignUpScreen,
+ },
+ },
+ },
+ screens: {
+ Help: HelpScreen,
+ },
+});
+```
+
+
+
+
+```js
+isSignedIn ? (
+ <>
+
+
+
+ >
+) : (
+ <>
+
+
+
+ >
+);
+```
+
+
+
+
+Here we have specific screens such as `SignIn`, `Home` etc. which are only shown depending on the sign in state. But we also have the `Help` screen which can be shown regardless of the login status. This also means that if the sign in state changes when the user is in the `Help` screen, they'll stay on the `Help` screen.
+
+This can be a problem, we probably want the user to be taken to the `SignIn` screen or `Home` screen instead of keeping them on the `Help` screen.
+
+
+
+
+To make this work, we can move the `Help` screen to both of the groups instead of keeping it outside. This will ensure that the [`navigationKey`](screen.md#navigation-key) (the name of the group) for the screen changes when the sign in state changes.
+
+So our updated code will look like the following:
+
+```js
+const RootStack = createNativeStackNavigator({
+ groups: {
+ LoggedIn: {
+ if: useIsSignedIn,
+ screens: {
+ Home: HomeScreen,
+ Profile: ProfileScreen,
+ Help: HelpScreen,
+ },
+ },
+ LoggedOut: {
+ if: useIsSignedOut,
+ screens: {
+ SignIn: SignInScreen,
+ SignUp: SignUpScreen,
+ Help: HelpScreen,
+ },
+ },
+ },
+});
+```
+
+
+
+
+To make this work, we can use [`navigationKey`](screen.md#navigation-key). When the `navigationKey` changes, React Navigation will remove all the screen.
+
+So our updated code will look like the following:
+
+```js
+<>
+ {isSignedIn ? (
+ <>
+
+
+ >
+ ) : (
+ <>
+
+
+ >
+ )}
+
+>
+```
+
+If you have a bunch of shared screens, you can also use [`navigationKey` with a `Group`](group.md#navigation-key) to remove all of the screens in the group. For example:
+
+```js
+<>
+ {isSignedIn ? (
+ <>
+
+
+ >
+ ) : (
+ <>
+
+
+ >
+ )}
+
+
+
+
+>
+```
+
+
+
+
+The examples above show stack navigator, but you can use the same approach with any navigator.
+
+By specifying a condition for our screens, we can implement auth flow in a simple way that doesn't require additional logic to make sure that the correct screen is shown.
+
+## Don't manually navigate when conditionally rendering screens
+
+It's important to note that when using such a setup, you **don't manually navigate** to the `Home` screen by calling `navigation.navigate('Home')` or any other method. **React Navigation will automatically navigate to the correct screen** when `isSignedIn` changes - `Home` screen when `isSignedIn` becomes `true`, and to `SignIn` screen when `isSignedIn` becomes `false`. You'll get an error if you attempt to navigate manually.
+
+## Handling deep links after auth
+
+When using deep links, you may want to handle the case where the user opens a deep link that requires authentication.
+
+Example scenario:
+
+- User opens a deep link to `myapp://profile` but is not signed in.
+- The app shows the `SignIn` screen.
+- After the user signs in, you want to navigate them to the `Profile` screen.
+
+To achieve this, you can set [`UNSTABLE_routeNamesChangeBehavior`](navigator.md#route-names-change-behavior) to `"lastUnhandled"`:
+
+:::warning
+
+This API is experimental and may change in a minor release.
+
+:::
+
+
+
+
+```js
+const RootStack = createNativeStackNavigator({
+ // highlight-next-line
+ UNSTABLE_routeNamesChangeBehavior: 'lastUnhandled',
+ screens: {
+ Home: {
+ if: useIsSignedIn,
+ screen: HomeScreen,
+ },
+ SignIn: {
+ if: useIsSignedOut,
+ screen: SignInScreen,
+ options: {
+ title: 'Sign in',
+ },
+ },
+ },
+});
+```
+
+
+
+
+```js
+
+ {isSignedIn ? (
+
+ ) : (
+
+ )}
+
+```
+
+
+
+
+The `UNSTABLE_routeNamesChangeBehavior` option allows you to control how React Navigation handles navigation when the available screens change because of conditions such as authentication state. When `lastUnhandled` is specified, React Navigation will remember the last screen that couldn't be handled, and after the condition changes, it'll automatically navigate to that screen if it's now available.
diff --git a/versioned_docs/version-8.x/bottom-tab-navigator.md b/versioned_docs/version-8.x/bottom-tab-navigator.md
new file mode 100755
index 00000000000..aee5db4c775
--- /dev/null
+++ b/versioned_docs/version-8.x/bottom-tab-navigator.md
@@ -0,0 +1,1576 @@
+---
+id: bottom-tab-navigator
+title: Bottom Tabs Navigator
+sidebar_label: Bottom Tabs
+---
+
+Bottom Tab Navigator displays a set of screens with a tab bar to switch between them.
+
+
+
+
+
+## Installation
+
+To use this navigator, ensure that you have [`@react-navigation/native` and its dependencies (follow this guide)](getting-started.md), then install [`@react-navigation/bottom-tabs`](https://github.com/react-navigation/react-navigation/tree/main/packages/bottom-tabs):
+
+```bash npm2yarn
+npm install @react-navigation/bottom-tabs
+```
+
+## Usage
+
+To use this navigator, import it from `@react-navigation/bottom-tabs`:
+
+```js name="Bottom Tab Navigator" snack static2dynamic
+import * as React from 'react';
+import { Text, View } from 'react-native';
+import {
+ createStaticNavigation,
+ useNavigation,
+} from '@react-navigation/native';
+import { Button } from '@react-navigation/elements';
+// codeblock-focus-start
+import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
+
+// codeblock-focus-end
+function HomeScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ Home Screen
+
+
+ );
+}
+
+function ProfileScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ Profile Screen
+
+
+ );
+}
+
+// codeblock-focus-start
+const MyTabs = createBottomTabNavigator({
+ screens: {
+ Home: HomeScreen,
+ Profile: ProfileScreen,
+ },
+});
+// codeblock-focus-end
+
+const Navigation = createStaticNavigation(MyTabs);
+
+export default function App() {
+ return ;
+}
+```
+
+## API Definition
+
+### Props
+
+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.
+
+It supports the following values:
+
+- `firstRoute` - return to the first screen defined in the navigator (default)
+- `initialRoute` - return to initial screen passed in `initialRouteName` prop, if not passed, defaults to the first screen
+- `order` - return to screen defined before the focused screen
+- `history` - return to last visited screen in the navigator; if the same screen is visited multiple times, the older entries are dropped from the history
+- `fullHistory` - return to last visited screen in the navigator; doesn't drop duplicate entries unlike `history` - this behavior is useful to match how web pages work
+- `none` - do not handle back button
+
+#### `detachInactiveScreens`
+
+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.
+- `descriptors` - The descriptors object containing options for the tab navigator.
+- `navigation` - The navigation object for the tab navigator.
+
+The `state.routes` array contains all the routes defined in the navigator. Each route's options can be accessed using `descriptors[route.key].options`.
+
+Example:
+
+```js name="Custom tab bar" snack tabs=config
+import * as React from 'react';
+import {
+ createStaticNavigation,
+ NavigationContainer,
+} from '@react-navigation/native';
+// codeblock-focus-start
+import { View, Platform } from 'react-native';
+import { useLinkBuilder, useTheme } from '@react-navigation/native';
+import { Text, PlatformPressable } from '@react-navigation/elements';
+import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
+
+function MyTabBar({ state, descriptors, navigation }) {
+ const { colors } = useTheme();
+ const { buildHref } = useLinkBuilder();
+
+ return (
+
+ {state.routes.map((route, index) => {
+ const { options } = descriptors[route.key];
+ const label =
+ options.tabBarLabel !== undefined
+ ? options.tabBarLabel
+ : options.title !== undefined
+ ? options.title
+ : route.name;
+
+ const isFocused = state.index === index;
+
+ const onPress = () => {
+ const event = navigation.emit({
+ type: 'tabPress',
+ target: route.key,
+ canPreventDefault: true,
+ });
+
+ if (!isFocused && !event.defaultPrevented) {
+ navigation.navigate(route.name, route.params);
+ }
+ };
+
+ const onLongPress = () => {
+ navigation.emit({
+ type: 'tabLongPress',
+ target: route.key,
+ });
+ };
+
+ return (
+
+
+ {label}
+
+
+ );
+ })}
+
+ );
+}
+
+// codeblock-focus-end
+
+function HomeScreen() {
+ return (
+
+ Home Screen
+
+ );
+}
+
+function ProfileScreen() {
+ return (
+
+ Profile Screen
+
+ );
+}
+
+// codeblock-tabs=static
+// codeblock-focus-start
+const MyTabs = createBottomTabNavigator({
+ // highlight-next-line
+ tabBar: (props) => ,
+ screens: {
+ Home: HomeScreen,
+ Profile: ProfileScreen,
+ },
+});
+// codeblock-focus-end
+
+const Navigation = createStaticNavigation(MyTabs);
+
+export default function App() {
+ return ;
+}
+// codeblock-tabs-end
+
+// codeblock-tabs=dynamic
+const Tab = createBottomTabNavigator();
+
+// codeblock-focus-start
+function MyTabs() {
+ return (
+ }
+ >
+
+
+
+ );
+}
+// codeblock-focus-end
+
+export default function App() {
+ return (
+
+
+
+ );
+}
+// codeblock-tabs-end
+```
+
+This example will render a basic tab bar with labels.
+
+Note that you **cannot** use the `useNavigation` hook inside the `tabBar` since `useNavigation` is only available inside screens. You get a `navigation` prop for your `tabBar` which you can use instead:
+
+```js
+function MyTabBar({ navigation }) {
+ return (
+
+ );
+}
+```
+
+### Options
+
+The following [options](screen-options.md) can be used to configure the screens in the navigator. These can be specified under `screenOptions` prop of `Tab.Navigator` or `options` prop of `Tab.Screen`.
+
+#### `title`
+
+Generic title that can be used as a fallback for `headerTitle` and `tabBarLabel`.
+
+#### `tabBarLabel`
+
+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',
+},
+```
+
+
+
+#### `tabBarLabelVisibilityMode`
+
+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`
+
+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)
+
+
+- `beside-icon` the label is shown next to the icon (typical for iPad)
+
+
+#### `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. Supported properties:
+
+- `fontFamily`
+- `fontSize`
+- `fontWeight`
+- `fontStyle`
+
+
+
+Example:
+
+```js
+ tabBarLabelStyle: {
+ fontSize: 16,
+ fontFamily: 'Georgia',
+ fontWeight: 300,
+ },
+```
+
+#### `tabBarIcon`
+
+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`.
+
+
+
+#### `tabBarBadgeStyle`
+
+Style for the badge on the tab icon. Supported properties:
+
+- `backgroundColor`
+- `color`
+
+Only supported with `native` implementation on Android.
+
+
+
+Example:
+
+```js
+ tabBarBadgeStyle: {
+ color: 'black',
+ backgroundColor: 'yellow',
+ },
+```
+
+#### `tabBarAccessibilityLabel`
+
+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
+tabBarButton: (props) => ;
+```
+
+#### `tabBarButtonTestID`
+
+ID to locate this tab button in tests.
+
+Only supported with `custom` implementation.
+
+#### `tabBarActiveTintColor`
+
+Color for the icon and label in the active tab.
+
+
+#### `tabBarInactiveTintColor`
+
+Color for the icon and label in the inactive tabs.
+
+
+#### `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.
+
+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
+
+```
+
+You also might need to add a bottom margin to your content if you have an absolutely positioned tab bar. React Navigation won't do it automatically. See [`useBottomTabBarHeight`](#usebottomtabbarheight) for more details.
+
+#### `tabBarBackground`
+
+Function which returns a React Element to use as background for the tab bar. You could render an image, a gradient, blur view etc.
+
+Only supported with `custom` implementation.
+
+Example:
+
+```js
+import { BlurView } from 'expo-blur';
+
+// ...
+
+ (
+
+ ),
+ }}
+>
+```
+
+When using `BlurView`, make sure to set `position: 'absolute'` in `tabBarStyle` as well. You'd also need to use [`useBottomTabBarHeight`](#usebottomtabbarheight) to add bottom padding to your content.
+
+
+
+#### `tabBarPosition`
+
+Position of the tab bar. Available values are:
+
+- `bottom` (Default)
+- `top`
+- `left`
+- `right`
+
+Only supported with `custom` implementation.
+
+When the tab bar is positioned on the `left` or `right`, it is styled as a sidebar. This can be useful when you want to show a sidebar on larger screens and a bottom tab bar on smaller screens:
+
+
+
+
+```js
+const Tabs = createBottomTabNavigator({
+ screenOptions: {
+ tabBarPosition: isLargeScreen ? 'left' : 'bottom',
+ },
+
+ // ...
+});
+```
+
+
+
+
+```js
+
+```
+
+
+
+
+
+
+You can also render a compact sidebar by placing the label below the icon. This is only supported when the [`tabBarVariant`](#tabbarvariant) is set to `material`:
+
+
+
+
+```js
+const Tabs = createBottomTabNavigator({
+ screenOptions: {
+ tabBarPosition: isLargeScreen ? 'left' ? 'bottom',
+ tabBarVariant: isLargeScreen ? 'material' : 'uikit',
+ tabBarLabelPosition: 'below-icon',
+ },
+
+ // ...
+});
+```
+
+
+
+
+```js
+
+```
+
+
+
+
+
+
+#### `tabBarVariant`
+
+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`.
+
+
+
+#### `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.
+
+#### `freezeOnBlur`
+
+Boolean indicating whether to prevent inactive screens from re-rendering. Defaults to `false`.
+Defaults to `true` when `enableFreeze()` from `react-native-screens` package is run at the top of the application.
+
+Only supported on iOS and Android.
+
+#### `popToTopOnBlur`
+
+Boolean indicating whether any nested stack should be popped to the top of the stack when navigating away from this tab. Defaults to `false`.
+
+It only works when there is a stack navigator (e.g. [stack navigator](stack-navigator.md) or [native stack navigator](native-stack-navigator.md)) nested under the tab navigator.
+
+#### `sceneStyle`
+
+Style object for the component wrapping the screen content.
+
+### Header related options
+
+With `custom` implementation, you can find the list of header related options in the [Elements library documentation](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 documented in that page.
+
+With `native` implementation, 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 most 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:
+
+#### `header`
+
+Custom header to use instead of the default header.
+
+This accepts a function that returns a React Element to display as a header. The function receives an object containing the following properties as the argument:
+
+- `navigation` - The navigation object for the current screen.
+- `route` - The route object for the current screen.
+- `options` - The options for the current screen
+- `layout` - Dimensions of the screen, contains `height` and `width` properties.
+
+Example:
+
+```js
+import { getHeaderTitle } from '@react-navigation/elements';
+
+// ..
+
+header: ({ navigation, route, options }) => {
+ const title = getHeaderTitle(options, route.name);
+
+ return ;
+};
+```
+
+To set a custom header for all the screens in the navigator, you can specify this option in the `screenOptions` prop of the navigator.
+
+##### Specify a `height` in `headerStyle`
+
+If your custom header's height differs from the default header height, then you might notice glitches due to measurement being async. Explicitly specifying the height will avoid such glitches.
+
+Example:
+
+```js
+headerStyle: {
+ height: 80, // Specify the height of your custom header
+};
+```
+
+Note that this style is not applied to the header by default since you control the styling of your custom header. If you also want to apply this style to your header, use `options.headerStyle` from the props.
+
+#### `headerShown`
+
+Whether to show or hide the header for the screen. The header is shown by default. Setting this to `false` hides the header.
+
+### Events
+
+The navigator can [emit events](navigation-events.md) on certain actions. Supported events are:
+
+#### `tabPress`
+
+This event is fired when the user presses the tab button for the current screen in the tab bar. By default a tab press does several things:
+
+- If the tab is not focused, tab press will focus that tab
+- If the tab is already focused:
+ - 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`.
+
+:::note
+
+Calling `event.preventDefault` is only supported with the `custom` implementation. With the `native` implementation, the default behavior cannot be prevented.
+
+:::
+
+```js name="Tab Press Event" snack static2dynamic
+import * as React from 'react';
+import { Alert, Text, View } from 'react-native';
+import {
+ createStaticNavigation,
+ useNavigation,
+} from '@react-navigation/native';
+import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
+
+function HomeScreen() {
+ const navigation = useNavigation();
+
+ // codeblock-focus-start
+ React.useEffect(() => {
+ const unsubscribe = navigation.addListener('tabPress', (e) => {
+ // Prevent default behavior
+ e.preventDefault();
+
+ // Do something manually
+ // ...
+ });
+
+ return unsubscribe;
+ }, [navigation]);
+ // codeblock-focus-end
+
+ return (
+
+ Home Screen
+
+ Tab press event is prevented
+
+
+ );
+}
+
+function SettingsScreen() {
+ return (
+
+ Settings Screen
+
+ );
+}
+
+const MyTabs = createBottomTabNavigator({
+ screens: {
+ Home: HomeScreen,
+ Settings: SettingsScreen,
+ },
+});
+
+const Navigation = createStaticNavigation(MyTabs);
+
+export default function App() {
+ return ;
+}
+```
+
+If you have a custom tab bar, make sure to emit this event.
+
+:::note
+
+By default, tabs are rendered lazily. So if you add a listener inside a screen component, it won't receive the event until the screen is focused for the first time. If you need to listen to this event before the screen is focused, you can specify the [listener in the screen config](navigation-events.md#listeners-prop-on-screen) instead.
+
+:::
+
+#### `tabLongPress`
+
+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
+React.useEffect(() => {
+ const unsubscribe = navigation.addListener('tabLongPress', (e) => {
+ // Do something
+ });
+
+ return unsubscribe;
+}, [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:
+
+#### `jumpTo`
+
+Navigates to an existing screen in the tab navigator. The method accepts following arguments:
+
+- `name` - _string_ - Name of the route to jump to.
+- `params` - _object_ - Screen params to use for the destination route.
+
+```js name="Tab Navigator - jumpTo" snack static2dynamic
+import * as React from 'react';
+import { Text, View } from 'react-native';
+import {
+ createStaticNavigation,
+ useNavigation,
+} from '@react-navigation/native';
+import { Button } from '@react-navigation/elements';
+import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
+
+function HomeScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ Home Screen
+
+
+ );
+}
+
+function ProfileScreen({ route }) {
+ return (
+
+ Profile Screen
+ {route.params?.owner && (
+ Owner: {route.params.owner}
+ )}
+
+ );
+}
+
+const MyTabs = createBottomTabNavigator({
+ screens: {
+ Home: HomeScreen,
+ Profile: ProfileScreen,
+ },
+});
+
+const Navigation = createStaticNavigation(MyTabs);
+
+export default function App() {
+ return ;
+}
+```
+
+### Hooks
+
+The bottom tab navigator exports the following hooks:
+
+#### `useBottomTabBarHeight`
+
+This hook returns the height of the bottom tab bar. By default, the screen content doesn't go under the tab bar. However, if you want to make the tab bar absolutely positioned and have the content go under it (e.g. to show a blur effect), it's necessary to adjust the content to take the tab bar height into account.
+
+Example:
+
+```js
+import { useBottomTabBarHeight } from '@react-navigation/bottom-tabs';
+
+function MyComponent() {
+ const tabBarHeight = useBottomTabBarHeight();
+
+ return (
+
+ {/* Content */}
+
+ );
+}
+```
+
+Alternatively, you can use the `BottomTabBarHeightContext` directly if you are using a class component or need it in a reusable component that can be used outside the bottom tab navigator:
+
+```js
+import { BottomTabBarHeightContext } from '@react-navigation/bottom-tabs';
+
+// ...
+
+
+ {tabBarHeight => (
+ /* render something */
+ )}
+
+```
+
+## Animations
+
+By default, switching between tabs doesn't have any animation. You can specify the `animation` option to customize the transition animation.
+
+:::note
+
+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.
+
+
+
+- `shift` - Shifting animation for the screen transition where the screens slightly shift to left/right.
+
+
+
+- `none` - The screen transition doesn't have any animation. This is the default value.
+
+```js name="Bottom Tabs animation" snack static2dynamic
+import * as React from 'react';
+import { View, Text, Easing } from 'react-native';
+import { createStaticNavigation } from '@react-navigation/native';
+import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
+
+function HomeScreen() {
+ return (
+
+ Home!
+
+ );
+}
+
+function ProfileScreen() {
+ return (
+
+ Profile!
+
+ );
+}
+
+// codeblock-focus-start
+const RootTabs = createBottomTabNavigator({
+ screenOptions: {
+ // highlight-start
+ animation: 'fade',
+ // highlight-end
+ },
+ screens: {
+ Home: HomeScreen,
+ Profile: ProfileScreen,
+ },
+});
+// codeblock-focus-end
+
+const Navigation = createStaticNavigation(RootTabs);
+
+export default function App() {
+ return ;
+}
+```
+
+If you need more control over the animation, you can customize individual parts of the animation using the various animation-related options:
+
+### Animation related options
+
+Bottom Tab Navigator exposes various options to configure the transition animation when switching tabs. These transition animations can be customized on a per-screen basis by specifying the options in the `options` for each screen, or for all screens in the tab navigator by specifying them in the `screenOptions`.
+
+- `transitionSpec` - An object that specifies the animation type (`timing` or `spring`) and its options (such as `duration` for `timing`). It contains 2 properties:
+ - `animation` - The animation function to use for the animation. Supported values are `timing` and `spring`.
+ - `config` - The configuration object for the timing function. For `timing`, it can be `duration` and `easing`. For `spring`, it can be `stiffness`, `damping`, `mass`, `overshootClamping`, `restDisplacementThreshold` and `restSpeedThreshold`.
+
+ A config that uses a timing animation looks like this:
+
+ ```js
+ const config = {
+ animation: 'timing',
+ config: {
+ duration: 150,
+ easing: Easing.inOut(Easing.ease),
+ },
+ };
+ ```
+
+ We can pass this config in the `transitionSpec` option:
+
+
+
+
+ ```js
+ {
+ Profile: {
+ screen: Profile,
+ options: {
+ // highlight-start
+ transitionSpec: {
+ animation: 'timing',
+ config: {
+ duration: 150,
+ easing: Easing.inOut(Easing.ease),
+ },
+ },
+ // highlight-end
+ },
+ },
+ }
+ ```
+
+
+
+
+ ```js
+
+ ```
+
+
+
+
+- `sceneStyleInterpolator` - This is a function that specifies interpolated styles for various parts of the scene. It currently supports style for the view containing the screen:
+ - `sceneStyle` - Style for the container view wrapping the screen content.
+
+ The function receives the following properties in its argument:
+ - `current` - Animation values for the current screen:
+ - `progress` - Animated node representing the progress value of the current screen.
+
+ A config that fades the screen looks like this:
+
+ ```js
+ const forFade = ({ current }) => ({
+ sceneStyle: {
+ opacity: current.progress.interpolate({
+ inputRange: [-1, 0, 1],
+ outputRange: [0, 1, 0],
+ }),
+ },
+ });
+ ```
+
+ The value of `current.progress` is as follows:
+ - -1 if the index is lower than the active tab,
+ - 0 if they're active,
+ - 1 if the index is higher than the active tab
+
+ We can pass this function in `sceneStyleInterpolator` option:
+
+
+
+
+ ```js
+ {
+ Profile: {
+ screen: Profile,
+ options: {
+ // highlight-start
+ sceneStyleInterpolator: ({ current }) => ({
+ sceneStyle: {
+ opacity: current.progress.interpolate({
+ inputRange: [-1, 0, 1],
+ outputRange: [0, 1, 0],
+ }),
+ },
+ }),
+ // highlight-end
+ },
+ },
+ }
+ ```
+
+
+
+
+ ```js
+ ({
+ sceneStyle: {
+ opacity: current.progress.interpolate({
+ inputRange: [-1, 0, 1],
+ outputRange: [0, 1, 0],
+ }),
+ },
+ }),
+ // highlight-end
+ }}
+ />
+ ```
+
+
+
+
+Putting these together, you can customize the transition animation for a screen:
+
+Putting these together, you can customize the transition animation for a screen:
+
+```js name="Bottom Tabs custom animation" snack static2dynamic
+import * as React from 'react';
+import { View, Text, Easing } from 'react-native';
+import { createStaticNavigation } from '@react-navigation/native';
+import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
+
+function HomeScreen() {
+ return (
+
+ Home!
+
+ );
+}
+
+function ProfileScreen() {
+ return (
+
+ Profile!
+
+ );
+}
+
+// codeblock-focus-start
+const RootTabs = createBottomTabNavigator({
+ screenOptions: {
+ transitionSpec: {
+ animation: 'timing',
+ config: {
+ duration: 150,
+ easing: Easing.inOut(Easing.ease),
+ },
+ },
+ sceneStyleInterpolator: ({ current }) => ({
+ sceneStyle: {
+ opacity: current.progress.interpolate({
+ inputRange: [-1, 0, 1],
+ outputRange: [0, 1, 0],
+ }),
+ },
+ }),
+ },
+ screens: {
+ Home: HomeScreen,
+ Profile: ProfileScreen,
+ },
+});
+// codeblock-focus-end
+
+const Navigation = createStaticNavigation(RootTabs);
+
+export default function App() {
+ return ;
+}
+```
+
+### Pre-made configs
+
+We also export various configs from the library with ready-made configs that you can use to customize the animations:
+
+#### `TransitionSpecs`
+
+- `FadeSpec` - Configuration for a cross-fade animation between screens.
+- `ShiftSpec` - Configuration for a shifting animation between screens.
+
+Example:
+
+
+
+
+```js
+import { TransitionSpecs } from '@react-navigation/bottom-tabs';
+
+// ...
+
+{
+ Profile: {
+ screen: Profile,
+ options: {
+ // highlight-start
+ transitionSpec: TransitionSpecs.CrossFadeSpec,
+ // highlight-end
+ },
+ },
+}
+```
+
+
+
+
+```js
+import { TransitionSpecs } from '@react-navigation/bottom-tabs';
+
+// ...
+
+;
+```
+
+
+
+
+#### `SceneStyleInterpolators`
+
+- `forFade` - Cross-fade animation for the screen transition where the new screen fades in and the old screen fades out.
+- `forShift` - Shifting animation for the screen transition where the screens slightly shift to left/right.
+
+Example:
+
+
+
+
+```js
+import { SceneStyleInterpolators } from '@react-navigation/bottom-tabs';
+
+// ...
+
+{
+ Profile: {
+ screen: Profile,
+ options: {
+ // highlight-start
+ sceneStyleInterpolator: SceneStyleInterpolators.forFade,
+ // highlight-end
+ },
+ },
+}
+```
+
+
+
+
+```js
+import { SceneStyleInterpolators } from '@react-navigation/bottom-tabs';
+
+// ...
+
+;
+```
+
+
+
+
+#### `TransitionPresets`
+
+We export transition presets that bundle various sets of these options together. A transition preset is an object containing a few animation-related screen options exported under `TransitionPresets`. Currently the following presets are available:
+
+- `FadeTransition` - Cross-fade animation for the screen transition where the new screen fades in and the old screen fades out.
+- `ShiftTransition` - Shifting animation for the screen transition where the screens slightly shift to left/right.
+
+You can spread these presets in `options` to customize the animation for a screen:
+
+Example:
+
+
+
+
+```js
+import { TransitionPresets } from '@react-navigation/bottom-tabs';
+
+// ...
+
+{
+ Profile: {
+ screen: Profile,
+ options: {
+ // highlight-start
+ ...TransitionPresets.FadeTransition,
+ // highlight-end
+ },
+ },
+}
+```
+
+
+
+
+```js
+import { TransitionPresets } from '@react-navigation/bottom-tabs';
+
+// ...
+
+;
+```
+
+
+
diff --git a/versioned_docs/version-8.x/combine-static-with-dynamic.md b/versioned_docs/version-8.x/combine-static-with-dynamic.md
new file mode 100644
index 00000000000..bfb8c80b619
--- /dev/null
+++ b/versioned_docs/version-8.x/combine-static-with-dynamic.md
@@ -0,0 +1,215 @@
+---
+id: combine-static-with-dynamic
+title: Combining static and dynamic APIs
+sidebar_label: Static and dynamic APIs
+---
+
+While the static API has many advantages, it doesn't fit use cases where the navigation configuration needs to be dynamic. So React Navigation supports interop between the static and dynamic APIs.
+
+Keep in mind that the features provided by the static API such as automatic linking configuration and automatic TypeScript types need the whole configuration to be static. If part of the configuration is dynamic, you'll need to handle those parts manually.
+
+There are 2 ways you may want to combine the static and dynamic APIs:
+
+## Static root navigator, dynamic nested navigator
+
+This is useful if you want to keep your configuration static, but need to use a dynamic configuration for a specific navigator.
+
+Let's consider the following example:
+
+- You have a root stack navigator that contains a tab navigator in a screen.
+- The tab navigator is defined using the dynamic API.
+
+Our static configuration would look like this:
+
+```js
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+
+const RootStack = createNativeStackNavigator({
+ screens: {
+ Home: {
+ screen: HomeScreen,
+ },
+ Feed: {
+ screen: FeedScreen,
+ linking: {
+ path: 'feed',
+ },
+ },
+ },
+});
+```
+
+Here, `FeedScreen` is a component that renders a tab navigator and is defined using the dynamic API:
+
+```js
+import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
+
+const Tab = createBottomTabNavigator();
+
+function FeedScreen() {
+ return (
+
+
+
+
+ );
+}
+```
+
+This code will work, but we're missing 2 things:
+
+- Linking configuration for the screens in the top tab navigator.
+- TypeScript types for the screens in the top tab navigator.
+
+Since the nested navigator is defined using the dynamic API, we need to handle these manually. For the linking configuration, we can define the screens in the `linking` property of the `Feed` screen:
+
+```js
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+
+const RootStack = createNativeStackNavigator({
+ screens: {
+ Home: {
+ screen: HomeScreen,
+ },
+ Feed: {
+ screen: FeedScreen,
+ linking: {
+ path: 'feed',
+ // highlight-start
+ screens: {
+ Latest: 'latest',
+ Popular: 'popular',
+ },
+ // highlight-end
+ },
+ },
+ },
+});
+```
+
+Here the `screens` property is the same as how you'd define it with `linking` config with the dynamic API. It can contain configuration for any nested navigators as well. See [configuring links](configuring-links.md) for more details on the API.
+
+For the TypeScript types, we can define the type of the `FeedScreen` component:
+
+```tsx
+import {
+ StaticScreenProps,
+ NavigatorScreenParams,
+} from '@react-navigation/native';
+
+type FeedParamList = {
+ Latest: undefined;
+ Popular: undefined;
+};
+
+// highlight-next-line
+type Props = StaticScreenProps>;
+
+// highlight-next-line
+function FeedScreen(_: Props) {
+ // ...
+}
+```
+
+In the above snippet:
+
+1. We first define the param list type for screens in the navigator that defines params for each screen
+2. Then we use the `NavigatorScreenParams` type to get the type of route's `params` which will include types for the nested screens
+3. Finally, we use the type of `params` with `StaticScreenProps` to define the type of the screen component
+
+This is based on how we'd define the type for a screen with a nested navigator with the dynamic API. See [Type checking screens and params in nested navigator](typescript.md#type-checking-screens-and-params-in-nested-navigator).
+
+## Dynamic root navigator, static nested navigator
+
+This is useful if you already have a dynamic configuration, but want to migrate to the static API. This way you can migrate one navigator at a time.
+
+Let's consider the following example:
+
+- You have a root stack navigator that contains a tab navigator in a screen.
+- The root stack navigator is defined using the dynamic API.
+
+Our dynamic configuration would look like this:
+
+```js
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+
+const RootStack = createNativeStackNavigator();
+
+function RootStackScreen() {
+ return (
+
+
+
+
+ );
+}
+```
+
+Here, `FeedScreen` is a component that renders a tab navigator and is defined using the static API:
+
+```js
+import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
+
+const FeedTabs = createBottomTabNavigator({
+ screens: {
+ Latest: {
+ screen: LatestScreen,
+ },
+ Popular: {
+ screen: PopularScreen,
+ },
+ },
+});
+```
+
+To use the `FeedTabs` navigator for the `Feed` screen, we need to use the `createComponentForStaticNavigation` function:
+
+```js
+import { createComponentForStaticNavigation } from '@react-navigation/native';
+
+// highlight-next-line
+const FeedScreen = createComponentForStaticNavigation(FeedTabs, 'Feed');
+```
+
+In addition, we can generate the TypeScript types for the `FeedTabs` navigator and use it in the types of `RootStack` without needing to write them manually:
+
+```tsx
+import {
+ StaticParamList,
+ NavigatorScreenParams,
+} from '@react-navigation/native';
+
+// highlight-next-line
+type FeedTabsParamList = StaticParamList;
+
+type RootStackParamList = {
+ Home: undefined;
+ // highlight-next-line
+ Feed: NavigatorScreenParams;
+};
+```
+
+Similarly, we can generate the linking configuration for the `FeedTabs` navigator and use it in the linking configuration passed to `NavigationContainer`:
+
+```js
+import { createPathConfigForStaticNavigation } from '@react-navigation/native';
+
+// highlight-next-line
+const feedScreens = createPathConfigForStaticNavigation(FeedTabs);
+
+const linking = {
+ prefixes: ['https://example.com', 'example://'],
+ config: {
+ screens: {
+ Home: '',
+ Feed: {
+ path: 'feed',
+ // highlight-next-line
+ screens: feedScreens,
+ },
+ },
+ },
+};
+```
+
+This will generate the linking configuration for the `Feed` screen based on the configuration of the `FeedTabs` navigator.
diff --git a/versioned_docs/version-8.x/community-libraries.md b/versioned_docs/version-8.x/community-libraries.md
new file mode 100755
index 00000000000..74b8fc2e3e0
--- /dev/null
+++ b/versioned_docs/version-8.x/community-libraries.md
@@ -0,0 +1,31 @@
+---
+id: community-libraries
+title: Community libraries
+sidebar_label: Community libraries
+---
+
+This guide lists various community libraries that can be used alongside React Navigation to enhance its functionality.
+
+:::note
+
+Please refer to the library's documentation to see which version of React Navigation it supports.
+
+:::
+
+## react-native-screen-transitions
+
+A library that provides customizable screen transition animations for React Navigation's [Native Stack Navigator](native-stack-navigator.md).
+
+[Repository](https://github.com/eds2002/react-native-screen-transitions)
+
+### react-navigation-header-buttons
+
+Helps you to render buttons in the navigation bar and handle the styling so you don't have to. It tries to mimic the appearance of native navbar buttons and attempts to offer a simple interface for you to interact with.
+
+[Repository](https://github.com/vonovak/react-navigation-header-buttons)
+
+### react-navigation-props-mapper
+
+Provides simple HOCs that map react-navigation props to your screen components directly - ie. instead of `const user = this.props.route.params.activeUser`, you'd write `const user = this.props.activeUser`.
+
+[Repository](https://github.com/vonovak/react-navigation-props-mapper)
diff --git a/versioned_docs/version-8.x/community-navigators.md b/versioned_docs/version-8.x/community-navigators.md
new file mode 100755
index 00000000000..74c1a3278c8
--- /dev/null
+++ b/versioned_docs/version-8.x/community-navigators.md
@@ -0,0 +1,31 @@
+---
+id: community-navigators
+title: Community navigators
+sidebar_label: Community navigators
+---
+
+This guide lists various community navigators for React Navigation. These navigators offer provide UI components for navigation with the familiar React Navigation API.
+
+If you're looking to build your own navigator, check out the [custom navigators guide](custom-navigators.md).
+
+:::note
+
+Please refer to the library's documentation to see which version of React Navigation it supports.
+
+:::
+
+## React Native Bottom Tabs
+
+This project aims to expose the native Bottom Tabs component to React Native. It exposes SwiftUI's TabView on iOS and the material design tab bar on Android. Using `react-native-bottom-tabs` can bring several benefits, including multi-platform support and a native-feeling tab bar.
+
+[Documentation](https://oss.callstack.com/react-native-bottom-tabs/)
+
+[Repository](https://github.com/callstackincubator/react-native-bottom-tabs)
+
+## BottomNavigation - React Native Paper
+
+The library provides React Navigation integration for its Material Bottom Tabs. Material Bottom Tabs is a material-design themed tab bar on the bottom of the screen that lets you switch between different routes with animation.
+
+[Documentation](https://callstack.github.io/react-native-paper/docs/guides/bottom-navigation/)
+
+[Repository](https://github.com/callstack/react-native-paper)
diff --git a/versioned_docs/version-8.x/community-solutions.md b/versioned_docs/version-8.x/community-solutions.md
new file mode 100755
index 00000000000..18b8f2c81c6
--- /dev/null
+++ b/versioned_docs/version-8.x/community-solutions.md
@@ -0,0 +1,35 @@
+---
+id: community-solutions
+title: Community solutions
+sidebar_label: Community solutions
+---
+
+This guide lists various community navigation solutions built on top of React Navigation that offer a different API or complement React Navigation in some way.
+
+:::note
+
+Please refer to the library's documentation to see which version of React Navigation it supports.
+
+:::
+
+## Expo Router
+
+Expo Router is a file-based router for React Native and web applications built by the Expo team.
+
+[Documentation](https://docs.expo.dev/router/introduction/)
+
+[Repository](https://github.com/expo/expo/tree/main/packages/expo-router)
+
+## Solito
+
+A wrapper around React Navigation and Next.js that lets you share navigation code across platforms. Also, it provides a set of patterns and examples for building cross-platform apps with React Native + Next.js.
+
+[Documentation](https://solito.dev/)
+
+[Repository](https://github.com/nandorojo/solito)
+
+## Navio
+
+Navio provides a different API for React Navigation. It's main goal is to improve DX by building the app layout in one place and using the power of TypeScript to provide route names autocompletion.
+
+[Repository](https://github.com/kanzitelli/rn-navio)
diff --git a/versioned_docs/version-8.x/configuring-links.md b/versioned_docs/version-8.x/configuring-links.md
new file mode 100644
index 00000000000..835cf623fb2
--- /dev/null
+++ b/versioned_docs/version-8.x/configuring-links.md
@@ -0,0 +1,1380 @@
+---
+id: configuring-links
+title: Configuring links
+sidebar_label: Configuring links
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+In this guide, we will configure React Navigation to handle external links. This is necessary if you want to:
+
+1. Handle deep links in React Native apps on Android and iOS
+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.
+
+
+
+
+The [`Navigation`](static-configuration.md#createstaticnavigation) component accepts a [`linking`](static-configuration.md#differences-in-the-linking-prop) prop that makes it easier to handle incoming links:
+
+```js
+import { createStaticNavigation } from '@react-navigation/native';
+
+function App() {
+ return (
+ Loading...}
+ />
+ );
+}
+
+const Navigation = createStaticNavigation(RootStack);
+```
+
+
+
+
+The `NavigationContainer` accepts a [`linking`](navigation-container.md#linking) prop that makes it easier to handle incoming links. The 2 of the most important properties you can specify in the `linking` prop are `prefixes` and `config`:
+
+```js
+import { NavigationContainer } from '@react-navigation/native';
+
+// highlight-start
+const linking = {
+ config: {
+ /* configuration for matching screens with paths */
+ },
+};
+// highlight-end
+
+function App() {
+ return (
+ Loading...}
+ >
+ {/* content */}
+
+ );
+}
+```
+
+
+
+
+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 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:
+
+```js
+const linking = {
+ prefixes: ['example://', 'https://example.com'],
+};
+```
+
+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
+
+To match all subdomains of an associated domain, you can specify a wildcard by prefixing `*`. before the beginning of a specific domain. Note that an entry for `*.example.com` does not match `example.com` because of the period after the asterisk. To enable matching for both `*.example.com` and `example.com`, you need to provide a separate prefix entry for each.
+
+```js
+const linking = {
+ prefixes: ['example://', 'https://example.com', 'https://*.example.com'],
+};
+```
+
+## Filtering certain paths
+
+Sometimes we may not want to handle all incoming links. For example, we may want to filter out links meant for authentication (e.g. `expo-auth-session`) or other purposes instead of navigating to a specific screen.
+
+To achieve this, you can use the `filter` option:
+
+```js
+const linking = {
+ prefixes: ['example://', 'https://example.com'],
+ // highlight-next-line
+ filter: (url) => !url.includes('+expo-auth-session'),
+};
+```
+
+This is not supported on Web as we always need to handle the URL of the page.
+
+## Apps under subpaths
+
+If your app is hosted under a subpath, you can specify the subpath at the top-level of the `config`. For example, if your app is hosted at `https://example.com/app`, you can specify the `path` as `app`:
+
+```js
+const linking = {
+ prefixes: ['example://', 'https://example.com'],
+ config: {
+ // highlight-next-line
+ path: 'app',
+
+ // ...
+ },
+};
+```
+
+It's not possible to specify params here since this doesn't belong to a screen, e.g. `app/:id` won't work.
+
+## Mapping path to route names
+
+
+
+
+If you specify `enabled: 'auto'` in the `linking` prop, React Navigation will automatically generate paths for all screens. For example, if you have a `Profile` screen in the navigator, it'll automatically generate a path for it as `profile`.
+
+If you wish to handle the configuration manually, or want to override the generated path for a specific screen, you can specify `linking` property next to the screen in the navigator to map a path to a screen. For example:
+
+```js
+const RootStack = createStackNavigator({
+ screens: {
+ Profile: {
+ screen: ProfileScreen,
+ // highlight-start
+ linking: {
+ path: 'user',
+ },
+ // highlight-end
+ },
+ Chat: {
+ screen: ChatScreen,
+ // highlight-start
+ linking: {
+ path: 'feed/:sort',
+ },
+ // highlight-end
+ },
+ },
+});
+```
+
+In this example:
+
+- `Chat` screen that handles the URL `/feed` with the param `sort` (e.g. `/feed/latest` - the `Chat` screen will receive a param `sort` with the value `latest`).
+- `Profile` screen that handles the URL `/user`.
+
+Similarly, when you have a nested navigator, you can specify the `linking` property for the screens in the navigator to handle the path for the nested screens:
+
+```js
+const HomeTabs = createBottomTabNavigator({
+ screens: {
+ Home: {
+ screen: HomeScreen,
+ // highlight-start
+ linking: {
+ path: 'home',
+ },
+ // highlight-end
+ },
+ Settings: {
+ screen: SettingsScreen,
+ // highlight-start
+ linking: {
+ path: 'settings',
+ },
+ // highlight-end
+ },
+ },
+});
+
+const RootStack = createStackNavigator({
+ screens: {
+ HomeTabs: {
+ screen: HomeTabs,
+ },
+ Profile: {
+ screen: ProfileScreen,
+ // highlight-start
+ linking: {
+ path: 'user',
+ },
+ // highlight-end
+ },
+ Chat: {
+ screen: ChatScreen,
+ // highlight-start
+ linking: {
+ path: 'feed/:sort',
+ },
+ // highlight-end
+ },
+ },
+});
+```
+
+In the above example, the following path formats are handled:
+
+- `/home` navigates to the `HomeTabs` -> `Home` screen
+- `/settings` navigates to the `HomeTabs` -> `Settings` screen
+- `/user` navigates to the `Profile` screen
+- `/feed/:sort` navigates to the `Chat` screen with the param `sort`
+
+### How does automatic path generation work?
+
+When using automatic path generation with `enabled: 'auto'`, the following rules are applied:
+
+- Screens with an explicit `linking` property are not used for path generation and will be added as-is.
+- Screen names will be converted from `PascalCase` to `kebab-case` to use as the path (e.g. `NewsFeed` -> `news-feed`).
+- Unless a screen has explicit empty path (`path: ''`) to use for the homepage, the first leaf screen encountered will be used as the homepage.
+- Path generation only handles leaf screens, i.e. no path is generated for screens containing nested navigators. It's still possible to specify a path for them with an explicit `linking` property.
+
+Let's say we have the following navigation structure:
+
+```js
+const HomeTabs = createBottomTabNavigator({
+ screens: {
+ Home: {
+ screen: HomeScreen,
+ },
+ Settings: {
+ screen: SettingsScreen,
+ },
+ },
+});
+
+const RootStack = createStackNavigator({
+ screens: {
+ HomeTabs: {
+ screen: HomeTabs,
+ },
+ Profile: {
+ screen: ProfileScreen,
+ },
+ Chat: {
+ screen: ChatScreen,
+ },
+ },
+});
+```
+
+With automatic path generation, the following paths will be generated:
+
+- `/` navigates to the `HomeTabs` -> `Home` screen
+- `/settings` navigates to the `HomeTabs` -> `Settings` screen
+- `/profile` navigates to the `Profile` screen
+- `/chat` navigates to the `Chat` screen
+
+If the URL contains a query string, it'll be passed as params to the screen. For example, the URL `/profile?user=jane` will pass the `user` param to the `Profile` screen.
+
+
+
+
+If you specify a `linking` option, by default React Navigation will use the path segments as the route name when parsing the URL. However, directly translating path segments to route names may not be the expected behavior.
+
+You can specify the [`config`](navigation-container.md#linkingconfig) option in `linking` to control how the deep link is parsed to suit your needs. The config should specify the mapping between route names and path patterns:
+
+```js
+const config = {
+ screens: {
+ Chat: 'feed/:sort',
+ Profile: 'user',
+ },
+};
+```
+
+In this example:
+
+- `Chat` screen that handles the URL `/feed` with the param `sort` (e.g. `/feed/latest` - the `Chat` screen will receive a param `sort` with the value `latest`).
+- `Profile` screen that handles the URL `/user`.
+
+The config option can then be passed in the `linking` prop to the container:
+
+```js
+import { NavigationContainer } from '@react-navigation/native';
+
+const config = {
+ screens: {
+ Chat: 'feed/:sort',
+ Profile: 'user',
+ },
+};
+
+const linking = {
+ prefixes: ['https://example.com', 'example://'],
+ config,
+};
+
+function App() {
+ return (
+ Loading...}>
+ {/* content */}
+
+ );
+}
+```
+
+The config object must match the navigation structure for your app. For example, the above configuration is if you have `Chat` and `Profile` screens in the navigator at the root:
+
+```js
+function App() {
+ return (
+
+
+
+
+ );
+}
+```
+
+If your `Chat` screen is inside a nested navigator, we'd need to account for that. For example, consider the following structure where your `Profile` screen is at the root, but the `Chat` screen is nested inside `Home`:
+
+```js
+function App() {
+ return (
+
+
+
+
+ );
+}
+
+function HomeScreen() {
+ return (
+
+
+
+ );
+}
+```
+
+For the above structure, our configuration will look like this:
+
+```js
+const config = {
+ screens: {
+ Home: {
+ screens: {
+ Chat: 'feed/:sort',
+ },
+ },
+ Profile: 'user',
+ },
+};
+```
+
+Similarly, any nesting needs to be reflected in the configuration.
+
+
+
+
+How it works
+
+The linking works by translating the URL to a valid [navigation state](navigation-state.md) and vice versa using the configuration provided. For example, the path `/rooms/chat?user=jane` may be translated to a state object like this:
+
+```js
+const state = {
+ routes: [
+ {
+ name: 'rooms',
+ state: {
+ routes: [
+ {
+ name: 'chat',
+ params: { user: 'jane' },
+ },
+ ],
+ },
+ },
+ ],
+};
+```
+
+For example, you might want to parse the path `/feed/latest` to something like:
+
+```js
+const state = {
+ routes: [
+ {
+ name: 'Chat',
+ params: {
+ sort: 'latest',
+ },
+ },
+ ];
+}
+```
+
+See [Navigation State reference](navigation-state.md) for more details on how the state object is structured.
+
+
+
+## Passing params
+
+A common use case is to pass params to a screen to pass some data. For example, you may want the `Profile` screen to have an `id` param to know which user's profile it is. It's possible to pass params to a screen through a URL when handling deep links.
+
+By default, query params are parsed to get the params for a screen. For example, with the above example, the URL `/user?id=jane` will pass the `id` param to the `Profile` screen.
+
+You can also customize how the params are parsed from the URL. Let's say you want the URL to look like `/user/jane` where the `id` param is `jane` instead of having the `id` in query params. You can do this by specifying `user/:id` for the `path`. **When the path segment starts with `:`, it'll be treated as a param**. For example, the URL `/user/jane` would resolve to `Profile` screen with the string `jane` as a value of the `id` param and will be available in `route.params.id` in `Profile` screen.
+
+By default, all params are treated as strings. You can also customize how to parse them by specifying a function in the `parse` property to parse the param, and a function in the `stringify` property to convert it back to a string.
+
+If you wanted to resolve `/user/@jane/settings` to result in the params `{ id: 'jane' section: 'settings' }`, you could make `Profile`'s config to look like this:
+
+
+
+
+```js
+const RootStack = createStackNavigator({
+ screens: {
+ Profile: {
+ screen: ProfileScreen,
+ // highlight-start
+ linking: {
+ path: 'user/:id/:section',
+ parse: {
+ id: (id) => id.replace(/^@/, ''),
+ },
+ stringify: {
+ id: (id) => `@${id}`,
+ },
+ },
+ // highlight-end
+ },
+ },
+});
+```
+
+
+
+
+```js
+const config = {
+ screens: {
+ Profile: {
+ // highlight-start
+ path: 'user/:id/:section',
+ parse: {
+ id: (id) => id.replace(/^@/, ''),
+ },
+ stringify: {
+ id: (id) => `@${id}`,
+ },
+ // highlight-end
+ },
+ },
+};
+```
+
+
+
+
+
+Result Navigation State
+
+With this configuration, the path `/user/@jane/settings` will resolve to the following state object:
+
+```js
+const state = {
+ routes: [
+ {
+ name: 'Profile',
+ params: { id: 'jane', section: 'settings' },
+ },
+ ],
+};
+```
+
+
+
+## Marking params as optional
+
+Sometimes a param may or may not be present in the URL depending on certain conditions. For example, in the above scenario, you may not always have the section parameter in the URL, i.e. both `/user/jane/settings` and `/user/jane` should go to the `Profile` screen, but the `section` param (with the value `settings` in this case) may or may not be present.
+
+In this case, you would need to mark the `section` param as optional. You can do it by adding the `?` suffix after the param name:
+
+
+
+
+```js
+const RootStack = createStackNavigator({
+ screens: {
+ Profile: {
+ screen: ProfileScreen,
+ linking: {
+ // highlight-next-line
+ path: 'user/:id/:section?',
+ parse: {
+ id: (id) => `user-${id}`,
+ },
+ stringify: {
+ id: (id) => id.replace(/^user-/, ''),
+ },
+ },
+ },
+ },
+});
+```
+
+
+
+
+```js
+const config = {
+ screens: {
+ Profile: {
+ // highlight-next-line
+ path: 'user/:id/:section?',
+ parse: {
+ id: (id) => `user-${id}`,
+ },
+ stringify: {
+ id: (id) => id.replace(/^user-/, ''),
+ },
+ },
+ },
+};
+```
+
+
+
+
+
+Result Navigation State
+
+With this configuration, the path `/user/jane` will resolve to the following state object:
+
+```js
+const state = {
+ routes: [
+ {
+ name: 'Profile',
+ params: { id: 'user-jane' },
+ },
+ ],
+};
+```
+
+If the URL contains a `section` param (e.g. `/user/jane/settings`), this will result in the following with the same config:
+
+```js
+const state = {
+ routes: [
+ {
+ name: 'Profile',
+ params: { id: 'user-jane', section: 'settings' },
+ },
+ ],
+};
+```
+
+
+
+## Handling unmatched routes or 404
+
+If your app is opened with an invalid URL, most of the times you'd want to show an error page with some information. On the web, this is commonly known as 404 - or page not found error.
+
+To handle this, you'll need to define a catch-all route that will be rendered if no other routes match the path. You can do it by specifying `*` for the path matching pattern:
+
+
+
+
+```js
+const HomeTabs = createBottomTabNavigator({
+ screens: {
+ Feed: {
+ screen: FeedScreen,
+ },
+ Profile: {
+ screen: HomeScreen,
+ linking: {
+ path: 'users/:id',
+ },
+ },
+ Settings: {
+ screen: SettingsScreen,
+ linking: {
+ path: 'settings',
+ },
+ },
+ },
+});
+
+const RootStack = createStackNavigator({
+ screens: {
+ Home: {
+ screen: HomeTabs,
+ },
+ NotFound: {
+ screen: NotFoundScreen,
+ linking: {
+ // highlight-next-line
+ path: '*',
+ },
+ },
+ },
+});
+```
+
+
+
+
+```js
+const config = {
+ screens: {
+ Home: {
+ initialRouteName: 'Feed',
+ screens: {
+ Profile: 'users/:id',
+ Settings: 'settings',
+ },
+ },
+ NotFound: {
+ // highlight-start
+ path: '*',
+ },
+ },
+};
+```
+
+
+
+
+Here, we have defined a route named `NotFound` and set it to match `*` aka everything. If the path didn't match `user/:id` or `settings`, it'll be matched by this route.
+
+
+Result Navigation State
+
+With this configuration, a path like `/library` or `/settings/notification` will resolve to the following state object:
+
+```js
+const state = {
+ routes: [{ name: 'NotFound' }],
+};
+```
+
+
+
+You can even go more specific, for example, say if you want to show a different screen for invalid paths under `/settings`, you can specify such a pattern under `Settings`:
+
+
+
+
+```js
+const SettingsStack = createStackNavigator({
+ screens: {
+ UserSettings: {
+ screen: UserSettingsScreen,
+ linking: {
+ path: 'user-settings',
+ },
+ },
+ InvalidSettings: {
+ screen: InvalidSettingsScreen,
+ linking: {
+ // highlight-next-line
+ path: '*',
+ },
+ },
+ },
+});
+
+const HomeTabs = createBottomTabNavigator({
+ screens: {
+ Feed: {
+ screen: FeedScreen,
+ },
+ Profile: {
+ screen: HomeScreen,
+ linking: {
+ path: 'users/:id',
+ },
+ },
+ Settings: {
+ screen: SettingsStack,
+ },
+ },
+});
+
+const RootStack = createStackNavigator({
+ screens: {
+ Home: {
+ screen: HomeTabs,
+ },
+ NotFound: {
+ screen: NotFoundScreen,
+ linking: {
+ path: '*',
+ },
+ },
+ },
+});
+```
+
+
+
+
+```js
+const config = {
+ screens: {
+ Home: {
+ initialRouteName: 'Feed',
+ screens: {
+ Profile: 'users/:id',
+ Settings: {
+ path: 'settings',
+ screens: {
+ InvalidSettings: '*',
+ },
+ },
+ },
+ },
+ NotFound: '*',
+ },
+};
+```
+
+
+
+
+
+Result Navigation State
+
+With this configuration, the path `/settings/notification` will resolve to the following state object:
+
+```js
+const state = {
+ routes: [
+ {
+ name: 'Home',
+ state: {
+ index: 1,
+ routes: [
+ { name: 'Feed' },
+ {
+ name: 'Settings',
+ state: {
+ routes: [
+ { name: 'InvalidSettings', path: '/settings/notification' },
+ ],
+ },
+ },
+ ],
+ },
+ },
+ ],
+};
+```
+
+
+
+The `route` passed to the `NotFound` screen will contain a `path` property which corresponds to the path that opened the page. If you need, you can use this property to customize what's shown in this screen, e.g. load the page in a `WebView`:
+
+```js
+function NotFoundScreen({ route }) {
+ if (route.path) {
+ return ;
+ }
+
+ return This screen doesn't exist!;
+}
+```
+
+When doing server rendering, you'd also want to return correct status code for 404 errors. See [server rendering docs](server-rendering.md#handling-404-or-other-status-codes) for a guide on how to handle it.
+
+## Rendering an initial route
+
+Sometimes you want to ensure that a certain screen will always be present as the first screen in the navigator's state. You can use the `initialRouteName` property to specify the screen to use for the initial screen.
+
+In the above example, if you want the `Feed` screen to be the initial route in the navigator under `Home`, your config will look like this:
+
+
+
+
+```js
+const HomeTabs = createBottomTabNavigator({
+ screens: {
+ Feed: {
+ screen: FeedScreen,
+ },
+ Profile: {
+ screen: HomeScreen,
+ linking: {
+ path: 'users/:id',
+ },
+ },
+ Settings: {
+ screen: SettingsScreen,
+ linking: {
+ path: 'settings',
+ },
+ },
+ },
+});
+
+const RootStack = createStackNavigator({
+ screens: {
+ Home: {
+ screen: HomeTabs,
+ linking: {
+ // highlight-next-line
+ initialRouteName: 'Feed',
+ },
+ },
+ NotFound: {
+ screen: NotFoundScreen,
+ linking: {
+ path: '*',
+ },
+ },
+ },
+});
+```
+
+
+
+
+```js
+const config = {
+ screens: {
+ Home: {
+ // highlight-next-line
+ initialRouteName: 'Feed',
+ screens: {
+ Profile: 'users/:id',
+ Settings: 'settings',
+ },
+ },
+ },
+};
+```
+
+
+
+
+
+Result Navigation State
+
+With this configuration, the path `/users/42` will resolve to the following state object:
+
+```js
+const state = {
+ routes: [
+ {
+ name: 'Home',
+ state: {
+ index: 1,
+ routes: [
+ { name: 'Feed' },
+ {
+ name: 'Profile',
+ params: { id: '42' },
+ },
+ ],
+ },
+ },
+ ],
+};
+```
+
+
+
+:::warning
+
+The `initialRouteName` will add the screen to React Navigation's state only. If your app is running on the Web, the browser's history will not contain this screen as the user has never visited it. So, if the user presses the browser's back button, it'll not go back to this screen.
+
+:::
+
+Another thing to keep in mind is that it's not possible to pass params to the initial screen through the URL. So make sure that your initial route doesn't need any params or specify `initialParams` in the screen configuration to pass the required params.
+
+In this case, any params in the URL are only passed to the `Profile` screen which matches the path pattern `users/:id`, and the `Feed` screen doesn't receive any params. If you want to have the same params in the `Feed` screen, you can specify a [custom `getStateFromPath` function](navigation-container.md#linkinggetstatefrompath) and copy those params.
+
+Similarly, if you want to access params of a parent screen from a child screen, you can use [React Context](https://react.dev/reference/react/useContext) to expose them.
+
+## Matching exact paths
+
+By default, paths defined for each screen are matched against the URL relative to their parent screen's path. Consider the following config:
+
+
+
+
+```js
+const ProfileTabs = createBottomTabNavigator({
+ screens: {
+ Profile: {
+ screen: HomeScreen,
+ linking: {
+ path: 'users/:id',
+ },
+ },
+ },
+});
+
+const RootStack = createStackNavigator({
+ screens: {
+ Home: {
+ screen: ProfileTabs,
+ linking: {
+ path: 'feed',
+ },
+ },
+ },
+});
+```
+
+
+
+
+```js
+const config = {
+ screens: {
+ Home: {
+ path: 'feed',
+ screens: {
+ Profile: 'users/:id',
+ },
+ },
+ },
+};
+```
+
+
+
+
+Here, you have a `path` property defined for the `Home` screen, as well as the child `Profile` screen. The profile screen specifies the path `users/:id`, but since it's nested inside a screen with the path `feed`, it'll try to match the pattern `feed/users/:id`.
+
+This will result in the URL `/feed` navigating to `Home` screen, and `/feed/users/cal` navigating to the `Profile` screen.
+
+In this case, it makes more sense to navigate to the `Profile` screen using a URL like `/users/cal`, rather than `/feed/users/cal`. To achieve this, you can override the relative matching behavior to `exact` matching:
+
+
+
+
+```js
+const ProfileTabs = createBottomTabNavigator({
+ screens: {
+ Profile: {
+ screen: HomeScreen,
+ linking: {
+ path: 'users/:id',
+ // highlight-next-line
+ exact: true,
+ },
+ },
+ },
+});
+
+const RootStack = createStackNavigator({
+ screens: {
+ Home: {
+ screen: ProfileTabs,
+ linking: {
+ path: 'feed',
+ },
+ },
+ },
+});
+```
+
+
+
+
+```js
+const config = {
+ screens: {
+ Home: {
+ path: 'feed',
+ screens: {
+ Profile: {
+ path: 'users/:id',
+ // highlight-next-line
+ exact: true,
+ },
+ },
+ },
+ },
+};
+```
+
+
+
+
+With `exact` property set to `true`, `Profile` will ignore the parent screen's `path` config and you'll be able to navigate to `Profile` using a URL like `users/cal`.
+
+## Omitting a screen from path
+
+Sometimes, you may not want to have the route name of a screen in the path. For example, let's say you have a `Home` screen and the following config. When the page is opened in the browser you'll get `/home` as the URL:
+
+
+
+
+```js
+const RootStack = createStackNavigator({
+ screens: {
+ Home: {
+ screen: ProfileScreen,
+ linking: {
+ path: 'home',
+ },
+ },
+ Profile: {
+ screen: HomeScreen,
+ linking: {
+ path: 'users/:id',
+ },
+ },
+ },
+});
+```
+
+
+
+
+```js
+const config = {
+ screens: {
+ Home: {
+ path: 'home',
+ },
+ Profile: 'users/:id',
+ },
+};
+```
+
+
+
+
+But it'll be nicer if the URL was just `/` when visiting the home screen.
+
+You can specify an empty string as path or not specify a path at all, and React Navigation won't add the screen to the path (think of it like adding empty string to the path, which doesn't change anything):
+
+
+
+
+```js
+const RootStack = createStackNavigator({
+ screens: {
+ Home: {
+ screen: ProfileScreen,
+ linking: {
+ path: '',
+ },
+ },
+ Profile: {
+ screen: HomeScreen,
+ linking: {
+ path: 'users/:id',
+ },
+ },
+ },
+});
+```
+
+
+
+
+```js
+const config = {
+ screens: {
+ Home: {
+ path: '',
+ },
+ Profile: 'users/:id',
+ },
+};
+```
+
+
+
+
+## Serializing and parsing params
+
+Since URLs are strings, any params you have for routes are also converted to strings when constructing the path.
+
+For example, say you have the URL `/chat/1589842744264` with the following config:
+
+
+
+
+```js
+const RootStack = createStackNavigator({
+ screens: {
+ Chat: {
+ screen: ChatScreen,
+ linking: {
+ path: 'chat/:date',
+ },
+ },
+ },
+});
+```
+
+
+
+
+```js
+const config = {
+ screens: {
+ Chat: 'chat/:date',
+ },
+};
+```
+
+
+
+
+When handling the URL, your params will look like this:
+
+```yml
+{ date: '1589842744264' }
+```
+
+Here, the `date` param was parsed as a string because React Navigation doesn't know that it's supposed to be a timestamp, and hence number. You can customize it by providing a custom function to use for parsing:
+
+
+
+
+```js
+const RootStack = createStackNavigator({
+ screens: {
+ Chat: {
+ screen: ChatScreen,
+ linking: {
+ path: 'chat/:date',
+ parse: {
+ date: Number,
+ },
+ },
+ },
+ },
+});
+```
+
+
+
+
+```js
+const config = {
+ screens: {
+ Chat: {
+ path: 'chat/:date',
+ parse: {
+ date: Number,
+ },
+ },
+ },
+};
+```
+
+
+
+
+You can also provide a your own function to serialize the params. For example, let's say that you want to use a DD-MM-YYYY format in the path instead of a timestamp:
+
+
+
+
+```js
+const RootStack = createStackNavigator({
+ screens: {
+ Chat: {
+ screen: ChatScreen,
+ linking: {
+ path: 'chat/:date',
+ parse: {
+ date: (date) => new Date(date).getTime(),
+ },
+ stringify: {
+ date: (date) => {
+ const d = new Date(date);
+
+ return d.getFullYear() + '-' + d.getMonth() + '-' + d.getDate();
+ },
+ },
+ },
+ },
+ },
+});
+```
+
+
+
+
+```js
+const config = {
+ screens: {
+ Chat: {
+ path: 'chat/:date',
+ parse: {
+ date: (date) => new Date(date).getTime(),
+ },
+ stringify: {
+ date: (date) => {
+ const d = new Date(date);
+
+ return d.getFullYear() + '-' + d.getMonth() + '-' + d.getDate();
+ },
+ },
+ },
+ },
+};
+```
+
+
+
+
+Depending on your requirements, you can use this functionality to parse and stringify more complex data.
+
+## Matching regular expressions
+
+If you need more complex matching logic, you can use regular expressions to match the path. For example, if you want to use the pattern `@username` to match a user's profile, you can use a regular expression to match the path. This allows you to have the same path pattern for multiple screens, but fine-tune the matching logic to be more specific for a particular screen.
+
+Regular expressions can be specified between parentheses `(` and `)` in the after a param name. For example:
+
+
+
+
+```js
+const RootStack = createStackNavigator({
+ screens: {
+ Feed: {
+ screen: FeedScreen,
+ linking: {
+ path: ':sort(latest|popular)',
+ },
+ },
+ Profile: {
+ screen: ProfileScreen,
+ linking: {
+ path: ':username(@[A-Za-z0-9_]+)',
+ },
+ },
+ },
+});
+```
+
+
+
+
+```js
+const config = {
+ screens: {
+ Feed: ':sort(latest|popular)',
+ Profile: ':username(@[A-Za-z0-9_]+)',
+ },
+};
+```
+
+
+
+
+This will only match the path if it starts with `@` followed by alphanumeric characters or underscores. For example, the URL `/@jane` will match the `Profile` screen, but `/jane` won't.
+
+Regular expressions are intended to only match path segments, not the entire path. So avoid using `/`, `^`, `$`, etc. in the regular expressions.
+
+:::warning
+
+Regular expressions are an advanced feature. They cannot be validated to warn you about potential issues, so it's up to you to ensure that the regular expression is correct.
+
+:::
+
+## Alias for paths
+
+If you want to have multiple paths for the same screen, you can use the `alias` property to specify an array of paths. This can be useful to keep backward compatibility with old URLs while transitioning to a new URL structure.
+
+For example, if you want to match both `/users/:id` and `/:id` to the `Profile` screen, you can do this:
+
+
+
+
+```js
+const RootStack = createStackNavigator({
+ screens: {
+ Profile: {
+ screen: ProfileScreen,
+ linking: {
+ path: ':id',
+ alias: ['users/:id'],
+ },
+ },
+ },
+});
+```
+
+
+
+
+```js
+const config = {
+ screens: {
+ Profile: {
+ path: ':id',
+ alias: ['users/:id'],
+ },
+ },
+};
+```
+
+
+
+
+In this case, when the URL is `/users/jane` or `/jane`, it'll match the `Profile` screen. The `path` is the primary pattern that will be used to generate the URL, e.g. when navigating to the `Profile` screen in the app on the Web. The patterns in `alias` will be ignored when generating URLs. The `alias` patterns are not used for matching any child screens in nested navigators.
+
+On the web, if a screen containing an alias contains a nested navigator, the URL matching the alias will only be used to match the screen, and will be updated to the URL of the focused child screen once the app renders.
+
+Each item in the `alias` array can be a string matching the syntax of the `path` property, or an object with the following properties:
+
+- `path` (required) - The path pattern to match.
+- `exact` - Whether to match the path exactly. Defaults to `false`. See [Matching exact paths](#matching-exact-paths) for more details.
+- `parse` - Function to parse path segments into param values. See [Passing params](#passing-params) for more details.
+
+## Advanced cases
+
+For some advanced cases, specifying the mapping may not be sufficient. To handle such cases, you can specify a custom function to parse the URL into a state object ([`getStateFromPath`](navigation-container.md#linkinggetstatefrompath)), and a custom function to serialize the state object into an URL ([`getPathFromState`](navigation-container.md#linkinggetpathfromstate)).
+
+Example:
+
+```js
+const linking = {
+ prefixes: ['https://example.com', 'example://'],
+ getStateFromPath: (path, options) => {
+ // Return a state object here
+ // You can also reuse the default logic by importing `getStateFromPath` from `@react-navigation/native`
+ },
+ getPathFromState(state, config) {
+ // Return a path string here
+ // You can also reuse the default logic by importing `getPathFromState` from `@react-navigation/native`
+ },
+
+ // ...
+};
+```
+
+## Playground
+
+import LinkingTester from '@site/src/components/LinkingTester'
+
+
+
+
+Playground is not available for static config.
+
+
+
+
+You can play around with customizing the config and path below, and see how the path is parsed.
+
+
+
+
+
diff --git a/versioned_docs/version-8.x/contributing.md b/versioned_docs/version-8.x/contributing.md
new file mode 100755
index 00000000000..ec05c128ca0
--- /dev/null
+++ b/versioned_docs/version-8.x/contributing.md
@@ -0,0 +1,159 @@
+---
+id: contributing
+title: React Navigation contributor guide
+sidebar_label: Contributing
+---
+
+Want to help improve React Navigation? Your help would be greatly appreciated!
+
+Here are some of the ways to contribute to the project:
+
+- [Contributing](#contributing)
+ - [Reporting Bugs](#reporting-bugs)
+ - [Improving the Documentation](#improving-the-documentation)
+ - [Responding to Issues](#responding-to-issues)
+ - [Bug Fixes](#bug-fixes)
+ - [Suggesting a Feature](#suggesting-a-feature)
+ - [Big Pull Requests](#big-pull-requests)
+
+And here are a few helpful resources to aid in getting started:
+
+- [Information](#information)
+ - [Issue Template](#issue-template)
+ - [Pull Request Template](#pull-request-template)
+ - [Forking the Repository](#forking-the-repository)
+ - [Code Review Guidelines](#code-review-guidelines)
+ - [Run the Example App](#run-the-example-app)
+ - [Run Tests](#run-tests)
+
+## Contributing
+
+### Reporting Bugs
+
+You can't write code without writing the occasional bug. Especially as React Navigation is moving quickly, bugs happen. When you think you've found one here's what to do:
+
+1. Search the existing issues for one like what you're seeing. If you see one, add a 👍 reaction (please no +1 comments). Read through the comments and see if you can provide any more valuable information to the thread
+2. If there are no other issues like yours then create a new one. Be sure to follow the [issue template](https://github.com/react-navigation/react-navigation/blob/main/.github/ISSUE_TEMPLATE/bug-report.yml).
+
+Creating a high quality reproduction is critical. Without it we likely can't fix the bug and, in an ideal situation, you'll find out that it's not actually a bug of the library but simply done incorrectly in your project. Instant bug fix!
+
+### Improving the Documentation
+
+Any successful projects needs quality documentation and React Navigation is no different.
+
+Read more about the documentation on the [react-navigation/react-navigation.github.io repository](https://github.com/react-navigation/react-navigation.github.io).
+
+### Responding to Issues
+
+Another great way to contribute to React Navigation is by responding to issues. Maybe it's answering someone's question, pointing out a small typo in their code, or helping them put together a reproduction. If you're interested in a more active role in React Navigation start with responding to issues - not only is it helpful but it demonstrates your commitment and knowledge of the code!
+
+### Bug Fixes
+
+Find a bug, fix it up, all day long you'll have good luck! Like it was mentioned earlier, bugs happen. If you find a bug do the following:
+
+1. Check if a pull request already exists addressing that bug. If it does give it a review and leave your comments
+2. If there isn't already a pull request then figure out the fix! If it's relatively small go ahead and fix it and submit a pull request. If it's a decent number of changes file an issue first so we can discuss it (see the [Big Pull Requests](#big-pull-requests) section)
+3. If there is an issue related to that bug leave a comment on it, linking to your pull request, so others know it's been addressed.
+
+Check out the [help wanted](https://github.com/react-navigation/react-navigation/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22) and [good first issue](https://github.com/react-navigation/react-navigation/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) tags to see where you can start helping out!
+
+### Suggesting a Feature
+
+Is there something you want to see from React Navigation? Please [create a feature request on Canny](https://react-navigation.canny.io/feature-requests).
+
+### Big Pull Requests
+
+For any changes that will add/remove/modify multiple files in the project (new features or bug fixes) hold off on writing code right away. There's a few reasons for that
+
+1. Big pull requests take a lot of time to review and it's sometimes hard to pick up the context
+2. Often you may not have to make as big of a change as you expect
+
+With that in mind, here's the suggestion
+
+1. Open an issue and clearly define what it is you want to accomplish and how you intend to accomplish it
+2. Discuss that solution with the community and maintainers. Provide context, establish edge cases, and figure out the design
+3. Decide on a plan of action
+4. Write the code and submit the PR
+5. Review the PR. This can take some time but, if you followed the steps above, hopefully it won't take too much time.
+
+The reason we want to do this is to save everyone time. Maybe that feature already exists but isn't documented? Or maybe it doesn't fit with the library. Regardless, by discussing a major change up front you're saving your time and others time as well.
+
+## Information
+
+### Issue Template
+
+Before submitting an issue, please take a look at the [issue template](https://github.com/react-navigation/react-navigation/blob/main/.github/ISSUE_TEMPLATE/bug-report.yml) and follow it. This is in place to help everyone better understand the issue you're having and reduce the back and forth to get the necessary information.
+
+Yes, it takes time and effort to complete the issue template. But that's the only way to ask high quality questions that actually get responses.
+
+Would you rather take 1 minute to create an incomplete issue report and wait months to get any sort of response? Or would you rather take 20 minutes to fill out a high quality issue report, with all the necessary elements, and get a response in days? It's also a respectful thing to do for anyone willing to take the time to review your issue.
+
+### Pull Request Template
+
+Much like the issue template, the [pull request template](https://github.com/react-navigation/react-navigation/blob/main/.github/PULL_REQUEST_TEMPLATE.md) lays out instructions to ensure your pull request gets reviewed in a timely manner and reduces the back and forth. Make sure to look it over before you start writing any code.
+
+### Forking the Repository
+
+- Fork the [`repo`](https://github.com/react-navigation/react-navigation) on GitHub
+- Run these commands in the terminal to download locally and install it:
+
+```bash
+git clone https://github.com//navigation-ex.git
+cd navigation-ex
+git remote add upstream https://github.com/react-navigation/react-navigation.git
+yarn
+```
+
+The project uses a monorepo structure for the packages managed by [yarn workspaces](https://yarnpkg.com/lang/en/docs/workspaces/) and [lerna](https://lerna.js.org). All of the packages are under the [packages/](https://github.com/react-navigation/react-navigation/tree/main/packages) directory.
+
+### Code Review Guidelines
+
+Look around. Match the style of the rest of the codebase. This project uses ESLint to ensure consistency throughout the project. You can check your project by running:
+
+```bash
+yarn lint
+```
+
+If any errors occur you'll either have to manually fix them or you can attempt to automatically fix them by running:
+
+```bash
+yarn lint --fix
+```
+
+The codebase is written in TypeScript, and must pass typecheck. To typecheck files, run:
+
+```bash
+yarn typescript
+```
+
+It's useful to run typechecking in watch mode when working on the project. To do it, run:
+
+```bash
+yarn typescript --watch
+```
+
+### Run the Example App
+
+The [example app](https://github.com/react-navigation/react-navigation/tree/main/packages/example) includes a variety of patterns and is used as a simple way for contributors to manually integration test changes.
+
+While developing, you can run the [example app](https://github.com/react-navigation/react-navigation/tree/main/example) with [Expo](https://expo.io/) to test your changes:
+
+```bash
+yarn example start
+```
+
+### Run Tests
+
+React Navigation has tests implemented in [Jest](https://facebook.github.io/jest/). To run either of these, from the React Navigation directory, run either of the following commands (after installing the `node_modules`) to run tests or type-checking.
+
+```bash
+yarn test
+```
+
+It's useful to run tests in watch mode when working on the project. To do it, run:
+
+```bash
+yarn test --watch
+```
+
+These commands will be run by our CI and are required to pass before any contributions are merged.
diff --git a/versioned_docs/version-8.x/custom-android-back-button-handling.md b/versioned_docs/version-8.x/custom-android-back-button-handling.md
new file mode 100755
index 00000000000..ce741a6180e
--- /dev/null
+++ b/versioned_docs/version-8.x/custom-android-back-button-handling.md
@@ -0,0 +1,254 @@
+---
+id: custom-android-back-button-handling
+title: Custom Android back button behavior
+sidebar_label: Android back button behavior
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+By default, when user presses the Android hardware back button, react-navigation will pop a screen or exit the app if there are no screens to pop. This is a sensible default behavior, but there are situations when you might want to implement custom handling.
+
+As an example, consider a screen where user is selecting items in a list, and a "selection mode" is active. On a back button press, you would first want the "selection mode" to be deactivated, and the screen should be popped only on the second back button press. The following code snippet demonstrates the situation. We make use of [`BackHandler`](https://reactnative.dev/docs/backhandler.html) which comes with react-native, along with the `useFocusEffect` hook to add our custom `hardwareBackPress` listener.
+
+Returning `true` from `onBackPress` denotes that we have handled the event, and react-navigation's listener will not get called, thus not popping the screen. Returning `false` will cause the event to bubble up and react-navigation's listener will pop the screen.
+
+
+
+
+```js name="Custom android back button" snack
+import * as React from 'react';
+import { Text, View, BackHandler, StyleSheet } from 'react-native';
+import { createStaticNavigation } from '@react-navigation/native';
+import { useFocusEffect } from '@react-navigation/native';
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+import { PlatformPressable, Button } from '@react-navigation/elements';
+
+const listData = [{ key: 'Apple' }, { key: 'Orange' }, { key: 'Carrot' }];
+
+// codeblock-focus-start
+function ScreenWithCustomBackBehavior() {
+ // codeblock-focus-end
+ const [selected, setSelected] = React.useState(listData[0].key);
+ const [isSelectionModeEnabled, setIsSelectionModeEnabled] =
+ React.useState(false);
+
+ // codeblock-focus-start
+ // ...
+
+ useFocusEffect(
+ React.useCallback(() => {
+ const onBackPress = () => {
+ if (isSelectionModeEnabled) {
+ setIsSelectionModeEnabled(false);
+ return true;
+ } else {
+ return false;
+ }
+ };
+
+ const subscription = BackHandler.addEventListener(
+ 'hardwareBackPress',
+ onBackPress
+ );
+
+ return () => subscription.remove();
+ }, [isSelectionModeEnabled])
+ );
+ // codeblock-focus-end
+
+ return (
+
+ {listData.map((item) => (
+ <>
+ {isSelectionModeEnabled ? (
+ {
+ setSelected(item.key);
+ }}
+ style={{
+ textDecorationLine: item.key === selected ? 'underline' : '',
+ }}
+ >
+
+ {item.key}
+
+
+ ) : (
+
+ {item.key === selected ? 'Selected: ' : ''}
+ {item.key}
+
+ )}
+ >
+ ))}
+
+ Selection mode: {isSelectionModeEnabled ? 'ON' : 'OFF'}
+
+ );
+ // codeblock-focus-start
+
+ // ...
+}
+// codeblock-focus-end
+
+const RootStack = createNativeStackNavigator({
+ screens: {
+ CustomScreen: ScreenWithCustomBackBehavior,
+ },
+});
+
+const Navigation = createStaticNavigation(RootStack);
+
+export default function App() {
+ return ;
+}
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ alignItems: 'center',
+ justifyContent: 'center',
+ },
+ text: {
+ fontSize: 20,
+ marginBottom: 20,
+ },
+});
+```
+
+
+
+
+```js name="Custom android back button" snack
+import * as React from 'react';
+import { Text, View, BackHandler, StyleSheet } from 'react-native';
+import { NavigationContainer } from '@react-navigation/native';
+import { useFocusEffect } from '@react-navigation/native';
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+import { PlatformPressable, Button } from '@react-navigation/elements';
+
+const Stack = createNativeStackNavigator();
+
+const listData = [{ key: 'Apple' }, { key: 'Orange' }, { key: 'Carrot' }];
+
+// codeblock-focus-start
+function ScreenWithCustomBackBehavior() {
+ // codeblock-focus-end
+
+ const [selected, setSelected] = React.useState(listData[0].key);
+ const [isSelectionModeEnabled, setIsSelectionModeEnabled] =
+ React.useState(false);
+ // codeblock-focus-start
+ // ...
+
+ useFocusEffect(
+ React.useCallback(() => {
+ const onBackPress = () => {
+ if (isSelectionModeEnabled) {
+ setIsSelectionModeEnabled(false);
+ return true;
+ } else {
+ return false;
+ }
+ };
+
+ const subscription = BackHandler.addEventListener(
+ 'hardwareBackPress',
+ onBackPress
+ );
+
+ return () => subscription.remove();
+ }, [isSelectionModeEnabled])
+ );
+ // codeblock-focus-end
+
+ return (
+
+ {listData.map((item) => (
+ <>
+ {isSelectionModeEnabled ? (
+ {
+ setSelected(item.key);
+ }}
+ style={{
+ textDecorationLine: item.key === selected ? 'underline' : '',
+ }}
+ >
+
+ {item.key}
+
+
+ ) : (
+
+ {item.key === selected ? 'Selected: ' : ''}
+ {item.key}
+
+ )}
+ >
+ ))}
+
+ Selection mode: {isSelectionModeEnabled ? 'ON' : 'OFF'}
+
+ );
+ // codeblock-focus-start
+
+ // ...
+}
+// codeblock-focus-end
+
+export default function App() {
+ return (
+
+
+
+
+
+ );
+}
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ alignItems: 'center',
+ justifyContent: 'center',
+ },
+ text: {
+ fontSize: 20,
+ marginBottom: 20,
+ },
+});
+```
+
+
+
+
+The presented approach will work well for screens that are shown in a `StackNavigator`. Custom back button handling in other situations may not be supported at the moment (eg. A known case when this does not work is when you want to handle back button press in an open drawer. PRs for such use cases are welcome!).
+
+If instead of overriding system back button, you'd like to prevent going back from the screen, see docs for [preventing going back](preventing-going-back.md).
+
+### Why not use component lifecycle methods
+
+At first, you may be inclined to use `componentDidMount` to subscribe for the back press event and `componentWillUnmount` to unsubscribe, or use `useEffect` to add the listener. This approach will not work - learn more about this in [navigation lifecycle](navigation-lifecycle.md).
diff --git a/versioned_docs/version-8.x/custom-navigators.md b/versioned_docs/version-8.x/custom-navigators.md
new file mode 100755
index 00000000000..55d758c3bd7
--- /dev/null
+++ b/versioned_docs/version-8.x/custom-navigators.md
@@ -0,0 +1,480 @@
+---
+id: custom-navigators
+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.
+
+A custom navigator behaves just like a built-in navigator, and can be used in the same way. This means you can define screens the same way, use [route](route-object.md) and [navigation](navigation-object.md) objects in your screens, and navigate between screens with familiar API. The navigator will also be able to handle deep linking, state persistence, and other features that built-in navigators support.
+
+## Overview
+
+Under the hood, navigators are plain React components that use the [`useNavigationBuilder`](#usenavigationbuilder) hook.
+
+The navigator component then uses this state to layout the screens appropriately with any additional UI based on the use case. This component is then wrapped in [`createNavigatorFactory`](#createnavigatorfactory) to create the API for the navigator.
+
+A very basic example looks like this:
+
+```js
+function MyNavigator(props) {
+ const { state, descriptors, NavigationContent } = useNavigationBuilder(
+ StackRouter,
+ props
+ );
+
+ const focusedRoute = state.routes[state.index];
+ const descriptor = descriptors[focusedRoute.key];
+
+ return {descriptor.render()};
+}
+
+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 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.
+- Then we get the corresponding descriptor for the focused route with `descriptors[focusedRoute.key]` and call the `render()` method on it to get the React element for the screen.
+- The content of the navigator is wrapped in `NavigationContent` to provide appropriate context and wrappers.
+
+With this, we have a basic stack navigator that renders only the focused screen. Unlike the built-in stack navigator, this doesn't keep unfocused screens rendered. But you can loop through `state.routes` and render all of the screens if you want to keep them mounted. You can also read `descriptor.options` to get the [options](screen-options.md) to handle the screen's title, header, and other options.
+
+This also doesn't have any additional UI apart from the screen content. There are no gestures or animations. So you're free to add any additional UI, gestures, animations etc. as needed. You can also layout the screens in any way you want, such as rendering them side-by-side or in a grid, instead of stacking them on top of each other like the built-in stack navigator does.
+
+You can see a more complete example of a custom navigator later in this document.
+
+## API Definition
+
+### `useNavigationBuilder`
+
+This hook contains the core logic of a navigator, and is responsible for storing and managing the [navigation state](navigation-state.md). It takes a [router](custom-routers.md) as an argument to know how to handle various navigation actions. It then returns the state and helper methods for the navigator component to use.
+
+It accepts the following arguments:
+
+- `createRouter` - A factory method which returns a router object (e.g. `StackRouter`, `TabRouter`).
+- `options` - Options for the hook and the router. The navigator should forward its props here so that user can provide props to configure the navigator. By default, the following options are accepted:
+ - `children` (required) - The `children` prop should contain route configurations as `Screen` components.
+ - `screenOptions` - The `screenOptions` prop should contain default options for all of the screens.
+ - `initialRouteName` - The `initialRouteName` prop determines the screen to focus on initial render. This prop is forwarded to the router.
+
+ If any other options are passed here, they'll be forwarded to the router.
+
+The hook returns an object with following properties:
+
+- `state` - The [navigation state](navigation-state.md) for the navigator. The component can take this state and decide how to render it.
+- `navigation` - The navigation object containing various helper methods for the navigator to manipulate the [navigation state](navigation-state.md). This isn't the same as the navigation object for the screen and includes some helpers such as `emit` to emit events to the screens.
+- `descriptors` - This is an object containing descriptors for each route with the route keys as its properties. The descriptor for a route can be accessed by `descriptors[route.key]`. Each descriptor contains the following properties:
+ - `navigation` - The navigation object for the screen. You don't need to pass this to the screen manually. But it's useful if we're rendering components outside the screen that need to receive `navigation` prop as well, such as a header component.
+ - `options` - A getter which returns the options such as `title` for the screen if they are specified.
+ - `render` - A function which can be used to render the actual screen. Calling `descriptors[route.key].render()` will return a React element containing the screen content. It's important to use this method to render a screen, otherwise any child navigators won't be connected to the navigation tree properly.
+
+Example:
+
+```js
+import * as React from 'react';
+import { Text, Pressable, View } from 'react-native';
+import {
+ NavigationHelpersContext,
+ useNavigationBuilder,
+ TabRouter,
+ TabActions,
+} from '@react-navigation/native';
+
+function TabNavigator({ tabBarStyle, contentStyle, ...rest }) {
+ const { state, navigation, descriptors, NavigationContent } =
+ useNavigationBuilder(TabRouter, rest);
+
+ return (
+
+
+ {state.routes.map((route, index) => (
+ {
+ const isFocused = state.index === index;
+ const event = navigation.emit({
+ type: 'tabPress',
+ target: route.key,
+ canPreventDefault: true,
+ });
+
+ if (!isFocused && !event.defaultPrevented) {
+ navigation.dispatch({
+ ...TabActions.jumpTo(route.name, route.params),
+ target: state.key,
+ });
+ }
+ }}
+ style={{ flex: 1 }}
+ >
+ {descriptors[route.key].options.title ?? route.name}
+
+ ))}
+
+
+ {state.routes.map((route, i) => {
+ return (
+
+ {descriptors[route.key].render()}
+
+ );
+ })}
+
+
+ );
+}
+```
+
+The `navigation` object for navigators also has an `emit` method to emit custom events to the child screens. The usage looks like this:
+
+```js
+navigation.emit({
+ type: 'transitionStart',
+ data: { blurring: false },
+ target: route.key,
+});
+```
+
+The `data` is available under the `data` property in the `event` object, i.e. `event.data`.
+
+The `target` property determines the screen that will receive the event. If the `target` property is omitted, the event is dispatched to all screens in the navigator.
+
+### `createNavigatorFactory`
+
+This `createNavigatorFactory` function is used to create a function that will `Navigator` and `Screen` pair. Custom navigators need to wrap the navigator component in `createNavigatorFactory` before exporting.
+
+Example:
+
+```js
+import {
+ useNavigationBuilder,
+ createNavigatorFactory,
+} from '@react-navigation/native';
+
+// ...
+
+export function createMyNavigator(config) {
+ return createNavigatorFactory(TabNavigator)(config);
+}
+```
+
+:::note
+
+We can also do `export const createMyNavigator = createNavigatorFactory(MyNavigator)` directly instead of wrapping in another function. However, the wrapper function is necessary to have proper [TypeScript support](#type-checking-navigators) for the navigator.
+
+:::
+
+Then it can be used like this:
+
+```js static2dynamic
+import { createStaticNavigation } from '@react-navigation/native';
+import { createMyNavigator } from './myNavigator';
+
+const MyTabs = createMyNavigator({
+ screens: {
+ Home: HomeScreen,
+ Feed: FeedScreen,
+ },
+});
+
+const Navigation = createStaticNavigation(MyTabs);
+
+function App() {
+ return ;
+}
+```
+
+## Type-checking navigators
+
+To type-check navigators, we need to provide few types:
+
+- Type of the props accepted by the view
+- Type of supported screen options
+- A map of event types emitted by the navigator
+
+For example, to type-check our custom tab navigator, we can do something like this:
+
+```tsx
+import * as React from 'react';
+import {
+ View,
+ Text,
+ Pressable,
+ type StyleProp,
+ type ViewStyle,
+ StyleSheet,
+} from 'react-native';
+import {
+ createNavigatorFactory,
+ CommonActions,
+ type DefaultNavigatorOptions,
+ type NavigatorTypeBagBase,
+ type ParamListBase,
+ type StaticConfig,
+ type StaticScreenConfig,
+ type TabActionHelpers,
+ type TabNavigationState,
+ TabRouter,
+ type TabRouterOptions,
+ type TypedNavigator,
+ useNavigationBuilder,
+} from '@react-navigation/native';
+
+// Additional props accepted by the view
+type TabNavigationConfig = {
+ tabBarStyle: StyleProp;
+ contentStyle: StyleProp;
+};
+
+// Supported screen options
+type TabNavigationOptions = {
+ title?: string;
+};
+
+// Map of event name and the type of data (in event.data)
+// canPreventDefault: true adds the defaultPrevented property to the
+// emitted events.
+type TabNavigationEventMap = {
+ tabPress: {
+ data: { isAlreadyFocused: boolean };
+ canPreventDefault: true;
+ };
+};
+
+// The type of the navigation object for each screen
+type TabNavigationProp<
+ ParamList extends ParamListBase,
+ RouteName extends keyof ParamList = keyof ParamList,
+ NavigatorID extends string | undefined = undefined,
+> = NavigationProp<
+ ParamList,
+ RouteName,
+ NavigatorID,
+ TabNavigationState,
+ TabNavigationOptions,
+ TabNavigationEventMap
+> &
+ TabActionHelpers;
+
+// The props accepted by the component is a combination of 3 things
+type Props = DefaultNavigatorOptions<
+ ParamListBase,
+ string | undefined,
+ TabNavigationState,
+ TabNavigationOptions,
+ TabNavigationEventMap,
+ TabNavigationProp
+> &
+ TabRouterOptions &
+ TabNavigationConfig;
+
+function TabNavigator({ tabBarStyle, contentStyle, ...rest }: Props) {
+ const { state, navigation, descriptors, NavigationContent } =
+ useNavigationBuilder<
+ TabNavigationState,
+ TabRouterOptions,
+ TabActionHelpers,
+ TabNavigationOptions,
+ TabNavigationEventMap
+ >(TabRouter, rest);
+
+ return (
+
+
+ {state.routes.map((route, index) => (
+ {
+ const isFocused = state.index === index;
+ const event = navigation.emit({
+ type: 'tabPress',
+ target: route.key,
+ canPreventDefault: true,
+ data: {
+ isAlreadyFocused: isFocused,
+ },
+ });
+
+ if (!isFocused && !event.defaultPrevented) {
+ navigation.dispatch({
+ ...CommonActions.navigate(route),
+ target: state.key,
+ });
+ }
+ }}
+ style={{ flex: 1 }}
+ >
+ {descriptors[route.key].options.title || route.name}
+
+ ))}
+
+
+ {state.routes.map((route, i) => {
+ return (
+
+ {descriptors[route.key].render()}
+
+ );
+ })}
+
+
+ );
+}
+
+// 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,
+>(): 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
+
+All of the built-in navigators export their views, which we can reuse and build additional functionality on top of them. For example, if we want to re-build the bottom tab navigator, we need the following code:
+
+```js
+import * as React from 'react';
+import {
+ useNavigationBuilder,
+ createNavigatorFactory,
+ TabRouter,
+} from '@react-navigation/native';
+import { BottomTabView } from '@react-navigation/bottom-tabs';
+
+function BottomTabNavigator({
+ initialRouteName,
+ children,
+ layout,
+ screenListeners,
+ screenOptions,
+ screenLayout,
+ backBehavior,
+ ...rest
+}) {
+ const { state, descriptors, navigation, NavigationContent } =
+ useNavigationBuilder(TabRouter, {
+ initialRouteName,
+ children,
+ layout,
+ screenListeners,
+ screenOptions,
+ screenLayout,
+ backBehavior,
+ });
+
+ return (
+
+
+
+ );
+}
+
+export function createMyNavigator(config) {
+ return createNavigatorFactory(TabNavigator)(config);
+}
+```
+
+Now, we can customize it to add additional functionality or change the behavior. For example, use a [custom router](custom-routers.md) instead of the default `TabRouter`:
+
+```js
+import MyRouter from './MyRouter';
+
+// ...
+
+const { state, descriptors, navigation, NavigationContent } =
+ useNavigationBuilder(MyRouter, {
+ id,
+ initialRouteName,
+ children,
+ layout,
+ screenListeners,
+ screenOptions,
+ screenLayout,
+ backBehavior,
+ });
+
+// ...
+```
+
+:::note
+
+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
+- [`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/custom-routers.md b/versioned_docs/version-8.x/custom-routers.md
new file mode 100755
index 00000000000..86b66d3a1f9
--- /dev/null
+++ b/versioned_docs/version-8.x/custom-routers.md
@@ -0,0 +1,224 @@
+---
+id: custom-routers
+title: Custom routers
+sidebar_label: Custom routers
+---
+
+The router object provides various helper methods to deal with the state and actions, a reducer to update the state as well as some action creators.
+
+The router is responsible for handling actions dispatched by calling methods on the navigation object. If the router cannot handle an action, it can return `null`, which would propagate the action to other routers until it's handled.
+
+You can make your own router by building an object with the following functions:
+
+- `type` - String representing the type of the router, e.g. `'stack'`, `'tab'`, `'drawer'` etc.
+- `getInitialState` - Function that returns the initial state for the navigator. Receives an options object with `routeNames` and `routeParamList` properties.
+- `getRehydratedState` - Function that rehydrates the full [navigation state](navigation-state.md) from a given partial state. Receives a partial state object and an options object with `routeNames` and `routeParamList` properties.
+- `getStateForRouteNamesChange` - Function that takes the current state and updated list of route names, and returns a new state. Receives the state object and an options object with `routeNames` and `routeParamList` properties.
+- `getStateForAction` - Reducer function that takes the current state and action along with an options object with `routeNames` and `routeParamList` properties, and returns a new state. If the action cannot be handled, it should return `null`.
+- `getStateForRouteFocus` - Function that takes the current state and key of a route, and returns a new state with that route focused.
+- `shouldActionChangeFocus` - Function that determines whether the action should also change focus in parent navigator. Some actions such as `NAVIGATE` can change focus in the parent.
+- `actionCreators` - Optional object containing a list of action creators, such as `push`, `pop` etc. These will be used to add helper methods to the `navigation` object to dispatch those actions.
+
+:::info
+
+The functions in the router object should be pure functions, i.e. they should not have any side-effects, mutate parameters or external variables, and should return the same output for the same input.
+
+:::
+
+Example:
+
+```js
+const router = {
+ type: 'tab',
+
+ getInitialState({ routeNames, routeParamList }) {
+ const index =
+ options.initialRouteName === undefined
+ ? 0
+ : routeNames.indexOf(options.initialRouteName);
+
+ return {
+ stale: false,
+ type: 'tab',
+ key: shortid(),
+ index,
+ routeNames,
+ routes: routeNames.map(name => ({
+ name,
+ key: name,
+ params: routeParamList[name],
+ })),
+ };
+ },
+
+ getRehydratedState(partialState, { routeNames, routeParamList }) {
+ const state = partialState;
+
+ if (state.stale === false) {
+ return state as NavigationState;
+ }
+
+ const routes = state.routes
+ .filter(route => routeNames.includes(route.name))
+ .map(
+ route =>
+ ({
+ ...route,
+ key: route.key || `${route.name}-${shortid()}`,
+ params:
+ routeParamList[route.name] !== undefined
+ ? {
+ ...routeParamList[route.name],
+ ...route.params,
+ }
+ : route.params,
+ } as Route)
+ );
+
+ return {
+ stale: false,
+ type: 'tab',
+ key: shortid(),
+ index:
+ typeof state.index === 'number' && state.index < routes.length
+ ? state.index
+ : 0,
+ routeNames,
+ routes,
+ };
+ },
+
+ getStateForRouteNamesChange(state, { routeNames }) {
+ const routes = state.routes.filter(route =>
+ routeNames.includes(route.name)
+ );
+
+ return {
+ ...state,
+ routeNames,
+ routes,
+ index: Math.min(state.index, routes.length - 1),
+ };
+ },
+
+ getStateForRouteFocus(state, key) {
+ const index = state.routes.findIndex(r => r.key === key);
+
+ if (index === -1 || index === state.index) {
+ return state;
+ }
+
+ return { ...state, index };
+ },
+
+ getStateForAction(state, action) {
+ switch (action.type) {
+ case 'NAVIGATE': {
+ const index = state.routes.findIndex(
+ route => route.name === action.payload.name
+ );
+
+ if (index === -1) {
+ return null;
+ }
+
+ return { ...state, index };
+ }
+
+ default:
+ return BaseRouter.getStateForAction(state, action);
+ }
+ },
+
+ shouldActionChangeFocus() {
+ return false;
+ },
+};
+
+const SimpleRouter = () => router;
+
+export default SimpleRouter;
+```
+
+## Built-In Routers
+
+The library ships with a few standard routers:
+
+- `StackRouter`
+- `TabRouter`
+- `DrawerRouter`
+
+## Customizing Routers
+
+There are two main ways to customize routers:
+
+- Override an existing router with the [`UNSTABLE_router`](navigator.md#router) prop on navigators
+- Customized navigators with a custom router, see [extending navigators](custom-navigators.md#extending-navigators)
+
+### Custom Navigation Actions
+
+Let's say you want to add a custom action to clear the history:
+
+```js
+import { TabRouter } from '@react-navigation/native';
+
+const MyTabRouter = (options) => {
+ const router = TabRouter(options);
+
+ return {
+ ...router,
+ getStateForAction(state, action, options) {
+ switch (action.type) {
+ case 'CLEAR_HISTORY':
+ return {
+ ...state,
+ routeKeyHistory: [],
+ };
+ default:
+ return router.getStateForAction(state, action, options);
+ }
+ },
+
+ actionCreators: {
+ ...router.actionCreators,
+ clearHistory() {
+ return { type: 'CLEAR_HISTORY' };
+ },
+ },
+ };
+};
+```
+
+Instead of writing a custom router to handle custom actions, you can [pass a function to `dispatch`](navigation-object.md#dispatch) instead. It's cleaner and recommended instead of overriding routers.
+
+### Blocking Navigation Actions
+
+Sometimes you may want to prevent some navigation activity, depending on your route. Let's say, you want to prevent pushing a new screen if `isEditing` is `true`:
+
+```js
+import { StackRouter } from '@react-navigation/native';
+
+const MyStackRouter = (options) => {
+ const router = StackRouter(options);
+
+ return {
+ ...router,
+ getStateForAction(state, action, options) {
+ const result = router.getStateForAction(state, action, options);
+
+ if (
+ result != null &&
+ result.index > state.index &&
+ state.routes[state.index].params?.isEditing
+ ) {
+ // Returning the current state means that the action has been handled, but we don't have a new state
+ return state;
+ }
+
+ return result;
+ },
+ };
+};
+```
+
+If you want to prevent going back, the recommended approach is to use the [`usePreventRemove` hook](preventing-going-back.md).
diff --git a/versioned_docs/version-8.x/customizing-bottom-tabs.md b/versioned_docs/version-8.x/customizing-bottom-tabs.md
new file mode 100755
index 00000000000..4f17f068c5e
--- /dev/null
+++ b/versioned_docs/version-8.x/customizing-bottom-tabs.md
@@ -0,0 +1,290 @@
+---
+id: customizing-tabbar
+title: Customizing bottom tab bar
+sidebar_label: Customizing tab bar
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+This guide covers customizing the tab bar in [`createBottomTabNavigator`](bottom-tab-navigator.md). Make sure to install and configure the library according to the [installation instructions](bottom-tab-navigator.md#installation) first.
+
+## Add icons for each tab
+
+This is similar to how you would customize a stack navigator — there are some properties that are set when you initialize the tab navigator and others that can be customized per-screen in `options`.
+
+
+
+
+```js name="Tab bar icons" snack dependencies=@expo/vector-icons,@expo/vector-icons/Ionicons
+import * as React from 'react';
+import { View, Text } from 'react-native';
+import { createStaticNavigation } from '@react-navigation/native';
+import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
+// codeblock-focus-start
+// You can import Ionicons from @expo/vector-icons/Ionicons if you use Expo or
+// react-native-vector-icons/Ionicons otherwise.
+import Ionicons from 'react-native-vector-icons/Ionicons';
+
+// codeblock-focus-end
+
+function HomeScreen() {
+ return (
+
+ Home!
+
+ );
+}
+
+function SettingsScreen() {
+ return (
+
+ Settings!
+
+ );
+}
+
+// codeblock-focus-start
+const RootTabs = createBottomTabNavigator({
+ screenOptions: ({ route }) => ({
+ // highlight-start
+ tabBarIcon: ({ focused, color, size }) => {
+ let iconName;
+
+ if (route.name === 'Home') {
+ iconName = focused
+ ? 'ios-information-circle'
+ : 'ios-information-circle-outline';
+ } else if (route.name === 'Settings') {
+ iconName = focused ? 'ios-list' : 'ios-list-outline';
+ }
+
+ // You can return any component that you like here!
+ return ;
+ },
+ // highlight-end
+ tabBarActiveTintColor: 'tomato',
+ tabBarInactiveTintColor: 'gray',
+ }),
+ screens: {
+ Home: HomeScreen,
+ Settings: SettingsScreen,
+ },
+});
+// codeblock-focus-end
+
+const Navigation = createStaticNavigation(RootTabs);
+
+export default function App() {
+ return ;
+}
+```
+
+
+
+
+```js name="Tab based navigation" snack dependencies=@expo/vector-icons,@expo/vector-icons/Ionicons
+import * as React from 'react';
+import { Text, View } from 'react-native';
+import { NavigationContainer } from '@react-navigation/native';
+import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
+// codeblock-focus-start
+// You can import Ionicons from @expo/vector-icons/Ionicons if you use Expo or
+// react-native-vector-icons/Ionicons otherwise.
+import Ionicons from 'react-native-vector-icons/Ionicons';
+
+// codeblock-focus-end
+
+function HomeScreen() {
+ return (
+
+ Home!
+
+ );
+}
+
+function SettingsScreen() {
+ return (
+
+ Settings!
+
+ );
+}
+
+const Tab = createBottomTabNavigator();
+
+// codeblock-focus-start
+function RootTabs() {
+ return (
+ ({
+ // highlight-start
+ tabBarIcon: ({ focused, color, size }) => {
+ let iconName;
+
+ if (route.name === 'Home') {
+ iconName = focused
+ ? 'ios-information-circle'
+ : 'ios-information-circle-outline';
+ } else if (route.name === 'Settings') {
+ iconName = focused ? 'ios-list' : 'ios-list-outline';
+ }
+
+ // You can return any component that you like here!
+ return ;
+ },
+ // highlight-end
+ tabBarActiveTintColor: 'tomato',
+ tabBarInactiveTintColor: 'gray',
+ })}
+ >
+
+
+
+ );
+}
+// codeblock-focus-end
+
+export default function App() {
+ return (
+
+
+
+ );
+}
+```
+
+
+
+
+Let's dissect this:
+
+- `tabBarIcon` is a supported option in bottom tab navigator. So we know we can use it on our screen components in the `options` prop, but in this case chose to put it in the `screenOptions` prop of `Tab.Navigator` in order to centralize the icon configuration for convenience.
+- `tabBarIcon` is a function that is given the `focused` state, `color`, and `size` params. If you take a peek further down in the configuration you will see `tabBarActiveTintColor` and `tabBarInactiveTintColor`. These default to the iOS platform defaults, but you can change them here. The `color` that is passed through to the `tabBarIcon` is either the active or inactive one, depending on the `focused` state (focused is active). The `size` is the size of the icon expected by the tab bar.
+- Read the [full API reference](bottom-tab-navigator.md) for further information on `createBottomTabNavigator` configuration options.
+
+## Add badges to icons
+
+Sometimes we want to add badges to some icons. You can use the [`tabBarBadge` option](bottom-tab-navigator.md#tabbarbadge) to do it:
+
+
+
+
+```js name="Tab based navigation" snack
+import * as React from 'react';
+import { View, Text } from 'react-native';
+import { createStaticNavigation } from '@react-navigation/native';
+import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
+
+function HomeScreen() {
+ return (
+
+ Home!
+
+ );
+}
+
+function SettingsScreen() {
+ return (
+
+ Settings!
+
+ );
+}
+
+// codeblock-focus-start
+const RootTabs = createBottomTabNavigator({
+ screens: {
+ Home: {
+ screen: HomeScreen,
+ options: {
+ // highlight-start
+ tabBarBadge: 3,
+ // highlight-end
+ },
+ },
+ Settings: SettingsScreen,
+ },
+});
+// codeblock-focus-end
+
+const Navigation = createStaticNavigation(RootTabs);
+
+export default function App() {
+ return ;
+}
+```
+
+
+
+
+```js name="Tab based navigation" snack
+import * as React from 'react';
+import { Text, View } from 'react-native';
+import { NavigationContainer } from '@react-navigation/native';
+import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
+
+function HomeScreen() {
+ return (
+
+ Home!
+
+ );
+}
+
+function SettingsScreen() {
+ return (
+
+ Settings!
+
+ );
+}
+
+const Tab = createBottomTabNavigator();
+
+// codeblock-focus-start
+function RootTabs() {
+ return (
+
+
+
+
+ );
+}
+// codeblock-focus-end
+
+export default function App() {
+ return (
+
+
+
+ );
+}
+```
+
+
+
+
+From UI perspective this component is ready to use, but you still need to find some way to pass down the badge count properly from somewhere else, like using [React Context](https://react.dev/reference/react/useContext), [Redux](https://redux.js.org/), [MobX](https://mobx.js.org/) or [event emitters](https://github.com/facebook/react-native/blob/master/Libraries/vendor/emitter/EventEmitter.js).
+
+You can also update the badge from within the screen component by using the `setOptions` method:
+
+```js
+const navigation = useNavigation();
+
+React.useEffect(() => {
+ navigation.setOptions({
+ tabBarBadge: unreadMessagesCount,
+ });
+}, [navigation, unreadMessagesCount]);
+```
+
+
diff --git a/versioned_docs/version-8.x/deep-linking.md b/versioned_docs/version-8.x/deep-linking.md
new file mode 100755
index 00000000000..2dcac6d9f96
--- /dev/null
+++ b/versioned_docs/version-8.x/deep-linking.md
@@ -0,0 +1,517 @@
+---
+id: deep-linking
+title: Deep linking
+sidebar_label: Deep linking
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+This guide will describe how to configure your app to handle deep links on various platforms. To handle incoming links, you need to handle 2 scenarios:
+
+1. If the app wasn't previously open, the deep link needs to set the initial state
+2. If the app was already open, the deep link needs to update the state to reflect the incoming link
+
+React Native provides a [`Linking`](https://reactnative.dev/docs/linking) to get notified of incoming links. React Navigation can integrate with the `Linking` module to automatically handle deep links. On Web, React Navigation can integrate with browser's `history` API to handle URLs on client side. See [configuring links](configuring-links.md) to see more details on how to configure links in React Navigation.
+
+While you don't need to use the `linking` prop from React Navigation, and can handle deep links yourself by using the `Linking` API and navigating from there, it'll be significantly more complicated than using the `linking` prop which handles many edge cases for you. So we don't recommend implementing it by yourself.
+
+Below, we'll go through required configurations so that the deep link integration works.
+
+## Setting up deep links
+
+
+
+
+### Configuring URL scheme
+
+First, you will want to specify a URL scheme for your app. This corresponds to the string before `://` in a URL, so if your scheme is `example` then a link to your app would be `example://`. You can register for a scheme in your `app.json` by adding a string under the scheme key:
+
+```json
+{
+ "expo": {
+ "scheme": "example"
+ }
+}
+```
+
+Next, install `expo-linking` which we'd need to get the deep link prefix:
+
+```bash
+npx expo install expo-linking
+```
+
+Then you can use `Linking.createURL` to get the prefix for your app:
+
+```js
+const linking = {
+ prefixes: [Linking.createURL('/'),
+};
+```
+
+See more details below at [Configuring React Navigation](#configuring-react-navigation).
+
+
+Why use `Linking.createURL`?
+
+It is necessary to use `Linking.createURL` since the scheme differs between the [Expo Dev Client](https://docs.expo.dev/versions/latest/sdk/dev-client/) and standalone apps.
+
+The scheme specified in `app.json` only applies to standalone apps. In the Expo client app you can deep link using `exp://ADDRESS:PORT/--/` where `ADDRESS` is often `127.0.0.1` and `PORT` is often `19000` - the URL is printed when you run `expo start`. The `Linking.createURL` function abstracts it out so that you don't need to specify them manually.
+
+
+
+If you are using universal links, you need to add your domain to the prefixes as well:
+
+```js
+const linking = {
+ prefixes: [Linking.createURL('/'), 'https://app.example.com'],
+};
+```
+
+### Universal Links on iOS
+
+To set up iOS universal Links in your Expo app, you need to configure your [app config](https://docs.expo.dev/workflow/configuration) to include the associated domains and entitlements:
+
+```json
+{
+ "expo": {
+ "ios": {
+ "associatedDomains": ["applinks:app.example.com"],
+ "entitlements": {
+ "com.apple.developer.associated-domains": ["applinks:app.example.com"]
+ }
+ }
+ }
+}
+```
+
+You will also need to setup [Associated Domains](https://developer.apple.com/documentation/Xcode/supporting-associated-domains) on your server.
+
+See [Expo's documentation on iOS Universal Links](https://docs.expo.dev/linking/ios-universal-links/) for more details.
+
+### App Links on Android
+
+To set up Android App Links in your Expo app, you need to configure your [app config](https://docs.expo.dev/workflow/configuration) to include the `intentFilters`:
+
+```json
+{
+ "expo": {
+ "android": {
+ "intentFilters": [
+ {
+ "action": "VIEW",
+ "autoVerify": true,
+ "data": [
+ {
+ "scheme": "https",
+ "host": "app.example.com"
+ }
+ ],
+ "category": ["BROWSABLE", "DEFAULT"]
+ }
+ ]
+ }
+ }
+}
+```
+
+You will also need to [declare the association](https://developer.android.com/training/app-links/verify-android-applinks#web-assoc) between your website and your intent filters by hosting a Digital Asset Links JSON file.
+
+See [Expo's documentation on Android App Links](https://docs.expo.dev/linking/android-app-links/) for more details.
+
+
+
+
+### Setup on iOS
+
+Let's configure the native iOS app to open based on the `example://` URI scheme.
+
+You'll need to add the `LinkingIOS` folder into your header search paths as described [here](https://reactnative.dev/docs/linking-libraries-ios#step-3). Then you'll need to add the following lines to your or `AppDelegate.swift` or `AppDelegate.mm` file:
+
+
+
+
+```swift
+func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
+ return RCTLinkingManager.application(app, open: url, options: options)
+}
+```
+
+
+
+
+```objc
+#import
+
+- (BOOL)application:(UIApplication *)application
+ openURL:(NSURL *)url
+ options:(NSDictionary *)options
+{
+ return [RCTLinkingManager application:application openURL:url options:options];
+}
+```
+
+
+
+
+If your app is using [Universal Links](https://developer.apple.com/ios/universal-links/), you'll need to add the following code as well:
+
+
+
+
+```swift
+func application(
+ _ application: UIApplication,
+ continue userActivity: NSUserActivity,
+ restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
+ return RCTLinkingManager.application(
+ application,
+ continue: userActivity,
+ restorationHandler: restorationHandler
+ )
+ }
+```
+
+
+
+
+```objc
+- (BOOL)application:(UIApplication *)application continueUserActivity:(nonnull NSUserActivity *)userActivity
+ restorationHandler:(nonnull void (^)(NSArray> * _Nullable))restorationHandler
+{
+ return [RCTLinkingManager application:application
+ continueUserActivity:userActivity
+ restorationHandler:restorationHandler];
+}
+```
+
+
+
+
+Now you need to add the scheme to your project configuration.
+
+The easiest way to do this is with the `uri-scheme` package by running the following:
+
+```bash
+npx uri-scheme add example --ios
+```
+
+If you want to do it manually, open the project (e.g. `SimpleApp/ios/SimpleApp.xcworkspace`) in Xcode. Select the project in sidebar and navigate to the info tab. Scroll down to "URL Types" and add one. In the new URL type, set the identifier and the URL scheme to your desired URL scheme.
+
+
+
+To make sure Universal Links work in your app, you also need to setup [Associated Domains](https://developer.apple.com/documentation/Xcode/supporting-associated-domains) on your server.
+
+#### Hybrid React Native and native iOS Applications
+
+If you're using React Navigation within a hybrid app - an iOS app that has both Swift/ObjC and React Native parts - you may be missing the `RCTLinkingIOS` subspec in your `Podfile`, which is installed by default in new React Native projects. To add this, ensure your `Podfile` looks like the following:
+
+```pod
+ pod 'React', :path => '../node_modules/react-native', :subspecs => [
+ . . . // other subspecs
+ 'RCTLinkingIOS',
+ . . .
+ ]
+```
+
+### Setup on Android
+
+To configure the external linking in Android, you can create a new intent in the manifest.
+
+The easiest way to do this is with the `uri-scheme` package: `npx uri-scheme add example --android`.
+
+If you want to add it manually, open up `SimpleApp/android/app/src/main/AndroidManifest.xml`, and make the following adjustments:
+
+1. Set `launchMode` of `MainActivity` to `singleTask` in order to receive intent on existing `MainActivity` (this is the default, so you may not need to actually change anything).
+2. Add the new [`intent-filter`](http://developer.android.com/training/app-indexing/deep-linking.html#adding-filters) inside the `MainActivity` entry with a `VIEW` type action:
+
+```xml
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+Similar to Universal Links on iOS, you can also use a domain to associate the app with your website on Android by [verifying Android App Links](https://developer.android.com/training/app-links/verify-android-applinks). First, you need to configure your `AndroidManifest.xml`:
+
+1. Add `android:autoVerify="true"` to your `` entry.
+2. Add your domain's `scheme` and `host` in a new `` entry inside the ``.
+
+After adding them, it should look like this:
+
+```xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+Then, you need to [declare the association](https://developer.android.com/training/app-links/verify-android-applinks#web-assoc) between your website and your intent filters by hosting a Digital Asset Links JSON file.
+
+
+
+
+## Configuring React Navigation
+
+To handle deep links, you need to configure React Navigation to use the `scheme` for parsing incoming deep links:
+
+
+
+
+```js
+const linking = {
+ prefixes: [
+ 'example://', // Or `Linking.createURL('/')` for Expo apps
+ ],
+};
+
+function App() {
+ return ;
+}
+```
+
+
+
+
+```js
+const linking = {
+ prefixes: [
+ 'example://', // Or `Linking.createURL('/')` for Expo apps
+ ],
+};
+
+function App() {
+ return (
+ Loading...}>
+ {/* content */}
+
+ );
+}
+```
+
+
+
+
+If you are using universal links, you need to add your domain to the prefixes as well:
+
+```js
+const linking = {
+ prefixes: [
+ 'example://', // Or `Linking.createURL('/')` for Expo apps
+ 'https://app.example.com',
+ ],
+};
+```
+
+See [configuring links](configuring-links.md) to see further details on how to configure links in React Navigation.
+
+## Testing deep links
+
+Before testing deep links, make sure that you rebuild and install the app in your emulator/simulator/device.
+
+If you're testing on iOS, run:
+
+```bash
+npx react-native run-ios
+```
+
+If you're testing on Android, run:
+
+```bash
+npx react-native run-android
+```
+
+If you're using Expo managed workflow and testing on Expo client, you don't need to rebuild the app. However, you will need to use the correct address and port that's printed when you run `expo start`, e.g. `exp://127.0.0.1:19000/--/`.
+
+If you want to test with your custom scheme in your Expo app, you will need rebuild your standalone app by running `expo build:ios -t simulator` or `expo build:android` and install the resulting binaries.
+
+### Testing with `npx uri-scheme`
+
+The `uri-scheme` package is a command line tool that can be used to test deep links on both iOS & Android. It can be used as follows:
+
+```bash
+npx uri-scheme open [your deep link] --[ios|android]
+```
+
+For example:
+
+```bash
+npx uri-scheme open "example://chat/jane" --ios
+```
+
+Or if using Expo client:
+
+```bash
+npx uri-scheme open "exp://127.0.0.1:19000/--/chat/jane" --ios
+```
+
+### Testing with `xcrun` on iOS
+
+The `xcrun` command can be used as follows to test deep links with the iOS simulator:
+
+```bash
+xcrun simctl openurl booted [your deep link]
+```
+
+For example:
+
+```bash
+xcrun simctl openurl booted "example://chat/jane"
+```
+
+### Testing with `adb` on Android
+
+The `adb` command can be used as follows to test deep links with the Android emulator or a connected device:
+
+```bash
+adb shell am start -W -a android.intent.action.VIEW -d [your deep link] [your android package name]
+```
+
+For example:
+
+```bash
+adb shell am start -W -a android.intent.action.VIEW -d "example://chat/jane" com.simpleapp
+```
+
+Or if using Expo client:
+
+```bash
+adb shell am start -W -a android.intent.action.VIEW -d "exp://127.0.0.1:19000/--/chat/jane" host.exp.exponent
+```
+
+## Integrating with other tools
+
+In addition to deep links and universal links with React Native's `Linking` API, you may also want to integrate other tools for handling incoming links, e.g. Push Notifications - so that tapping on a notification can open the app to a specific screen.
+
+To achieve this, you'd need to override how React Navigation subscribes to incoming links. To do so, you can provide your own [`getInitialURL`](navigation-container.md#linkinggetinitialurl) and [`subscribe`](navigation-container.md#linkingsubscribe) functions.
+
+Here is an example integration with [expo-notifications](https://docs.expo.dev/versions/latest/sdk/notifications):
+
+
+
+
+```js name="Expo Notifications"
+const linking = {
+ prefixes: ['example://', 'https://app.example.com'],
+
+ // Custom function to get the URL which was used to open the app
+ async getInitialURL() {
+ // First, handle deep links
+ const url = await Linking.getInitialURL();
+
+ if (url != null) {
+ return url;
+ }
+
+ // Handle URL from expo push notifications
+ const response = await Notifications.getLastNotificationResponseAsync();
+
+ return response?.notification.request.content.data.url;
+ },
+
+ // Custom function to subscribe to incoming links
+ subscribe(listener) {
+ // Listen to incoming links for deep links
+ const linkingSubscription = Linking.addEventListener('url', ({ url }) => {
+ listener(url);
+ });
+
+ // Listen to expo push notifications when user interacts with them
+ const pushNotificationSubscription =
+ Notifications.addNotificationResponseReceivedListener((response) => {
+ const url = response.notification.request.content.data.url;
+
+ listener(url);
+ });
+
+ return () => {
+ // Clean up the event listeners
+ linkingSubscription.remove();
+ pushNotificationSubscription.remove();
+ };
+ },
+};
+```
+
+
+
+
+```js name="Expo Notifications"
+const linking = {
+ prefixes: ['example://', 'https://app.example.com'],
+
+ // Custom function to get the URL which was used to open the app
+ async getInitialURL() {
+ // First, handle deep links
+ const url = await Linking.getInitialURL();
+
+ if (url != null) {
+ return url;
+ }
+
+ // Handle URL from expo push notifications
+ const response = await Notifications.getLastNotificationResponseAsync();
+
+ return response?.notification.request.content.data.url;
+ },
+
+ // Custom function to subscribe to incoming links
+ subscribe(listener) {
+ // Listen to incoming links for deep links
+ const linkingSubscription = Linking.addEventListener('url', ({ url }) => {
+ listener(url);
+ });
+
+ // Listen to expo push notifications when user interacts with them
+ const pushNotificationSubscription =
+ Notifications.addNotificationResponseReceivedListener((response) => {
+ const url = response.notification.request.content.data.url;
+
+ listener(url);
+ });
+
+ return () => {
+ // Clean up the event listeners
+ linkingSubscription.remove();
+ pushNotificationSubscription.remove();
+ };
+ },
+
+ config: {
+ // Deep link configuration
+ },
+};
+```
+
+
+
+
+Similar to the above example, you can integrate any API that provides a way to get the initial URL and to subscribe to new incoming URLs using the `getInitialURL` and `subscribe` options.
diff --git a/versioned_docs/version-8.x/devtools.md b/versioned_docs/version-8.x/devtools.md
new file mode 100644
index 00000000000..692ae42369b
--- /dev/null
+++ b/versioned_docs/version-8.x/devtools.md
@@ -0,0 +1,141 @@
+---
+id: devtools
+title: Developer tools
+sidebar_label: Developer tools
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+Developer tools to make debugging easier when using React Navigation.
+
+To use the developer tools, install [`@react-navigation/devtools`](https://github.com/react-navigation/react-navigation/tree/master/packages/devtools):
+
+```bash npm2yarn
+npm install @react-navigation/devtools
+```
+
+Hooks from this package only work during development and are disabled in production. You don't need to do anything special to remove them from the production build.
+
+## API Definition
+
+The package exposes the following APIs:
+
+### `useLogger`
+
+This hook provides a logger for React Navigation. It logs the navigation state and actions to the console.
+
+
+
+**Usage:**
+
+To use the hook, import it and pass a `ref` to the `NavigationContainer` as its argument:
+
+
+
+
+```js
+import * as React from 'react';
+import {
+ createStaticNavigation,
+ useNavigationContainerRef,
+} from '@react-navigation/native';
+import { useLogger } from '@react-navigation/devtools';
+
+/* content */
+
+export default function App() {
+ const navigationRef = useNavigationContainerRef();
+
+ useLogger(navigationRef);
+
+ return ;
+}
+```
+
+
+
+
+
+```js
+import * as React from 'react';
+import {
+ NavigationContainer,
+ useNavigationContainerRef,
+} from '@react-navigation/native';
+import { useLogger } from '@react-navigation/devtools';
+
+export default function App() {
+ const navigationRef = useNavigationContainerRef();
+
+ useLogger(navigationRef);
+
+ return (
+ {/* ... */}
+ );
+}
+```
+
+
+
+
+### `useReduxDevToolsExtension`
+
+This hook provides integration with [Redux DevTools Extension](https://github.com/reduxjs/redux-devtools). It also works with [`React Native Debugger app`](https://github.com/jhen0409/react-native-debugger) which includes this extension.
+
+**Usage:**
+
+To use the hook, import it and pass a `ref` to the `NavigationContainer` as its argument:
+
+
+
+
+```js
+import * as React from 'react';
+import {
+ createStaticNavigation,
+ useNavigationContainerRef,
+} from '@react-navigation/native';
+import { useReduxDevToolsExtension } from '@react-navigation/devtools';
+
+/* content */
+
+export default function App() {
+ const navigationRef = useNavigationContainerRef();
+
+ useReduxDevToolsExtension(navigationRef);
+
+ return ;
+}
+```
+
+
+
+
+
+```js
+import * as React from 'react';
+import {
+ NavigationContainer,
+ useNavigationContainerRef,
+} from '@react-navigation/native';
+import { useReduxDevToolsExtension } from '@react-navigation/devtools';
+
+export default function App() {
+ const navigationRef = useNavigationContainerRef();
+
+ useReduxDevToolsExtension(navigationRef);
+
+ return (
+ {/* ... */}
+ );
+}
+```
+
+
+
+
+Now, you'll be able to see logs from React Navigation in Redux DevTools Extension, e.g. when you're debugging your app with React Native Debugger app.
diff --git a/versioned_docs/version-8.x/drawer-actions.md b/versioned_docs/version-8.x/drawer-actions.md
new file mode 100755
index 00000000000..ed8e3d62c5c
--- /dev/null
+++ b/versioned_docs/version-8.x/drawer-actions.md
@@ -0,0 +1,250 @@
+---
+id: drawer-actions
+title: DrawerActions reference
+sidebar_label: DrawerActions
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+`DrawerActions` is an object containing methods for generating actions specific to drawer-based navigators. Its methods expand upon the actions available in [CommonActions](navigation-actions.md).
+
+The following actions are supported:
+
+### openDrawer
+
+The `openDrawer` action can be used to open the drawer pane.
+
+```js name="Drawer Actions - openDrawer" snack static2dynamic
+import * as React from 'react';
+import { View, Text } from 'react-native';
+import { Button } from '@react-navigation/elements';
+import {
+ createStaticNavigation,
+ useNavigation,
+} from '@react-navigation/native';
+import { createDrawerNavigator } from '@react-navigation/drawer';
+
+function HomeScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ Home!
+
+
+ );
+}
+
+const MyDrawer = createDrawerNavigator({
+ screens: {
+ Home: HomeScreen,
+ },
+});
+
+const Navigation = createStaticNavigation(MyDrawer);
+
+export default function App() {
+ return ;
+}
+```
+
+### closeDrawer
+
+The `closeDrawer` action can be used to close the drawer pane.
+
+```js name="Drawer Actions - closeDrawer" snack static2dynamic
+import * as React from 'react';
+import { View, Text } from 'react-native';
+import { Button } from '@react-navigation/elements';
+import {
+ createStaticNavigation,
+ useNavigation,
+ DrawerActions,
+} from '@react-navigation/native';
+import {
+ createDrawerNavigator,
+ DrawerContentScrollView,
+ DrawerItemList,
+ DrawerItem,
+} from '@react-navigation/drawer';
+
+function HomeScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ Home!
+
+
+ );
+}
+
+function CustomDrawerContent(props) {
+ const { navigation } = props;
+
+ return (
+
+
+ {
+ // codeblock-focus-start
+ navigation.dispatch(DrawerActions.closeDrawer());
+ // codeblock-focus-end
+ }}
+ />
+
+ );
+}
+
+const MyDrawer = createDrawerNavigator({
+ drawerContent: (props) => ,
+ screens: {
+ Home: HomeScreen,
+ },
+});
+
+const Navigation = createStaticNavigation(MyDrawer);
+
+export default function App() {
+ return ;
+}
+```
+
+### toggleDrawer
+
+The `toggleDrawer` action can be used to toggle the drawer pane.
+
+```js name="Drawer Actions - toggleDrawer" snack static2dynamic
+import * as React from 'react';
+import { View, Text } from 'react-native';
+import { Button } from '@react-navigation/elements';
+import {
+ createStaticNavigation,
+ useNavigation,
+ DrawerActions,
+} from '@react-navigation/native';
+import {
+ createDrawerNavigator,
+ DrawerContentScrollView,
+ DrawerItemList,
+ DrawerItem,
+} from '@react-navigation/drawer';
+
+function HomeScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ Home!
+
+
+ );
+}
+
+function CustomDrawerContent(props) {
+ const { navigation } = props;
+
+ return (
+
+
+ {
+ // codeblock-focus-start
+ navigation.dispatch(DrawerActions.toggleDrawer());
+ // codeblock-focus-end
+ }}
+ />
+
+ );
+}
+
+const MyDrawer = createDrawerNavigator({
+ drawerContent: (props) => ,
+ screens: {
+ Home: HomeScreen,
+ },
+});
+
+const Navigation = createStaticNavigation(MyDrawer);
+
+export default function App() {
+ return ;
+}
+```
+
+### jumpTo
+
+The `jumpTo` action can be used to jump to an existing route in the drawer navigator.
+
+- `name` - _string_ - Name of the route to jump to.
+- `params` - _object_ - Screen params to pass to the destination route.
+
+```js name="Drawer Actions - jumpTo" snack static2dynamic
+import * as React from 'react';
+import { View, Text } from 'react-native';
+import { Button } from '@react-navigation/elements';
+import {
+ createStaticNavigation,
+ useNavigation,
+ DrawerActions,
+} from '@react-navigation/native';
+import { createDrawerNavigator } from '@react-navigation/drawer';
+
+function HomeScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ Home!
+
+
+ );
+}
+
+function ProfileScreen({ route }) {
+ return (
+
+ Profile!
+ {route?.params?.user ? route.params.user : 'Noone'}'s profile
+
+ );
+}
+
+const MyDrawer = createDrawerNavigator({
+ screens: {
+ Home: HomeScreen,
+ Profile: ProfileScreen,
+ },
+});
+
+const Navigation = createStaticNavigation(MyDrawer);
+
+export default function App() {
+ return ;
+}
+```
diff --git a/versioned_docs/version-8.x/drawer-based-navigation.md b/versioned_docs/version-8.x/drawer-based-navigation.md
new file mode 100755
index 00000000000..d760ff5d33c
--- /dev/null
+++ b/versioned_docs/version-8.x/drawer-based-navigation.md
@@ -0,0 +1,627 @@
+---
+id: drawer-based-navigation
+title: Drawer navigation
+sidebar_label: Drawer navigation
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+Common pattern in navigation is to use drawer from left (sometimes right) side for navigating between screens.
+
+
+
+Before continuing, first install and configure [`@react-navigation/drawer`](https://github.com/react-navigation/react-navigation/tree/main/packages/drawer) and its dependencies following the [installation instructions](drawer-navigator.md#installation).
+
+## Minimal example of drawer-based navigation
+
+To use this drawer navigator, import it from `@react-navigation/drawer`:
+(swipe right to open)
+
+
+
+
+```js name="Drawer navigation" snack
+import * as React from 'react';
+import { View } from 'react-native';
+import { createDrawerNavigator } from '@react-navigation/drawer';
+import {
+ createStaticNavigation,
+ useNavigation,
+} from '@react-navigation/native';
+import { Button } from '@react-navigation/elements';
+
+function HomeScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+
+
+ );
+}
+
+function NotificationsScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+
+
+ );
+}
+
+const Drawer = createDrawerNavigator({
+ screens: {
+ Home: HomeScreen,
+ Notifications: NotificationsScreen,
+ },
+});
+
+const Navigation = createStaticNavigation(Drawer);
+
+export default function App() {
+ return ;
+}
+```
+
+
+
+
+```js name="Drawer navigation" snack
+import * as React from 'react';
+import { View } from 'react-native';
+import { createDrawerNavigator } from '@react-navigation/drawer';
+import { NavigationContainer, useNavigation } from '@react-navigation/native';
+import { Button } from '@react-navigation/elements';
+
+function HomeScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+
+
+ );
+}
+
+function NotificationsScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+
+
+ );
+}
+
+const Drawer = createDrawerNavigator();
+
+export default function App() {
+ return (
+
+
+
+
+
+
+ );
+}
+```
+
+
+
+
+## Opening and closing drawer
+
+To open and close drawer, use the following helpers:
+
+
+
+```js name="Drawer open and close" snack
+import * as React from 'react';
+import { View, Text } from 'react-native';
+import {
+ useNavigation,
+ createStaticNavigation,
+} from '@react-navigation/native';
+import {
+ createDrawerNavigator,
+ DrawerContentScrollView,
+ DrawerItemList,
+ DrawerItem,
+} from '@react-navigation/drawer';
+import { Button } from '@react-navigation/elements';
+
+function Feed() {
+ const navigation = useNavigation();
+
+ return (
+
+ Feed Screen
+ // codeblock-focus-start
+
+ // codeblock-focus-end
+
+
+ );
+}
+
+function Notifications() {
+ return (
+
+ Notifications Screen
+
+ );
+}
+
+// codeblock-focus-start
+
+/* content */
+
+// codeblock-focus-end
+
+function CustomDrawerContent(props) {
+ return (
+
+
+ // codeblock-focus-start
+ props.navigation.closeDrawer()}
+ />
+ // codeblock-focus-end
+ props.navigation.toggleDrawer()}
+ />
+
+ );
+}
+
+const Drawer = createDrawerNavigator({
+ drawerContent: (props) => ,
+ screens: {
+ Feed: Feed,
+ Notifications: Notifications,
+ },
+});
+
+const Navigation = createStaticNavigation(Drawer);
+
+export default function App() {
+ return ;
+}
+```
+
+
+
+
+```js name="Drawer open and close" snack
+import * as React from 'react';
+import { View, Text } from 'react-native';
+import { NavigationContainer, useNavigation } from '@react-navigation/native';
+import {
+ createDrawerNavigator,
+ DrawerContentScrollView,
+ DrawerItemList,
+ DrawerItem,
+} from '@react-navigation/drawer';
+import { Button } from '@react-navigation/elements';
+
+function Feed() {
+ const navigation = useNavigation();
+
+ return (
+
+ Feed Screen
+ // codeblock-focus-start
+
+ // codeblock-focus-end
+
+
+ );
+}
+
+function Notifications() {
+ return (
+
+ Notifications Screen
+
+ );
+}
+
+// codeblock-focus-start
+
+/* content */
+
+// codeblock-focus-end
+
+function CustomDrawerContent(props) {
+ return (
+
+
+ // codeblock-focus-start
+ props.navigation.closeDrawer()}
+ />
+ // codeblock-focus-end
+ props.navigation.toggleDrawer()}
+ />
+
+ );
+}
+
+const Drawer = createDrawerNavigator();
+
+function MyDrawer() {
+ return (
+ }
+ >
+
+
+
+ );
+}
+
+export default function App() {
+ return (
+
+
+
+ );
+}
+```
+
+
+
+
+If you would like to toggle the drawer you call the following:
+
+
+
+
+```js name="Drawer toggle" snack
+import * as React from 'react';
+import { View, Text } from 'react-native';
+import {
+ useNavigation,
+ createStaticNavigation,
+} from '@react-navigation/native';
+import {
+ createDrawerNavigator,
+ DrawerContentScrollView,
+ DrawerItemList,
+ DrawerItem,
+} from '@react-navigation/drawer';
+import { Button } from '@react-navigation/elements';
+
+function Feed() {
+ const navigation = useNavigation();
+
+ return (
+
+ Feed Screen
+
+ // codeblock-focus-start
+
+ // codeblock-focus-end
+
+ );
+}
+
+function Notifications() {
+ return (
+
+ Notifications Screen
+
+ );
+}
+
+function CustomDrawerContent(props) {
+ return (
+
+
+ props.navigation.closeDrawer()}
+ />
+ props.navigation.toggleDrawer()}
+ />
+
+ );
+}
+
+const Drawer = createDrawerNavigator({
+ drawerContent: (props) => ,
+ screens: {
+ Feed: Feed,
+ Notifications: Notifications,
+ },
+});
+
+const Navigation = createStaticNavigation(Drawer);
+
+export default function App() {
+ return ;
+}
+```
+
+
+
+
+```js name="Drawer toggle" snack
+import * as React from 'react';
+import { View, Text } from 'react-native';
+import { NavigationContainer, useNavigation } from '@react-navigation/native';
+import {
+ createDrawerNavigator,
+ DrawerContentScrollView,
+ DrawerItemList,
+ DrawerItem,
+} from '@react-navigation/drawer';
+import { Button } from '@react-navigation/elements';
+
+function Feed() {
+ const navigation = useNavigation();
+
+ return (
+
+ Feed Screen
+
+ // codeblock-focus-start
+
+ // codeblock-focus-end
+
+ );
+}
+
+function Notifications() {
+ return (
+
+ Notifications Screen
+
+ );
+}
+
+function CustomDrawerContent(props) {
+ return (
+
+
+ props.navigation.closeDrawer()}
+ />
+ props.navigation.toggleDrawer()}
+ />
+
+ );
+}
+
+const Drawer = createDrawerNavigator();
+
+function MyDrawer() {
+ return (
+ }
+ >
+
+
+
+ );
+}
+
+export default function App() {
+ return (
+
+
+
+ );
+}
+```
+
+
+
+
+Each of these functions, behind the scenes, are simply dispatching actions:
+
+
+
+
+```js name="Navigation dispatcher" snack
+import * as React from 'react';
+import { View, Text } from 'react-native';
+import {
+ createStaticNavigation,
+ useNavigation,
+ DrawerActions,
+} from '@react-navigation/native';
+import {
+ createDrawerNavigator,
+ DrawerContentScrollView,
+ DrawerItemList,
+ DrawerItem,
+} from '@react-navigation/drawer';
+import { Button } from '@react-navigation/elements';
+
+function Feed() {
+ const navigation = useNavigation();
+
+ return (
+
+ Feed Screen
+ // codeblock-focus-start
+
+ // codeblock-focus-end
+
+
+ );
+}
+
+function Notifications() {
+ return (
+
+ Notifications Screen
+
+ );
+}
+
+// codeblock-focus-start
+
+/* content */
+
+// codeblock-focus-end
+
+function CustomDrawerContent(props) {
+ return (
+
+
+ // codeblock-focus-start
+ props.navigation.dispatch(DrawerActions.closeDrawer())}
+ />
+ props.navigation.dispatch(DrawerActions.toggleDrawer())}
+ />
+ // codeblock-focus-end
+
+ );
+}
+
+const Drawer = createDrawerNavigator({
+ drawerContent: (props) => ,
+ screens: {
+ Feed: Feed,
+ Notifications: Notifications,
+ },
+});
+
+const Navigation = createStaticNavigation(Drawer);
+
+export default function App() {
+ return ;
+}
+```
+
+
+
+
+```js name="Navigation dispatcher" snack
+import * as React from 'react';
+import { View, Text } from 'react-native';
+import {
+ NavigationContainer,
+ useNavigation,
+ DrawerActions,
+} from '@react-navigation/native';
+import {
+ createDrawerNavigator,
+ DrawerContentScrollView,
+ DrawerItemList,
+ DrawerItem,
+} from '@react-navigation/drawer';
+import { Button } from '@react-navigation/elements';
+
+function Feed() {
+ const navigation = useNavigation();
+
+ return (
+
+ Feed Screen
+ // codeblock-focus-start
+
+ // codeblock-focus-end
+
+
+ );
+}
+
+function Notifications() {
+ return (
+
+ Notifications Screen
+
+ );
+}
+// codeblock-focus-start
+
+/* content */
+
+// codeblock-focus-end
+
+function CustomDrawerContent(props) {
+ return (
+
+
+ // codeblock-focus-start
+ props.navigation.dispatch(DrawerActions.closeDrawer())}
+ />
+ props.navigation.dispatch(DrawerActions.toggleDrawer())}
+ />
+ // codeblock-focus-end
+
+ );
+}
+
+const Drawer = createDrawerNavigator();
+
+function MyDrawer() {
+ return (
+ }
+ >
+
+
+
+ );
+}
+
+export default function App() {
+ return (
+
+
+
+ );
+}
+```
+
+
+
+
+If you would like to determine if drawer is open or closed, you can do the following:
+
+```js name="Drawer hook"
+import { useDrawerStatus } from '@react-navigation/drawer';
+
+// ...
+
+const isDrawerOpen = useDrawerStatus() === 'open';
+```
diff --git a/versioned_docs/version-8.x/drawer-layout.md b/versioned_docs/version-8.x/drawer-layout.md
new file mode 100644
index 00000000000..0a5d8ddb7d1
--- /dev/null
+++ b/versioned_docs/version-8.x/drawer-layout.md
@@ -0,0 +1,248 @@
+---
+id: drawer-layout
+title: React Native Drawer Layout
+sidebar_label: Drawer Layout
+---
+
+A cross-platform Drawer component for React Native implemented using [`react-native-gesture-handler`](https://docs.swmansion.com/react-native-gesture-handler/) and [`react-native-reanimated`](https://docs.swmansion.com/react-native-reanimated/) on native platforms and CSS transitions on Web.
+
+
+
+This package doesn't integrate with React Navigation. If you want to integrate the drawer layout with React Navigation's navigation system, e.g. want to show screens in the drawer and be able to navigate between them using `navigation.navigate` etc, use [Drawer Navigator](drawer-navigator.md) instead.
+
+## Installation
+
+To use this package, open a Terminal in the project root and run:
+
+```bash npm2yarn
+npm install react-native-drawer-layout
+```
+
+The library depends on [`react-native-gesture-handler`](https://docs.swmansion.com/react-native-gesture-handler/) for gestures and [`react-native-reanimated`](https://docs.swmansion.com/react-native-reanimated/) for animations.
+
+
+
+
+If you have a Expo managed project, in your project directory, run:
+
+```bash
+npx expo install react-native-gesture-handler react-native-reanimated react-native-worklets
+```
+
+
+
+
+If you have a bare React Native project, in your project directory, run:
+
+```bash npm2yarn
+npm install react-native-gesture-handler react-native-reanimated react-native-worklets
+```
+
+After installation, configure the Reanimated Babel Plugin in your project following the [installation guide](https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/getting-started).
+
+
+
+
+If you're on a Mac and developing for iOS, you also need to install [pods](https://cocoapods.org/) to complete the linking.
+
+```bash
+npx pod-install ios
+```
+
+## Quick start
+
+```js
+import * as React from 'react';
+import { Text } from 'react-native';
+import { Drawer } from 'react-native-drawer-layout';
+import { Button } from '@react-navigation/elements';
+
+export default function DrawerExample() {
+ const [open, setOpen] = React.useState(false);
+
+ return (
+ setOpen(true)}
+ onClose={() => setOpen(false)}
+ renderDrawerContent={() => {
+ return Drawer content;
+ }}
+ >
+
+ );
+}
+```
+
+## API reference
+
+The package exports a `Drawer` component which is the one you'd use to render the drawer.
+
+### `Drawer`
+
+Component is responsible for rendering a drawer sidebar with animations and gestures.
+
+#### Drawer Props
+
+##### `open`
+
+Whether the drawer is open or not.
+
+##### `onOpen`
+
+Callback which is called when the drawer is opened.
+
+##### `onClose`
+
+Callback which is called when the drawer is closed.
+
+##### `renderDrawerContent`
+
+Callback which returns a react element to render as the content of the drawer.
+
+##### `layout`
+
+Object containing the layout of the container. Defaults to the dimensions of the application's window.
+
+##### `drawerPosition`
+
+Position of the drawer on the screen. Defaults to `right` in RTL mode, otherwise `left`.
+
+##### `drawerType`
+
+Type of the drawer. It determines how the drawer looks and animates.
+
+- `front`: Traditional drawer which covers the screen with a overlay behind it.
+- `back`: The drawer is revealed behind the screen on swipe.
+- `slide`: Both the screen and the drawer slide on swipe to reveal the drawer.
+- `permanent`: A permanent drawer is shown as a sidebar.
+
+Defaults to `front`.
+
+##### `drawerStyle`
+
+Style object for the drawer. You can pass a custom background color for drawer or a custom width for the drawer.
+
+##### `overlayStyle`
+
+Style object for the overlay.
+
+##### `hideStatusBarOnOpen`
+
+Whether to hide the status bar when the drawer is open. Defaults to `false`.
+
+##### `keyboardDismissMode`
+
+Whether to dismiss the keyboard when the drawer is open. Supported values are:
+
+- `none`: The keyboard will not be dismissed when the drawer is open.
+- `on-drag`: The keyboard will be dismissed when the drawer is opened by a swipe gesture.
+
+Defaults to `on-drag`.
+
+##### `statusBarAnimation`
+
+Animation to use when the status bar is hidden. Supported values are:
+
+- `slide`: The status bar will slide out of view.
+- `fade`: The status bar will fade out of view.
+- `none`: The status bar will not animate.
+
+Use it in combination with `hideStatusBarOnOpen`.
+
+##### `swipeEnabled`
+
+Whether to enable swipe gestures to open the drawer. Defaults to `true`.
+
+Swipe gestures are only supported on iOS and Android.
+
+##### `swipeEdgeWidth`
+
+How far from the edge of the screen the swipe gesture should activate. Defaults to `32`.
+
+This is only supported on iOS and Android.
+
+##### `swipeMinDistance`
+
+Minimum swipe distance that should activate opening the drawer. Defaults to `60`.
+
+This is only supported on iOS and Android.
+
+##### `swipeMinVelocity`
+
+Minimum swipe velocity that should activate opening the drawer. Defaults to `500`.
+
+This is only supported on iOS and Android.
+
+#### `configureGestureHandler`
+
+Callback to configure the underlying [gesture from `react-native-gesture-handler`](https://docs.swmansion.com/react-native-gesture-handler/docs/gestures/gesture). It receives the `gesture` object as an argument:
+
+```js
+configureGestureHandler={({ gesture }) => {
+ return gesture.enableTrackpadTwoFingerGesture(false);
+}}
+```
+
+##### `children`
+
+Content that the drawer should wrap.
+
+### `useDrawerProgress`
+
+The `useDrawerProgress` hook returns a Reanimated `SharedValue` which represents the progress of the drawer. It can be used to animate the content of the screen.
+
+Example with modern implementation:
+
+```js
+import { Animated } from 'react-native-reanimated';
+import { useDrawerProgress } from 'react-native-drawer-layout';
+
+// ...
+
+function MyComponent() {
+ const progress = useDrawerProgress();
+
+ const animatedStyle = useAnimatedStyle(() => {
+ return {
+ transform: [
+ {
+ translateX: interpolate(progress, [0, 1], [-100, 0]),
+ },
+ ],
+ };
+ });
+
+ return {/* ... */};
+}
+```
+
+If you are using class components, you can use the `DrawerProgressContext` to get the progress value.
+
+```js
+import { DrawerProgressContext } from 'react-native-drawer-layout';
+
+// ...
+
+class MyComponent extends React.Component {
+ static contextType = DrawerProgressContext;
+
+ render() {
+ const progress = this.context;
+
+ // ...
+ }
+}
+```
+
+:::warning
+
+The `useDrawerProgress` hook (or `DrawerProgressContext`) will return a mock value on Web since Reanimated is not used on Web. The mock value can only represent the open state of the drawer (`0` when closed, `1` when open), and not the progress of the drawer.
+
+:::
diff --git a/versioned_docs/version-8.x/drawer-navigator.md b/versioned_docs/version-8.x/drawer-navigator.md
new file mode 100644
index 00000000000..0feb5e44dbb
--- /dev/null
+++ b/versioned_docs/version-8.x/drawer-navigator.md
@@ -0,0 +1,889 @@
+---
+id: drawer-navigator
+title: Drawer Navigator
+sidebar_label: Drawer
+---
+
+Drawer Navigator renders a navigation drawer on the side of the screen which can be opened and closed via gestures.
+
+
+
+This wraps [`react-native-drawer-layout`](drawer-layout.md). If you want to use the drawer without React Navigation integration, use the library directly instead.
+
+## Installation
+
+To use this navigator, ensure that you have [`@react-navigation/native` and its dependencies (follow this guide)](getting-started.md), then install [`@react-navigation/drawer`](https://github.com/react-navigation/react-navigation/tree/main/packages/drawer):
+
+```bash npm2yarn
+npm install @react-navigation/drawer
+```
+
+The navigator depends on [`react-native-gesture-handler`](https://docs.swmansion.com/react-native-gesture-handler/) for gestures and [`react-native-reanimated`](https://docs.swmansion.com/react-native-reanimated/) for animations.
+
+
+
+
+If you have a Expo managed project, in your project directory, run:
+
+```bash
+npx expo install react-native-gesture-handler react-native-reanimated react-native-worklets
+```
+
+
+
+
+If you have a bare React Native project, in your project directory, run:
+
+```bash npm2yarn
+npm install react-native-gesture-handler react-native-reanimated react-native-worklets
+```
+
+After installation, configure the Reanimated Babel Plugin in your project following the [installation guide](https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/getting-started).
+
+
+
+
+If you're on a Mac and developing for iOS, you also need to install [pods](https://cocoapods.org/) to complete the linking.
+
+```bash
+npx pod-install ios
+```
+
+## Usage
+
+To use this navigator, import it from `@react-navigation/drawer`:
+
+```js name="Drawer Navigator" snack static2dynamic
+import * as React from 'react';
+import { Text, View } from 'react-native';
+import {
+ createStaticNavigation,
+ useNavigation,
+} from '@react-navigation/native';
+import { Button } from '@react-navigation/elements';
+// codeblock-focus-start
+import { createDrawerNavigator } from '@react-navigation/drawer';
+
+// codeblock-focus-end
+function HomeScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ Home Screen
+
+
+ );
+}
+
+function ProfileScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ Profile Screen
+
+
+ );
+}
+
+// codeblock-focus-start
+const MyDrawer = createDrawerNavigator({
+ screens: {
+ Home: HomeScreen,
+ Profile: ProfileScreen,
+ },
+});
+// codeblock-focus-end
+
+const Navigation = createStaticNavigation(MyDrawer);
+
+export default function App() {
+ return ;
+}
+```
+
+## API Definition
+
+### Props
+
+In addition to the [common props](navigator.md#configuration) shared by all navigators, the drawer navigator component accepts the following additional props:
+
+#### `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.
+
+It supports the following values:
+
+- `firstRoute` - return to the first screen defined in the navigator (default)
+- `initialRoute` - return to initial screen passed in `initialRouteName` prop, if not passed, defaults to the first screen
+- `order` - return to screen defined before the focused screen
+- `history` - return to last visited screen in the navigator; if the same screen is visited multiple times, the older entries are dropped from the history
+- `fullHistory` - return to last visited screen in the navigator; doesn't drop duplicate entries unlike `history` - this behavior is useful to match how web pages work
+- `none` - do not handle back button
+
+#### `defaultStatus`
+
+The default status of the drawer - whether the drawer should stay `open` or `closed` by default.
+
+When this is set to `open`, the drawer will be open from the initial render. It can be closed normally using gestures or programmatically. However, when going back, the drawer will re-open if it was closed. This is essentially the opposite of the default behavior of the drawer where it starts `closed`, and the back button closes an open drawer.
+
+#### `detachInactiveScreens`
+
+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`.
+
+#### `drawerContent`
+
+Function that returns React element to render as the content of the drawer, for example, navigation items
+
+The content component receives the following props by default:
+
+- `state` - The [navigation state](navigation-state.md) of the navigator.
+- `navigation` - The navigation object for the navigator.
+- `descriptors` - An descriptor object containing options for the drawer screens. The options can be accessed at `descriptors[route.key].options`.
+
+##### Providing a custom `drawerContent`
+
+The default component for the drawer is scrollable and only contains links for the routes in the RouteConfig. You can easily override the default component to add a header, footer, or other content to the drawer. The default content component is exported as `DrawerContent`. It renders a `DrawerItemList` component inside a `ScrollView`.
+
+By default, the drawer is scrollable and supports devices with notches. If you customize the content, you can use `DrawerContentScrollView` to handle this automatically:
+
+```js
+import {
+ DrawerContentScrollView,
+ DrawerItemList,
+} from '@react-navigation/drawer';
+
+function CustomDrawerContent(props) {
+ return (
+
+
+
+ );
+}
+```
+
+To add additional items in the drawer, you can use the `DrawerItem` component:
+
+```js name="Custom Drawer Content" snack static2dynamic
+import * as React from 'react';
+import { Text, View, Linking } from 'react-native';
+import { createStaticNavigation } from '@react-navigation/native';
+import {
+ createDrawerNavigator,
+ DrawerContentScrollView,
+ DrawerItemList,
+ DrawerItem,
+} from '@react-navigation/drawer';
+
+function HomeScreen() {
+ return (
+
+ Home Screen
+
+ );
+}
+
+function SettingsScreen() {
+ return (
+
+ Settings Screen
+
+ );
+}
+
+// codeblock-focus-start
+function CustomDrawerContent(props) {
+ return (
+
+
+ Linking.openURL('https://mywebsite.com/help')}
+ />
+
+ );
+}
+// codeblock-focus-end
+
+const MyDrawer = createDrawerNavigator({
+ drawerContent: (props) => ,
+ screens: {
+ Home: HomeScreen,
+ Settings: SettingsScreen,
+ },
+});
+
+const Navigation = createStaticNavigation(MyDrawer);
+
+export default function App() {
+ return ;
+}
+```
+
+The `DrawerItem` component accepts the following props:
+
+- `label` (required): The label text of the item. Can be string, or a function returning a react element. e.g. `({ focused, color }) => {focused ? 'Focused text' : 'Unfocused text'}`.
+- `icon`: Icon to display for the item. Accepts a function returning a react element. e.g. `({ focused, color, size }) => `.
+- `focused`: Boolean indicating whether to highlight the drawer item as active.
+- `onPress` (required): Function to execute on press.
+- `activeTintColor`: Color for the icon and label when the item is active.
+- `inactiveTintColor`: Color for the icon and label when the item is inactive.
+- `activeBackgroundColor`: Background color for item when it's active.
+- `inactiveBackgroundColor`: Background color for item when it's inactive.
+- `labelStyle`: Style object for the label `Text`.
+- `style`: Style object for the wrapper `View`.
+
+Note that you **cannot** use the `useNavigation` hook inside the `drawerContent` since `useNavigation` is only available inside screens. You get a `navigation` prop for your `drawerContent` which you can use instead:
+
+```js
+function CustomDrawerContent({ navigation }) {
+ return (
+
+ );
+}
+```
+
+To use the custom component, we need to pass it in the `drawerContent` prop:
+
+```js
+}>
+ {/* screens */}
+
+```
+
+### Options
+
+The following [options](screen-options.md) can be used to configure the screens in the navigator. These can be specified under `screenOptions` prop of `Drawer.Navigator` or `options` prop of `Drawer.Screen`.
+
+#### `title`
+
+A generic title that can be used as a fallback for `headerTitle` and `drawerLabel`.
+
+#### `lazy`
+
+Whether this screen should render the first time it's accessed. Defaults to `true`. Set it to `false` if you want to render the screen on initial render.
+
+#### `drawerLabel`
+
+String or a function that given `{ focused: boolean, color: string }` returns a React.Node, to display in drawer sidebar. When undefined, scene `title` is used.
+
+#### `drawerIcon`
+
+Function, that given `{ focused: boolean, color: string, size: number }` returns a React.Node to display in drawer sidebar.
+
+#### `drawerActiveTintColor`
+
+Color for the icon and label in the active item in the drawer.
+
+
+
+```js
+ drawerActiveTintColor: 'green',
+```
+
+#### `drawerActiveBackgroundColor`
+
+Background color for the active item in the drawer.
+
+
+
+```js
+ screenOptions={{
+ drawerActiveTintColor: 'white',
+ drawerActiveBackgroundColor: '#003CB3',
+ drawerLabelStyle: {
+ color: 'white',
+ },
+ }}
+```
+
+#### `drawerInactiveTintColor`
+
+Color for the icon and label in the inactive items in the drawer.
+
+#### `drawerInactiveBackgroundColor`
+
+Background color for the inactive items in the drawer.
+
+#### `drawerItemStyle`
+
+Style object for the single item, which can contain an icon and/or a label.
+
+
+
+Example:
+
+```js
+ drawerItemStyle: {
+ backgroundColor: '#9dd3c8',
+ borderColor: 'black',
+ orderWidth: 2,
+ opacity: 0.6,
+ },
+```
+
+#### `drawerLabelStyle`
+
+Style object to apply to the `Text` style inside content section which renders a label.
+
+
+
+Example:
+
+```js
+ drawerLabelStyle: {
+ color: 'black',
+ fontSize: 20,
+ fontFamily: 'Georgia',
+ },
+```
+
+#### `drawerContentContainerStyle`
+
+Style object for the content section inside the `ScrollView`.
+
+#### `drawerContentStyle`
+
+Style object for the wrapper view.
+
+#### `drawerStyle`
+
+Style object for the drawer component. You can pass a custom background color for a drawer or a custom width here.
+
+
+
+```js
+
+ {/* screens */}
+
+```
+
+#### `drawerPosition`
+
+Options are `left` or `right`. Defaults to `left` for LTR languages and `right` for RTL languages.
+
+#### `drawerType`
+
+Type of the drawer. It determines how the drawer looks and animates.
+
+- `front`: Traditional drawer which covers the screen with an overlay behind it.
+
+
+- `back`: The drawer is revealed behind the screen on swipe.
+
+
+- `slide`: Both the screen and the drawer slide on swipe to reveal the drawer.
+
+
+- `permanent`: A permanent drawer is shown as a sidebar. Useful for having always visible drawer on larger screens.
+
+Defaults to `slide` on iOS and `front` on other platforms.
+
+You can conditionally specify the `drawerType` to show a permanent drawer on bigger screens and a traditional drawer drawer on small screens:
+
+```js
+import { useWindowDimensions } from 'react-native';
+import { createDrawerNavigator } from '@react-navigation/drawer';
+
+const Drawer = createDrawerNavigator();
+
+function MyDrawer() {
+ const dimensions = useWindowDimensions();
+
+ return (
+ = 768 ? 'permanent' : 'front',
+ }}
+ >
+ {/* Screens */}
+
+ );
+}
+```
+
+You can also specify other props such as `drawerStyle` based on screen size to customize the behavior. For example, you can combine it with `defaultStatus="open"` to achieve a master-detail layout:
+
+
+
+```js
+import { useWindowDimensions } from 'react-native';
+import { createDrawerNavigator } from '@react-navigation/drawer';
+
+const Drawer = createDrawerNavigator();
+
+function MyDrawer() {
+ const dimensions = useWindowDimensions();
+
+ const isLargeScreen = dimensions.width >= 768;
+
+ return (
+
+ {/* Screens */}
+
+ );
+}
+```
+
+#### `drawerHideStatusBarOnOpen`
+
+When set to `true`, Drawer will hide the OS status bar whenever the drawer is pulled or when it's in an "open" state.
+
+#### `drawerStatusBarAnimation`
+
+Animation of the statusbar when hiding it. use in combination with `drawerHideStatusBarOnOpen`.
+
+This is only supported on iOS. Defaults to `slide`.
+
+Supported values:
+
+- `slide`
+
+
+- `fade`
+
+
+- `none`
+
+#### `overlayStyle`
+
+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)',
+}
+```
+
+
+
+#### `sceneStyle`
+
+Style object for the component wrapping the screen content.
+
+#### `configureGestureHandler`
+
+Callback to configure the underlying [gesture from `react-native-gesture-handler`](https://docs.swmansion.com/react-native-gesture-handler/docs/gestures/gesture). It receives the `gesture` object as an argument:
+
+```js
+configureGestureHandler: ({ gesture }) => {
+ return gesture.enableTrackpadTwoFingerGesture(false);
+},
+```
+
+This is not supported on Web.
+
+#### `swipeEnabled`
+
+Whether you can use swipe gestures to open or close the drawer. Defaults to `true`.
+
+Swipe gesture is not supported on Web.
+
+#### `swipeEdgeWidth`
+
+Allows for defining how far from the edge of the content view the swipe gesture should activate.
+
+This is not supported on Web.
+
+#### `swipeMinDistance`
+
+Minimum swipe distance threshold that should activate opening the drawer.
+
+#### `keyboardDismissMode`
+
+Whether the keyboard should be dismissed when the swipe gesture begins. Defaults to `'on-drag'`. Set to `'none'` to disable keyboard handling.
+
+#### `freezeOnBlur`
+
+Boolean indicating whether to prevent inactive screens from re-rendering. Defaults to `false`.
+Defaults to `true` when `enableFreeze()` from `react-native-screens` package is run at the top of the application.
+
+Only supported on iOS and Android.
+
+#### `popToTopOnBlur`
+
+Boolean indicating whether any nested stack should be popped to the top of the stack when navigating away from this drawer screen. Defaults to `false`.
+
+It only works when there is a stack navigator (e.g. [stack navigator](stack-navigator.md) or [native stack navigator](native-stack-navigator.md)) nested under the drawer navigator.
+
+### 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 `Drawer.Navigator` or `options` prop of `Drawer.Screen`. You don't have to be using `@react-navigation/elements` directly to use these options, they are just documented in that page.
+
+In addition to those, the following options are also supported in drawer:
+
+#### `header`
+
+Custom header to use instead of the default header.
+
+This accepts a function that returns a React Element to display as a header. The function receives an object containing the following properties as the argument:
+
+- `navigation` - The navigation object for the current screen.
+- `route` - The route object for the current screen.
+- `options` - The options for the current screen
+- `layout` - Dimensions of the screen, contains `height` and `width` properties.
+
+Example:
+
+```js
+import { getHeaderTitle } from '@react-navigation/elements';
+
+// ..
+
+header: ({ navigation, route, options }) => {
+ const title = getHeaderTitle(options, route.name);
+
+ return ;
+};
+```
+
+To set a custom header for all the screens in the navigator, you can specify this option in the `screenOptions` prop of the navigator.
+
+##### Specify a `height` in `headerStyle`
+
+If your custom header's height differs from the default header height, then you might notice glitches due to measurement being async. Explicitly specifying the height will avoid such glitches.
+
+Example:
+
+```js
+headerStyle: {
+ height: 80, // Specify the height of your custom header
+};
+```
+
+Note that this style is not applied to the header by default since you control the styling of your custom header. If you also want to apply this style to your header, use `options.headerStyle` from the props.
+
+#### `headerShown`
+
+Whether to show or hide the header for the screen. The header is shown by default. Setting this to `false` hides the header.
+
+### Events
+
+The navigator can [emit events](navigation-events.md) on certain actions. Supported events are:
+
+#### `drawerItemPress`
+
+This event is fired when the user presses the button for the screen in the drawer. By default a drawer item press does several things:
+
+- If the screen is not focused, drawer item press will focus that screen
+- If the screen is already focused, then it'll close the drawer
+
+To prevent the default behavior, you can call `event.preventDefault`:
+
+```js
+React.useEffect(() => {
+ const unsubscribe = navigation.addListener('drawerItemPress', (e) => {
+ // Prevent default behavior
+ e.preventDefault();
+
+ // Do something manually
+ // ...
+ });
+
+ return unsubscribe;
+}, [navigation]);
+```
+
+If you have custom drawer content, make sure to emit this event.
+
+### Helpers
+
+The drawer navigator adds the following methods to the navigation object:
+
+#### `openDrawer`
+
+Opens the drawer pane.
+
+```js name="Drawer Helper Methods" snack static2dynamic
+import * as React from 'react';
+import { Text, View } from 'react-native';
+import {
+ createStaticNavigation,
+ useNavigation,
+} from '@react-navigation/native';
+import { Button } from '@react-navigation/elements';
+import { createDrawerNavigator } from '@react-navigation/drawer';
+
+function HomeScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ Home Screen
+
+
+ );
+}
+
+function SettingsScreen() {
+ return (
+
+ Settings Screen
+
+ );
+}
+
+const MyDrawer = createDrawerNavigator({
+ screens: {
+ Home: HomeScreen,
+ Settings: SettingsScreen,
+ },
+});
+
+const Navigation = createStaticNavigation(MyDrawer);
+
+export default function App() {
+ return ;
+}
+```
+
+#### `closeDrawer`
+
+Closes the drawer pane.
+
+```js
+navigation.closeDrawer();
+```
+
+#### `toggleDrawer`
+
+Opens the drawer pane if closed, closes the drawer pane if opened.
+
+```js
+navigation.toggleDrawer();
+```
+
+#### `jumpTo`
+
+Navigates to an existing screen in the drawer navigator. The method accepts the following arguments:
+
+- `name` - _string_ - Name of the route to jump to.
+- `params` - _object_ - Screen params to pass to the destination route.
+
+```js name="Drawer Navigator - jumpTo" snack static2dynamic
+import * as React from 'react';
+import { Text, View } from 'react-native';
+import {
+ createStaticNavigation,
+ useNavigation,
+} from '@react-navigation/native';
+import { Button } from '@react-navigation/elements';
+import { createDrawerNavigator } from '@react-navigation/drawer';
+
+function HomeScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ Home Screen
+
+
+ );
+}
+
+function ProfileScreen({ route }) {
+ return (
+
+ Profile Screen
+ {route.params?.owner && (
+ Owner: {route.params.owner}
+ )}
+
+ );
+}
+
+const MyDrawer = createDrawerNavigator({
+ screens: {
+ Home: HomeScreen,
+ Profile: ProfileScreen,
+ },
+});
+
+const Navigation = createStaticNavigation(MyDrawer);
+
+export default function App() {
+ return ;
+}
+```
+
+### Hooks
+
+The drawer navigator exports the following hooks:
+
+#### `useDrawerProgress`
+
+This hook returns the progress of the drawer. It is available in the screen components rendered by the drawer navigator as well as in the [custom drawer content](#drawercontent).
+
+The `progress` object is a `SharedValue` that represents the animated position of the drawer (`0` is closed; `1` is open). It can be used to animate elements based on the animation of the drawer with [Reanimated](https://docs.swmansion.com/react-native-reanimated/):
+
+```js name="Drawer animation progress" snack static2dynamic
+import * as React from 'react';
+import { Text, View } from 'react-native';
+import { createStaticNavigation } from '@react-navigation/native';
+import { Button } from '@react-navigation/elements';
+// codeblock-focus-start
+import {
+ createDrawerNavigator,
+ useDrawerProgress,
+} from '@react-navigation/drawer';
+import Animated, { useAnimatedStyle } from 'react-native-reanimated';
+
+function HomeScreen() {
+ // highlight-next-line
+ const progress = useDrawerProgress();
+
+ const animatedStyle = useAnimatedStyle(() => ({
+ transform: [{ translateX: progress.value * -100 }],
+ }));
+
+ return (
+
+
+
+ );
+}
+// codeblock-focus-end
+
+const MyDrawer = createDrawerNavigator({
+ screens: {
+ Home: HomeScreen,
+ },
+});
+
+const Navigation = createStaticNavigation(MyDrawer);
+
+export default function App() {
+ return ;
+}
+```
+
+If you are using class components, you can use the `DrawerProgressContext` to get the progress value.
+
+:::warning
+
+The `useDrawerProgress` hook (or `DrawerProgressContext`) will return a mock value on Web since Reanimated is not used on Web. The mock value can only represent the open state of the drawer (`0` when closed, `1` when open), and not the progress of the drawer.
+
+:::
+
+#### `useDrawerStatus`
+
+You can check if the drawer is open by using the `useDrawerStatus` hook.
+
+```js
+import { useDrawerStatus } from '@react-navigation/drawer';
+
+// ...
+
+const isDrawerOpen = useDrawerStatus() === 'open';
+```
+
+If you can't use the hook, you can also use the `getDrawerStatusFromState` helper:
+
+```js
+import { getDrawerStatusFromState } from '@react-navigation/drawer';
+
+// ...
+
+const isDrawerOpen = getDrawerStatusFromState(navigation.getState()) === 'open';
+```
+
+For class components, you can listen to the `state` event to check if the drawer was opened or closed:
+
+```js
+class Profile extends React.Component {
+ componentDidMount() {
+ this._unsubscribe = navigation.addListener('state', () => {
+ const isDrawerOpen =
+ getDrawerStatusFromState(navigation.getState()) === 'open';
+
+ // do something
+ });
+ }
+
+ componentWillUnmount() {
+ this._unsubscribe();
+ }
+
+ render() {
+ // Content of the component
+ }
+}
+```
+
+## Nesting drawer navigators inside others
+
+If a drawer navigator is nested inside of another navigator that provides some UI, for example, a tab navigator or stack navigator, then the drawer will be rendered below the UI from those navigators. The drawer will appear below the tab bar and below the header of the stack. You will need to make the drawer navigator the parent of any navigator where the drawer should be rendered on top of its UI.
diff --git a/versioned_docs/version-8.x/elements.md b/versioned_docs/version-8.x/elements.md
new file mode 100644
index 00000000000..c2ebb46a096
--- /dev/null
+++ b/versioned_docs/version-8.x/elements.md
@@ -0,0 +1,910 @@
+---
+id: elements
+title: Elements Library
+sidebar_label: Elements
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+A component library containing the UI elements and helpers used in React Navigation. It can be useful if you're building your own navigator, or want to reuse a default functionality in your app.
+
+## Installation
+
+To use this package, ensure that you have [`@react-navigation/native` and its dependencies (follow this guide)](getting-started.md), then install [`@react-navigation/elements`](https://github.com/react-navigation/react-navigation/tree/main/packages/elements):
+
+```bash npm2yarn
+npm install @react-navigation/elements
+```
+
+## Components
+
+### `Header`
+
+A component that can be used as a header. This is used by all the navigators by default.
+
+Usage:
+
+```js name="React Navigation Elements Header" snack
+import * as React from 'react';
+import { SafeAreaProviderCompat } from '@react-navigation/elements';
+import { NavigationContainer } from '@react-navigation/native';
+// codeblock-focus-start
+import { Header } from '@react-navigation/elements';
+
+function MyHeader() {
+ return ;
+}
+// codeblock-focus-end
+
+export default function App() {
+ return (
+
+
+
+
+
+ );
+}
+```
+
+To use the header in a navigator, you can use the `header` option in the screen options:
+
+
+
+
+```js name="Header with Native Stack" snack
+import * as React from 'react';
+import { Text, View, Button } from 'react-native';
+import { createStaticNavigation } from '@react-navigation/native';
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+// codeblock-focus-start
+import { Header, getHeaderTitle } from '@react-navigation/elements';
+
+// codeblock-focus-end
+function HomeScreen() {
+ return (
+
+ Home Screen
+
+ );
+}
+
+// codeblock-focus-start
+const MyStack = createNativeStackNavigator({
+ screenOptions: {
+ header: ({ options, route, back }) => (
+
+ ),
+ },
+ screens: {
+ Home: HomeScreen,
+ },
+});
+// codeblock-focus-end
+
+const Navigation = createStaticNavigation(MyStack);
+
+export default function App() {
+ return ;
+}
+```
+
+
+
+
+```js name="Header with Native Stack" snack
+import * as React from 'react';
+import { Text, View, Button } from 'react-native';
+import { NavigationContainer } from '@react-navigation/native';
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+// codeblock-focus-start
+import { Header, getHeaderTitle } from '@react-navigation/elements';
+
+const Stack = createNativeStackNavigator();
+
+function MyStack() {
+ return (
+ (
+
+ ),
+ }}
+ >
+
+
+ );
+}
+// codeblock-focus-end
+
+function HomeScreen() {
+ return (
+
+ Home Screen
+
+ );
+}
+
+export default function App() {
+ return (
+
+
+
+ );
+}
+```
+
+
+
+
+:::note
+
+This doesn't replicate the behavior of the header in stack and native stack navigators as the stack navigator also includes animations, and the native stack navigator header is provided by the native platform.
+
+:::
+
+It accepts the following props:
+
+#### `headerTitle`
+
+String or a function that returns a React Element to be used by the header. Defaults to scene `title`.
+
+When a function is specified, it receives an object containing following properties:
+
+- `allowFontScaling`: Whether it scale to respect Text Size accessibility settings.
+- `tintColor`: Text color of the header title.
+- `style`: Style object for the `Text` component.
+- `children`: The title string (from `title` in `options`).
+
+
+
+
+```js
+const RootStack = createNativeStackNavigator({
+ screens: {
+ Home: {
+ screen: HomeScreen,
+ options: {
+ headerTitle: ({ allowFontScaling, tintColor, style, children }) => (
+
+ {children}
+
+ ),
+ },
+ },
+ },
+});
+```
+
+
+
+
+```js
+ (
+
+ {children}
+
+ ),
+ }}
+/>
+```
+
+
+
+
+#### `headerTitleAlign`
+
+How to align the header title. Possible values:
+
+- `left`
+- `center`
+
+Defaults to `center` on iOS and `left` on Android.
+
+#### `headerTitleAllowFontScaling`
+
+Whether header title font should scale to respect Text Size accessibility settings. Defaults to `false`.
+
+#### `headerLeft`
+
+Function which returns a React Element to display on the left side of the header.
+
+It receives an object containing following properties:
+
+- `tintColor`: The color of the icon and label.
+- `pressColor`: The color of the material ripple (Android >= 5.0 only).
+- `pressOpacity`: The opacity of the button when it's pressed (Android < 5.0, and iOS).
+- `displayMode`: How the element displays icon and title. Defaults to `default` on iOS and `minimal` on Android. Possible values:
+ - `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.
+- `href`: The URL to open when the button is pressed on the Web.
+
+You can use it to implement your custom left button, for example:
+
+
+
+
+```js
+const RootStack = createNativeStackNavigator({
+ screens: {
+ Home: {
+ screen: HomeScreen,
+ options: {
+ headerLeft: (props) => (
+ {
+ // Do something
+ }}>
+ )
+ }
+ }
+ }
+})
+```
+
+
+
+
+```js
+ (
+ {
+ // Do something
+ }}
+ />
+ ),
+ }}
+/>
+```
+
+
+
+
+#### `headerRight`
+
+Function which returns a React Element to display on the right side of the header.
+
+It receives an object containing following properties:
+
+- `tintColor`: The color of the icon and label.
+- `pressColor`: The color of the material ripple (Android >= 5.0 only).
+- `pressOpacity`: The opacity of the button when it's pressed (Android < 5.0, and iOS).
+
+
+
+
+```js
+const RootStack = createNativeStackNavigator({
+ screens: {
+ Home: {
+ screen: HomeScreen,
+ options: {
+ headerLeft: (props) => (
+ {
+ // Do something
+ }}>
+ )
+ }
+ }
+ }
+})
+```
+
+
+
+
+```js
+ (
+ {
+ // Do something
+ }}
+ />
+ ),
+ }}
+/>
+```
+
+
+
+
+#### `headerSearchBarOptions`
+
+Options for the search bar in the header. When this is specified, the header will contain a button to show a search input.
+
+It can contain the following properties:
+
+- `ref`: Ref to manipulate the search input imperatively. It contains the following methods:
+ - `focus` - focuses the search bar
+ - `blur` - removes focus from the search bar
+ - `setText` - sets the search bar's content to given value
+ - `clearText` - removes any text present in the search bar input field
+ - `cancelSearch` - cancel the search and close the search bar
+- `autoCapitalize`: The auto-capitalization behavior. Possible values: `none`, `words`, `sentences`, `characters`.
+- `autoFocus`: Automatically focus search input on mount.
+- `cancelButtonText`: Text to be used instead of default `Cancel` button text (iOS only).
+- `inputType`: Type of the input. Possible values: `text`, `phone`, `number`, `email`.
+- `onBlur`: Callback that gets called when search input has lost focus.
+- `onChangeText`: Callback that gets called when the text changes.
+- `onClose`: Callback that gets called when search input is closed.
+- `onFocus`: Callback that gets called when search input has received focus.
+- `placeholder`: Text displayed when search input is empty.
+
+```js
+React.useLayoutEffect(() => {
+ navigation.setOptions({
+ headerSearchBarOptions: {
+ placeholder: 'Search',
+ onChangeText: (text) => {
+ // Do something
+ },
+ },
+ });
+}, [navigation]);
+```
+
+#### `headerShadowVisible`
+
+Whether to hide the elevation shadow (Android) or the bottom border (iOS) on the header.
+
+This is a short-hand for the following styles:
+
+```js
+{
+ elevation: 0,
+ shadowOpacity: 0,
+ borderBottomWidth: 0,
+}
+```
+
+If any of the above styles are specified in `headerStyle` along with `headerShadowVisible: false`, then the styles in `headerStyle` will take precedence.
+
+#### `headerStyle`
+
+Style object for the header. You can specify a custom background color here, for example:
+
+```js
+{
+ backgroundColor: 'tomato',
+}
+```
+
+Note that `headerStyle` won't take effect if you are also using [`headerBackground`](#headerbackground). In that case, you should style the element returned from `headerBackground` instead.
+
+#### `headerTitleStyle`
+
+Style object for the title component
+
+#### `headerLeftContainerStyle`
+
+Customize the style for the container of the `headerLeft` component, for example to add padding.
+
+#### `headerRightContainerStyle`
+
+Customize the style for the container of the `headerRight` component, for example to add padding.
+
+#### `headerTitleContainerStyle`
+
+Customize the style for the container of the `headerTitle` component, for example to add padding.
+
+By default, `headerTitleContainerStyle` is with an absolute position style and offsets both `left` and `right`. This may lead to white space or overlap between `headerLeft` and `headerTitle` if a customized `headerLeft` is used. It can be solved by adjusting `left` and `right` style in `headerTitleContainerStyle` and `marginHorizontal` in `headerTitleStyle`.
+
+#### `headerBackgroundContainerStyle`
+
+Style object for the container of the `headerBackground` element.
+
+#### `headerTintColor`
+
+Tint color for the header
+
+#### `headerPressColor`
+
+Color for material ripple (Android >= 5.0 only)
+
+#### `headerPressOpacity`
+
+Press opacity for the buttons in header (Android < 5.0, and iOS)
+
+#### `headerTransparent`
+
+Defaults to `false`. If `true`, the header will not have a background unless you explicitly provide it with `headerBackground`. The header will also float over the screen so that it overlaps the content underneath.
+
+This is useful if you want to render a semi-transparent header or a blurred background.
+
+Note that if you don't want your content to appear under the header, you need to manually add a top margin to your content. React Navigation won't do it automatically.
+
+To get the height of the header, you can use [`HeaderHeightContext`](#headerheightcontext) with [React's Context API](https://react.dev/reference/react/useContext#contextconsumer) or [`useHeaderHeight`](#useheaderheight).
+
+#### `headerBackground`
+
+Function which returns a React Element to render as the background of the header. This is useful for using backgrounds such as an image or a gradient.
+
+For example, you can use this with `headerTransparent` to render a blur view to create a translucent header.
+
+
+
+
+```js name="Header blur" snack dependencies=expo-blur
+import * as React from 'react';
+import { View, StyleSheet } from 'react-native';
+import {
+ createStaticNavigation,
+ useNavigation,
+} from '@react-navigation/native';
+import { createStackNavigator } from '@react-navigation/stack';
+import { Button } from '@react-navigation/elements';
+import { BlurView } from 'expo-blur';
+
+function HomeScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+
+
+ );
+}
+
+function ProfileScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+
+
+ );
+}
+
+// codeblock-focus-start
+const Stack = createStackNavigator({
+ initialRouteName: 'Home',
+ screens: {
+ Home: {
+ screen: HomeScreen,
+ options: {
+ headerTransparent: true,
+ headerBackground: () => (
+
+ ),
+ },
+ },
+ Profile: ProfileScreen,
+ },
+});
+// codeblock-focus-end
+
+const Navigation = createStaticNavigation(Stack);
+
+function App() {
+ return ;
+}
+
+export default App;
+```
+
+
+
+
+```js name="Header blur" snack dependencies=expo-blur
+import * as React from 'react';
+import { View, StyleSheet } from 'react-native';
+import { NavigationContainer, useNavigation } from '@react-navigation/native';
+import { createStackNavigator } from '@react-navigation/stack';
+import { Button } from '@react-navigation/elements';
+// codeblock-focus-start
+import { BlurView } from 'expo-blur';
+
+// codeblock-focus-end
+
+function HomeScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+
+
+ );
+}
+
+function ProfileScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+
+
+ );
+}
+
+const Stack = createStackNavigator();
+
+function App() {
+ return (
+
+
+ // codeblock-focus-start
+ (
+
+ ),
+ }}
+ />
+ // codeblock-focus-end
+
+
+
+ );
+}
+
+export default App;
+```
+
+
+
+
+#### `headerStatusBarHeight`
+
+Extra padding to add at the top of header to account for translucent status bar. By default, it uses the top value from the safe area insets of the device. Pass 0 or a custom value to disable the default behavior, and customize the height.
+
+### `HeaderBackground`
+
+A component containing the styles used in the background of the header, such as the background color and shadow. It's the default for [`headerBackground`](#headerbackground). It accepts the same props as a [`View`](https://reactnative.dev/docs/view).
+
+Usage:
+
+```js
+
+```
+
+### `HeaderTitle`
+
+A component used to show the title text in header. It's the default for [`headerTitle`](#headertitle). It accepts the same props as a [`Text`](https://reactnative.dev/docs/Text).
+
+The color of title defaults to the [theme text color](themes.md). You can override it by passing a `tintColor` prop.
+
+Usage:
+
+```js
+Hello
+```
+
+### `HeaderButton`
+
+A component used to show a button in header. It can be used for both left and right buttons. It accepts the following props:
+
+- `onPress` - Callback to call when the button is pressed.
+- `href` - The `href` to use for the anchor tag on web.
+- `disabled` - Boolean which controls whether the button is disabled.
+- `accessibilityLabel` - Accessibility label for the button for screen readers.
+- `testID` - ID to locate this button in tests.
+- `tintColor` - Tint color for the header button.
+- `pressColor` - Color for material ripple (Android >= 5.0 only).
+- `pressOpacity` - Opacity when the button is pressed if material ripple isn't supported by the platform.
+- `style` - Style object for the button.
+- `children` - Content to render for the button. Usually the icon.
+
+Usage:
+
+```js
+ console.log('button pressed')}
+>
+
+
+```
+
+### `HeaderBackButton`
+
+A component used to show the back button header. It's the default for [`headerLeft`](#headerleft) in the [stack navigator](stack-navigator.md). It accepts the following props:
+
+- `disabled` - Boolean which controls Whether the button is disabled.
+- `onPress` - Callback to call when the button is pressed.
+- `pressColor` - Color for material ripple (Android >= 5.0 only).
+- `backImage` - Function which returns a React Element to display custom image in header's back button.
+- `tintColor` - Tint color for the header.
+- `label` - Label text for the button. Usually the title of the previous screen. By default, this is only shown on iOS.
+- `truncatedLabel` - Label text to show when there isn't enough space for the full label.
+- `displayMode`: How the back button displays icon and title. Defaults to `default` on iOS and `minimal` on Android. Possible values:
+ - `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.
+- `labelStyle` - Style object for the label.
+- `allowFontScaling` - Whether label font should scale to respect Text Size accessibility settings.
+- `onLabelLayout` - Callback to trigger when the size of the label changes.
+- `screenLayout` - Layout of the screen.
+- `titleLayout` - Layout of the title element in the header.
+- `canGoBack` - Boolean to indicate whether it's possible to navigate back in stack.
+- `accessibilityLabel` - Accessibility label for the button for screen readers.
+- `testID` - ID to locate this button in tests.
+- `style` - Style object for the button.
+
+Usage:
+
+```js
+ console.log('back pressed')} />
+```
+
+### `MissingIcon`
+
+A component that renders a missing icon symbol. It can be used as a fallback for icons to show that there's a missing icon. It accepts the following props:
+
+- `color` - Color of the icon.
+- `size` - Size of the icon.
+- `style` - Additional styles for the icon.
+
+### `PlatformPressable`
+
+A component which provides an abstraction on top of [`Pressable`](https://reactnative.dev/docs/Pressable) to handle platform differences. In addition to `Pressable`'s props, it accepts following additional props:
+
+- `pressColor` - Color of material ripple on Android when it's pressed
+- `pressOpacity` - Opacity when it's pressed if material ripple isn't supported by the platform
+
+### `Button`
+
+A component that renders a button. In addition to [`PlatformPressable`](#platformpressable)'s props, it accepts following additional props:
+
+- `variant` - Variant of the button. Possible values are:
+ - `tinted` (default)
+ - `plain`
+ - `filled`
+- `color` - Color of the button. Defaults to the [theme](themes.md)'s primary color.
+- `children` - Content to render inside the button.
+
+In addition, the button integrates with React Navigation and accepts the same props as [`useLinkProps`](use-link-props.md#options) hook.
+
+It can be used to navigate between screens by specifying a screen name and params:
+
+```js
+
+```
+
+Or as a regular button:
+
+```js
+
+```
+
+### `Label`
+
+The `Label` component is used to render small text. It is used in [Bottom Tab Navigator](bottom-tab-navigator.md) to render the label for each tab.
+
+In addition to the standard [`Text`](https://reactnative.dev/docs/text) props, it accepts the following props:
+
+- `tintColor` - Color of the label. Defaults to the [theme](themes.md)'s text color.
+
+Usage:
+
+```jsx
+
+```
+
+### `Badge`
+
+A component that renders a badge, typically used to show a count or status indicator on tab icons.
+
+It accepts the following props:
+
+- `visible` - Whether the badge is visible.
+- `children` - Content of the badge (string or number).
+- `size` - Size of the badge. Defaults to `18`.
+- `style` - Style object for the badge.
+
+Usage:
+
+```jsx
+5
+```
+
+### `Text`
+
+A themed text component that automatically applies the theme's text color and font styles.
+
+It accepts the same props as React Native's [`Text`](https://reactnative.dev/docs/text) component.
+
+Usage:
+
+```jsx
+Hello World
+```
+
+## Utilities
+
+### `SafeAreaProviderCompat`
+
+A wrapper over the `SafeAreaProvider` component from [`react-native-safe-area-context](https://github.com/th3rdwave/react-native-safe-area-context) which includes initial values.
+
+Usage:
+
+```js
+{/* Your components */}
+```
+
+### `HeaderBackContext`
+
+React context that can be used to get the back title of the parent screen.
+
+```js
+import { HeaderBackContext } from '@react-navigation/elements';
+
+// ...
+
+
+ {(headerBack) => {
+ if (headerBack) {
+ const backTitle = headerBack.title;
+
+ /* render something */
+ }
+
+ /* render something */
+ }}
+;
+```
+
+### `HeaderShownContext`
+
+React context that can be used to check if a header is visible in a parent screen.
+
+```js
+import { HeaderShownContext } from '@react-navigation/elements';
+
+// ...
+
+
+ {(headerShown) => {
+ /* render something */
+ }}
+;
+```
+
+### `HeaderHeightContext`
+
+React context that can be used to get the height of the nearest visible header in a parent screen.
+
+```js
+import { HeaderHeightContext } from '@react-navigation/elements';
+
+// ...
+
+
+ {(headerHeight) => {
+ /* render something */
+ }}
+;
+```
+
+### `useHeaderHeight`
+
+Hook that returns the height of the nearest visible header in the parent screen.
+
+```js
+import { useHeaderHeight } from '@react-navigation/elements';
+
+// ...
+
+const headerHeight = useHeaderHeight();
+```
+
+### `useFrameSize`
+
+Hook that returns the size of the frame of the parent navigator. It accepts a selector function which receives the frame dimensions and returns a value:
+
+```js
+import { useFrameSize } from '@react-navigation/elements';
+
+// ...
+
+const isLandscape = useFrameSize((frame) => frame.width > frame.height);
+```
+
+The selector ensures that the component only re-renders when we need to.
+
+### `getDefaultHeaderHeight`
+
+Helper that returns the default header height. It takes an object with the following properties:
+
+- `landscape` - Whether the device is in landscape orientation.
+- `modalPresentation` - Whether the screen is presented as a modal.
+- `topInset` - The height of the top inset (status bar height).
+
+```js
+import { getDefaultHeaderHeight } from '@react-navigation/elements';
+
+// ...
+
+const headerHeight = getDefaultHeaderHeight({
+ landscape: false,
+ modalPresentation: false,
+ topInset: statusBarHeight,
+});
+```
+
+### `getHeaderTitle`
+
+Helper that returns the title text to use in header. It takes the following parameters:
+
+- `options` - The options object of the screen.
+- `fallback` - Fallback title string if no title was found in options.
+
+### `getLabel`
+
+Helper that returns the label text to use. It takes the following parameters:
+
+- `options` - An object with optional `label` and `title` properties.
+- `fallback` - Fallback label string if no label or title was found in options.
+
+```js
+import { getLabel } from '@react-navigation/elements';
+
+// ...
+
+const label = getLabel(options, route.name);
+```
+
+### `getDefaultSidebarWidth`
+
+Helper that returns the default sidebar width based on the screen dimensions. It follows Material Design 3 guidelines for navigation drawer specs.
+
+It takes an object with the following property:
+
+- `width` - The width of the screen.
+
+```js
+import { getDefaultSidebarWidth } from '@react-navigation/elements';
+
+// ...
+
+const sidebarWidth = getDefaultSidebarWidth({ width: screenWidth });
+```
diff --git a/versioned_docs/version-8.x/function-after-focusing-screen.md b/versioned_docs/version-8.x/function-after-focusing-screen.md
new file mode 100755
index 00000000000..39add70e42a
--- /dev/null
+++ b/versioned_docs/version-8.x/function-after-focusing-screen.md
@@ -0,0 +1,353 @@
+---
+id: function-after-focusing-screen
+title: Call a function when focused screen changes
+sidebar_label: Call a function on focus
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+In this guide we will call a function or render something on screen focusing. This is useful for making additional API calls when a user revisits a particular screen in a Tab Navigator, or to track user events as they tap around our app.
+
+There are multiple approaches available to us:
+
+1. Listening to the `'focus'` event with an event listener.
+2. Using the `useFocusEffect` hook provided by react-navigation.
+3. Using the `useIsFocused` hook provided by react-navigation.
+
+## Triggering an action with a `'focus'` event listener
+
+We can also listen to the `'focus'` event with an event listener. After setting up an event listener, we must also stop listening to the event when the screen is unmounted.
+
+With this approach, we will only be able to call an action when the screen focuses. This is useful for performing an action such as logging the screen view for analytics.
+
+Example:
+
+
+
+
+```js name="Focus event listener" snack
+// codeblock-focus-start
+import * as React from 'react';
+import { View } from 'react-native';
+
+// codeblock-focus-end
+import {
+ useNavigation,
+ createStaticNavigation,
+} from '@react-navigation/native';
+import {
+ createBottomTabNavigator,
+ createBottomTabScreen,
+} from '@react-navigation/bottom-tabs';
+
+// codeblock-focus-start
+function ProfileScreen() {
+ const navigation = useNavigation();
+
+ React.useEffect(() => {
+ const unsubscribe = navigation.addListener('focus', () => {
+ alert('Screen is focused');
+ // The screen is focused
+ // Call any action
+ });
+
+ // Return the function to unsubscribe from the event so it gets removed on unmount
+ return unsubscribe;
+ }, [navigation]);
+
+ return ;
+}
+// codeblock-focus-end
+
+function HomeScreen() {
+ return ;
+}
+
+const MyTabs = createBottomTabNavigator({
+ screens: {
+ Home: createBottomTabScreen({
+ screen: HomeScreen,
+ }),
+ Profile: createBottomTabScreen({
+ screen: ProfileScreen,
+ }),
+ },
+});
+
+const Navigation = createStaticNavigation(MyTabs);
+
+export default function App() {
+ return ;
+}
+```
+
+
+
+
+```js name="Focus event listener" snack
+// codeblock-focus-start
+import * as React from 'react';
+import { View } from 'react-native';
+
+// codeblock-focus-end
+import { NavigationContainer, useNavigation } from '@react-navigation/native';
+import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
+
+// codeblock-focus-start
+function ProfileScreen() {
+ const navigation = useNavigation();
+
+ React.useEffect(() => {
+ const unsubscribe = navigation.addListener('focus', () => {
+ alert('Screen is focused');
+ // The screen is focused
+ // Call any action
+ });
+
+ // Return the function to unsubscribe from the event so it gets removed on unmount
+ return unsubscribe;
+ }, [navigation]);
+
+ return ;
+}
+// codeblock-focus-end
+
+function HomeScreen() {
+ return ;
+}
+
+const Tab = createBottomTabNavigator();
+
+export default function App() {
+ return (
+
+
+
+
+
+
+ );
+}
+```
+
+
+
+
+See the [navigation events guide](navigation-events.md) for more details on the event listener API.
+
+In most cases, it's recommended to use the `useFocusEffect` hook instead of adding the listener manually. See below for details.
+
+## Triggering an action with the `useFocusEffect` hook
+
+React Navigation provides a [hook](use-focus-effect.md) that runs an effect when the screen comes into focus and cleans it up when it goes out of focus. This is useful for cases such as adding event listeners, for fetching data with an API call when a screen becomes focused, or any other action that needs to happen once the screen comes into view.
+
+This is particularly handy when we are trying to stop something when the page is unfocused, like stopping a video or audio file from playing, or stopping the tracking of a user's location.
+
+
+
+
+```js name="useFocusEffect hook" snack
+import * as React from 'react';
+import { View } from 'react-native';
+import {
+ useFocusEffect,
+ createStaticNavigation,
+} from '@react-navigation/native';
+import {
+ createBottomTabNavigator,
+ createBottomTabScreen,
+} from '@react-navigation/bottom-tabs';
+
+// codeblock-focus-start
+function ProfileScreen() {
+ useFocusEffect(
+ React.useCallback(() => {
+ alert('Screen was focused');
+ // Do something when the screen is focused
+ return () => {
+ alert('Screen was unfocused');
+ // Do something when the screen is unfocused
+ // Useful for cleanup functions
+ };
+ }, [])
+ );
+
+ return ;
+}
+// codeblock-focus-end
+
+function HomeScreen() {
+ return ;
+}
+
+const MyTabs = createBottomTabNavigator({
+ screens: {
+ Home: createBottomTabScreen({
+ screen: HomeScreen,
+ }),
+ Profile: createBottomTabScreen({
+ screen: ProfileScreen,
+ }),
+ },
+});
+const Navigation = createStaticNavigation(MyTabs);
+
+export default function App() {
+ return ;
+}
+```
+
+
+
+
+```js name="useFocusEffect hook" snack
+import * as React from 'react';
+import { View } from 'react-native';
+import { NavigationContainer, useFocusEffect } from '@react-navigation/native';
+import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
+
+// codeblock-focus-start
+function ProfileScreen() {
+ useFocusEffect(
+ React.useCallback(() => {
+ alert('Screen was focused');
+ // Do something when the screen is focused
+ return () => {
+ alert('Screen was unfocused');
+ // Do something when the screen is unfocused
+ // Useful for cleanup functions
+ };
+ }, [])
+ );
+
+ return ;
+}
+// codeblock-focus-end
+
+function HomeScreen() {
+ return ;
+}
+
+const Tab = createBottomTabNavigator();
+
+export default function App() {
+ return (
+
+
+
+
+
+
+ );
+}
+```
+
+
+
+
+See the [`useFocusEffect`](https://reactnavigation.org/docs/use-focus-effect/) documentation for more details.
+
+## Re-rendering screen with the `useIsFocused` hook
+
+React Navigation provides a [hook](use-is-focused.md) that returns a boolean indicating whether the screen is focused or not.
+
+The hook will return `true` when the screen is focused and `false` when our component is no longer focused. This enables us to render something conditionally based on whether the user is on the screen or not.
+
+The `useIsFocused` hook will cause our component to re-render when we focus and unfocus a screen. Using this hook component may introduce unnecessary component re-renders as a screen comes in and out of focus. This could cause issues depending on the type of action we're calling on focusing. Hence we recommend to use this hook only if you need to trigger a re-render. For side-effects such as subscribing to events or fetching data, use the methods described above.
+
+
+
+
+```js name="useIsFocused hook" snack
+import * as React from 'react';
+import { View, Text } from 'react-native';
+import { useIsFocused, createStaticNavigation } from '@react-navigation/native';
+import {
+ createMaterialTopTabNavigator,
+ createMaterialTopTabScreen,
+} from '@react-navigation/material-top-tabs';
+
+// codeblock-focus-start
+function ProfileScreen() {
+ // codeblock-focus-end
+ // This hook returns `true` if the screen is focused, `false` otherwise
+ // codeblock-focus-start
+ const isFocused = useIsFocused();
+
+ return (
+
+ {isFocused ? 'focused' : 'unfocused'}
+
+ );
+}
+// codeblock-focus-end
+
+function HomeScreen() {
+ return ;
+}
+
+const MyTabs = createMaterialTopTabNavigator({
+ screens: {
+ Home: createMaterialTopTabScreen({
+ screen: HomeScreen,
+ }),
+ Profile: createMaterialTopTabScreen({
+ screen: ProfileScreen,
+ }),
+ },
+});
+
+const Navigation = createStaticNavigation(MyTabs);
+
+export default function App() {
+ return ;
+}
+```
+
+
+
+
+```js name="useIsFocused hook" snack
+import * as React from 'react';
+import { View, Text } from 'react-native';
+import { NavigationContainer, useIsFocused } from '@react-navigation/native';
+import { createMaterialTopTabNavigator } from '@react-navigation/material-top-tabs';
+
+// codeblock-focus-start
+function ProfileScreen() {
+ // codeblock-focus-end
+ // This hook returns `true` if the screen is focused, `false` otherwise
+ // codeblock-focus-start
+ const isFocused = useIsFocused();
+
+ return (
+
+ {isFocused ? 'focused' : 'unfocused'}
+
+ );
+}
+// codeblock-focus-end
+
+function HomeScreen() {
+ return ;
+}
+
+const Tab = createMaterialTopTabNavigator();
+
+export default function App() {
+ return (
+
+
+
+
+
+
+ );
+}
+```
+
+
+
+
+This example is also documented in the [`useIsFocused` API documentation](use-is-focused.md).
diff --git a/versioned_docs/version-8.x/getting-started.md b/versioned_docs/version-8.x/getting-started.md
new file mode 100755
index 00000000000..66272242552
--- /dev/null
+++ b/versioned_docs/version-8.x/getting-started.md
@@ -0,0 +1,190 @@
+---
+id: getting-started
+title: Getting started
+sidebar_label: Getting started
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+What follows within the _Fundamentals_ section of this documentation is a tour of the most important aspects of React Navigation. It should cover enough for you to know how to build your typical small mobile application, and give you the background that you need to dive deeper into the more advanced parts of React Navigation.
+
+## Pre-requisites
+
+If you're already familiar with JavaScript, React and React Native, then you'll be able to get moving with React Navigation quickly! If not, we highly recommend you to gain some basic knowledge first, then come back here when you're done.
+
+Here are some resources to help you out:
+
+1. [Main Concepts of React](https://react.dev/learn)
+2. [Getting started with React Native](https://reactnative.dev/docs/getting-started)
+3. [React Hooks](https://react.dev/reference/react/hooks)
+4. [React Context](https://react.dev/learn/passing-data-deeply-with-context)
+
+## Minimum requirements
+
+- `react-native` >= 0.81
+- `expo` >= 54 (if you use [Expo Go](https://expo.dev/go))
+- `typescript` >= 5.9.2 (if you use TypeScript)
+- `react-native-web` >= 0.21.0 (if you support Web)
+
+## Starter template
+
+If you're starting a new project, you can use the [React Navigation template](https://github.com/react-navigation/template) to quickly set up a new project with [Static configuration](#static-configuration):
+
+```bash
+npx create-expo-app@latest --template react-navigation/template
+```
+
+See the project's `README.md` for more information on how to get started.
+
+If you created a new project using the template, you can skip the installation steps below and move on to ["Hello React Navigation"](hello-react-navigation.md?config=static).
+
+Otherwise, you can follow the instructions below to install React Navigation into your existing project.
+
+## Installation
+
+The `@react-navigation/native` package contains the core functionality of React Navigation.
+
+In your project directory, run:
+
+```bash npm2yarn
+npm install @react-navigation/native
+```
+
+### Installing dependencies
+
+Let's also install and configure dependencies used by most navigators. The libraries we will install now are [`react-native-screens`](https://github.com/software-mansion/react-native-screens) and [`react-native-safe-area-context`](https://github.com/th3rdwave/react-native-safe-area-context).
+
+
+
+
+In your project directory, run:
+
+```bash
+npx expo install react-native-screens react-native-safe-area-context
+```
+
+This will install versions of these libraries that are compatible with your Expo SDK version.
+
+
+
+
+In your project directory, run:
+
+```bash npm2yarn
+npm install react-native-screens react-native-safe-area-context
+```
+
+If you're on a Mac and developing for iOS, you need to install the pods (via [Cocoapods](https://cocoapods.org/)) to complete the linking.
+
+```bash
+npx pod-install ios
+```
+
+#### Configuring `react-native-screens` on Android
+
+[`react-native-screens`](https://github.com/software-mansion/react-native-screens) requires one additional configuration to properly work on Android.
+
+Edit `MainActivity.kt` or `MainActivity.java` file under `android/app/src/main/java//`, and add the highlighted code to the body of `MainActivity` class:
+
+
+
+
+```kotlin
+// highlight-start
+import android.os.Bundle
+import com.swmansion.rnscreens.fragment.restoration.RNScreensFragmentFactory
+// highlight-end
+
+// ...
+
+class MainActivity: ReactActivity() {
+ // ...
+
+ // highlight-start
+ override fun onCreate(savedInstanceState: Bundle?) {
+ supportFragmentManager.fragmentFactory = RNScreensFragmentFactory()
+ super.onCreate(savedInstanceState)
+ }
+ // highlight-end
+
+ // ...
+}
+```
+
+
+
+
+```java
+// highlight-start
+import android.os.Bundle;
+import com.swmansion.rnscreens.fragment.restoration.RNScreensFragmentFactory;
+// highlight-end
+
+// ...
+
+public class MainActivity extends ReactActivity {
+ // ...
+
+ // highlight-start
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ getSupportFragmentManager().setFragmentFactory(new RNScreensFragmentFactory());
+ super.onCreate(savedInstanceState);
+ }
+ // highlight-end
+
+ // ...
+}
+```
+
+
+
+
+This change is required to avoid crashes related to View state being not persisted consistently across Activity restarts.
+
+#### Opting-out of predictive back on Android
+
+React Navigation doesn't yet support Android's predictive back gesture. Disabling it is necessary for the system back gesture to work properly with React Navigation.
+
+To opt out, in `AndroidManifest.xml`, in the `` tag (or `` tag to opt-out at activity level), set the `android:enableOnBackInvokedCallback` flag to `false`:
+
+```xml
+
+
+
+```
+
+
+
+
+## Setting up React Navigation
+
+Once you've installed and configured the dependencies, you can move on to setting up your project to use React Navigation.
+
+When using React Navigation, you configure [**navigators**](glossary-of-terms.md#navigator) in your app. Navigators handle the transition between screens in your app and provide UI such as header, tab bar etc.
+
+:::info
+
+When you use a navigator (such as stack navigator), you'll need to follow the installation instructions of that navigator for any additional dependencies.
+
+:::
+
+There are 2 primary ways to configure the navigators:
+
+### Static configuration
+
+The static configuration API lets you write your configuration in an object, and is defined statically, though some aspects of the configuration can still can be changed dynamically. This has reduced boilerplate and simplifies things such as TypeScript types and deep linking.
+
+If you're starting a new project or are new to React Navigation, this is the **recommended way** to set up your app. If you need more flexibility in the future, you can always mix and match with the dynamic configuration.
+
+Continue to ["Hello React Navigation"](hello-react-navigation.md?config=static) to start writing some code with the static API.
+
+### Dynamic configuration
+
+The dynamic configuration API lets you write your configuration in React components, and can change at runtime based on state or props. This allows for more flexibility but requires significantly more boilerplate and configuration for Typescript types, deep linking etc.
+
+Continue to ["Hello React Navigation"](hello-react-navigation.md?config=dynamic) to start writing some code with the dynamic API.
diff --git a/versioned_docs/version-8.x/glossary-of-terms.md b/versioned_docs/version-8.x/glossary-of-terms.md
new file mode 100755
index 00000000000..70edc366942
--- /dev/null
+++ b/versioned_docs/version-8.x/glossary-of-terms.md
@@ -0,0 +1,106 @@
+---
+id: glossary-of-terms
+title: Glossary of terms
+sidebar_label: Glossary of terms
+---
+
+:::note
+
+This is a new section of the documentation and it's missing a lot of terms! Please [submit a pull request or an issue](https://github.com/react-navigation/react-navigation.github.io) with a term that you think should be explained here.
+
+:::
+
+## Navigator
+
+A `Navigator` is React component that decides how to render the screens you have defined. It contains `Screen` elements as its children to define the configuration for screens.
+
+`NavigationContainer` is a component which manages our navigation tree and contains the [navigation state](navigation-state.md). This component must wrap all navigators structure. Usually, we'd render this component at the root of our app, which is usually the component exported from `App.js`.
+
+```js
+function App() {
+ return (
+
+ // <---- This is a Navigator
+
+
+
+ );
+}
+```
+
+## Router
+
+A router is a collection of functions that decide how to handle actions and state changes in the navigator (similar to reducers in Redux apps). Normally you'd never need to interact with a router directly, unless you're writing a [custom navigator](custom-navigators.md).
+
+## Screen component
+
+A screen component is a component that we use in our route configuration.
+
+```js
+const Stack = createNativeStackNavigator();
+
+const StackNavigator = (
+
+
+
+
+);
+```
+
+The suffix `Screen` in the component name is entirely optional, but a frequently used convention; we could call it `Michael` and this would work just the same.
+
+## Navigation object
+
+The navigation object contains methods used for navigation. It contains methods such as:
+
+- `dispatch` will send an action up to the router
+- `navigate`, `goBack`, etc are available to dispatch actions in a convenient way
+
+This object can be accessed with the [`useNavigation`](use-navigation.md) hook. It's also passed as a prop to screens defined with the dynamic API.
+
+For more details, see the ["Navigation object docs"](navigation-object.md).
+
+The ["Route object reference"](route-object.md) section goes into more detail on this, describes workarounds, and provides more information on other properties available on `route` object.
+
+## Route object
+
+This prop will be passed to all screens. Contains information about the current route i.e. `params`, `key` and `name`. It can also contain arbitrary params:
+
+```js
+{
+ key: 'B',
+ name: 'Profile',
+ params: { id: '123' }
+}
+```
+
+For more details, see the ["Route object reference"](route-object.md).
+
+## Navigation State
+
+The state of a navigator generally looks something like this:
+
+```js
+{
+ key: 'StackRouterRoot',
+ index: 1,
+ routes: [
+ { key: 'A', name: 'Home' },
+ { key: 'B', name: 'Profile' },
+ ]
+}
+```
+
+For this navigation state, there are two routes (which may be tabs, or cards in a stack). The index indicates the active route, which is "B".
+
+You can read more about the navigation state [here](navigation-state.md).
+
+## Header
+
+Also known as navigation header, navigation bar, app bar, and probably many other things. This is the rectangle at the top of your screen that contains the back button and the title for your screen. The entire rectangle is often referred to as the header in React Navigation.
diff --git a/versioned_docs/version-8.x/group.md b/versioned_docs/version-8.x/group.md
new file mode 100644
index 00000000000..4a7b6583abb
--- /dev/null
+++ b/versioned_docs/version-8.x/group.md
@@ -0,0 +1,335 @@
+---
+id: group
+title: Group
+sidebar_label: Group
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+A group contains several [screens](screen.md) inside a navigator for organizational purposes. They can also be used to apply the same options such as header styles to a group of screens, or to define a common layout etc.
+
+
+
+
+Groups can be defined using the `groups` property in the navigator configuration:
+
+```js name="Stack groups" snack
+import * as React from 'react';
+import { View, Text } from 'react-native';
+import { Button } from '@react-navigation/elements';
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+import {
+ createStaticNavigation,
+ useNavigation,
+} from '@react-navigation/native';
+
+function HomeScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ Home Screen
+
+
+ );
+}
+
+function EmptyScreen() {
+ return ;
+}
+
+// codeblock-focus-start
+const MyStack = createNativeStackNavigator({
+ groups: {
+ App: {
+ screenOptions: {
+ headerStyle: {
+ backgroundColor: '#FFB6C1',
+ },
+ },
+ screens: {
+ Home: HomeScreen,
+ Profile: EmptyScreen,
+ },
+ },
+ Modal: {
+ screenOptions: {
+ presentation: 'modal',
+ },
+ screens: {
+ Search: EmptyScreen,
+ Share: EmptyScreen,
+ },
+ },
+ },
+});
+// codeblock-focus-end
+
+const Navigation = createStaticNavigation(Stack);
+
+export default function App() {
+ return ;
+}
+```
+
+The keys of the `groups` object (e.g. `Guest`, `User`) are used as the [`navigationKey`](#navigation-key) for the group. You can use any string as the key.
+
+
+
+
+A `Group` component is returned from a `createXNavigator` function. After creating the navigator, it can be used as children of the `Navigator` component:
+
+```js name="Stack groups" snack
+import * as React from 'react';
+import { View, Text } from 'react-native';
+import { Button } from '@react-navigation/elements';
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+import { NavigationContainer, useNavigation } from '@react-navigation/native';
+
+const Stack = createNativeStackNavigator();
+
+function HomeScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ Home Screen
+
+
+ );
+}
+
+function EmptyScreen() {
+ return ;
+}
+
+export default function App() {
+ return (
+
+ // codeblock-focus-start
+
+
+
+
+
+
+
+
+
+
+ // codeblock-focus-end
+
+ );
+}
+```
+
+It's also possible to nest `Group` components inside other `Group` components.
+
+
+
+
+## Configuration
+
+### Screen options
+
+Options to configure how the screens inside the group get presented in the navigator. It accepts either an object or a function returning an object:
+
+
+
+
+```js
+const MyStack = createNativeStackNavigator({
+ groups: {
+ Modal: {
+ // highlight-start
+ screenOptions: {
+ presentation: 'modal',
+ },
+ // highlight-end
+ screens: {
+ /* screens */
+ },
+ },
+ },
+});
+```
+
+
+
+
+```jsx
+
+ {/* screens */}
+
+```
+
+
+
+
+When you pass a function, it'll receive the [`route`](route-object.md) and [`navigation`](navigation-object.md):
+
+
+
+
+```js
+const MyStack = createNativeStackNavigator({
+ groups: {
+ Modal: {
+ // highlight-start
+ screenOptions: ({ route, navigation }) => ({
+ title: route.params.title,
+ }),
+ // highlight-end
+ screens: {
+ /* screens */
+ },
+ },
+ },
+});
+```
+
+
+
+
+```jsx
+ ({
+ title: route.params.title,
+ })}
+ // highlight-end
+>
+ {/* screens */}
+
+```
+
+
+
+
+These options are merged with the `options` specified in the individual screens, and the screen's options will take precedence over the group's options.
+
+See [Options for screens](screen-options.md) for more details and examples.
+
+### Screen layout
+
+A screen layout is a wrapper around each screen in the group. It makes it easier to provide things such as an error boundary and suspense fallback for all screens in the group, or wrap each screen with additional UI.
+
+It takes a function that returns a React element:
+
+
+
+
+```js
+const MyStack = createNativeStackNavigator({
+ groups: {
+ Modal: {
+ // highlight-start
+ screenLayout: ({ children }) => (
+
+
+ Loading…
+
+ }
+ >
+ {children}
+
+
+ ),
+ // highlight-end
+ screens: {
+ /* screens */
+ },
+ },
+ },
+});
+```
+
+
+
+
+```jsx
+ (
+
+
+ Loading…
+
+ }
+ >
+ {children}
+
+
+ )}
+ // highlight-end
+>
+ {/* screens */}
+
+```
+
+
+
+
+### Navigation key
+
+Optional key for a group of screens. If the key changes, all existing screens in this group will be removed (if used in a stack navigator) or reset (if used in a tab or drawer navigator):
+
+
+
+
+The name of the group is used as the `navigationKey`:
+
+```js
+const MyStack = createNativeStackNavigator({
+ groups: {
+ // highlight-next-line
+ User: {
+ screens: {
+ /* screens */
+ },
+ },
+ // highlight-next-line
+ Guest: {
+ screens: {
+ /* screens */
+ },
+ },
+ },
+});
+```
+
+This means if a screen is defined in 2 groups and the groups use the [`if`](static-configuration.md#if) property, the screen will remount if the condition changes resulting in one group being removed and the other group being used.
+
+
+
+
+```jsx
+
+ {/* screens */}
+
+```
+
+
+
+
+This is similar to the [`navigationKey`](screen.md#navigation-key) prop for screens, but applies to a group of screens.
diff --git a/versioned_docs/version-8.x/handling-safe-area.md b/versioned_docs/version-8.x/handling-safe-area.md
new file mode 100755
index 00000000000..225bfa87c1d
--- /dev/null
+++ b/versioned_docs/version-8.x/handling-safe-area.md
@@ -0,0 +1,478 @@
+---
+id: handling-safe-area
+title: Supporting safe areas
+sidebar_label: Supporting safe areas
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+By default, React Navigation tries to ensure that the elements of the navigators display correctly on devices with notches (e.g. iPhone X) and UI elements which may overlap the app content. Such items include:
+
+- Physical notches
+- Status bar overlay
+- Home activity indicator on iOS
+- Navigation bar on Android
+
+The area not overlapped by such items is referred to as "safe area".
+
+We try to apply proper insets on the UI elements of the navigators to avoid being overlapped by such items. The goal is to (a) maximize usage of the screen (b) without hiding content or making it difficult to interact with by having it obscured by a physical display cutout or some operating system UI.
+
+While React Navigation handles safe areas for the built-in UI elements by default, your own content may also need to handle it to ensure that content isn't hidden by these items.
+
+It's tempting to solve (a) by wrapping your entire app in a container with padding that ensures all content will not be occluded. But in doing so, we waste a bunch of space on the screen, as pictured in the image on the left below. What we ideally want is the image pictured on the right.
+
+
+
+While React Native exports a `SafeAreaView` component, this component only supports iOS 10+ with no support for older iOS versions or Android. In addition, it also has some issues, i.e. if a screen containing safe area is animating, it causes jumpy behavior. So we recommend to use the `useSafeAreaInsets` hook from the [react-native-safe-area-context](https://github.com/th3rdwave/react-native-safe-area-context) library to handle safe areas in a more reliable way.
+
+:::warning
+
+The `react-native-safe-area-context` library also exports a `SafeAreaView` component. While it works on Android, it also has the same issues with jumpy behavior on vertical animations. In addition, the `SafeAreaView` component and `useSafeAreaInsets` hook can update at different times, resulting in flickering when using them together. So we recommend always using the `useSafeAreaInsets` hook instead and avoid using the `SafeAreaView` component for consistent behavior.
+
+:::
+
+The rest of this guide gives more information on how to support safe areas in React Navigation.
+
+## Hidden/Custom Header or Tab Bar
+
+
+
+React Navigation handles safe area in the default header. However, if you're using a custom header, it's important to ensure your UI is within the safe area.
+
+For example, if I render nothing for the `header` or `tabBar`, nothing renders
+
+
+
+
+```js name="Hidden components" snack
+import * as React from 'react';
+import { Text, View } from 'react-native';
+import { createStaticNavigation } from '@react-navigation/native';
+import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+
+function Demo() {
+ return (
+
+ This is top text.
+ This is bottom text.
+
+ );
+}
+
+// codeblock-focus-start
+const MyTabs = createBottomTabNavigator({
+ initialRouteName: 'Analytics',
+ // highlight-start
+ tabBar: () => null,
+ screenOptions: {
+ headerShown: false,
+ },
+ // highlight-end
+ screens: {
+ Analytics: Demo,
+ Profile: Demo,
+ },
+});
+
+const RootStack = createNativeStackNavigator({
+ initialRouteName: 'Home',
+ // highlight-start
+ screenOptions: {
+ headerShown: false,
+ },
+ // highlight-end
+ screens: {
+ Home: MyTabs,
+ Settings: Demo,
+ },
+});
+
+// codeblock-focus-end
+
+const Navigation = createStaticNavigation(RootStack);
+
+export default function App() {
+ return ;
+}
+```
+
+
+
+
+```js name="Hidden components" snack
+import * as React from 'react';
+import { Text, View } from 'react-native';
+import { NavigationContainer } from '@react-navigation/native';
+import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+
+function Demo() {
+ return (
+
+ This is top text.
+ This is bottom text.
+
+ );
+}
+const Stack = createNativeStackNavigator();
+const Tab = createBottomTabNavigator();
+
+export default function App() {
+ return (
+
+
+
+ {() => (
+ null}
+ screenOptions={{ headerShown: false }}
+ >
+
+
+
+ )}
+
+
+
+
+
+ );
+}
+```
+
+
+
+
+
+
+To fix this issue you can apply safe area insets on your content. This can be achieved using the `useSafeAreaInsets` hook from the `react-native-safe-area-context` library:
+
+
+
+
+```js name="Safe area example" snack
+import * as React from 'react';
+import { Text, View } from 'react-native';
+import { createStaticNavigation } from '@react-navigation/native';
+import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+import {
+ SafeAreaProvider,
+ useSafeAreaInsets,
+} from 'react-native-safe-area-context';
+
+// codeblock-focus-start
+function Demo() {
+ const insets = useSafeAreaInsets();
+
+ return (
+
+ This is top text.
+ This is bottom text.
+
+ );
+}
+// codeblock-focus-end
+
+const MyTabs = createBottomTabNavigator({
+ initialRouteName: 'Analytics',
+ tabBar: () => null,
+ screenOptions: {
+ headerShown: false,
+ },
+ screens: {
+ Analytics: Demo,
+ Profile: Demo,
+ },
+});
+
+const RootStack = createNativeStackNavigator({
+ initialRouteName: 'Home',
+ screenOptions: {
+ headerShown: false,
+ },
+ screens: {
+ Home: MyTabs,
+ Settings: Demo,
+ },
+});
+
+// codeblock-focus-start
+
+const Navigation = createStaticNavigation(RootStack);
+
+export default function App() {
+ return (
+
+
+
+ );
+}
+// codeblock-focus-end
+```
+
+
+
+
+```js name="Safe area example" snack
+import * as React from 'react';
+import { View, Text } from 'react-native';
+import { NavigationContainer } from '@react-navigation/native';
+import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+import {
+ SafeAreaProvider,
+ useSafeAreaInsets,
+} from 'react-native-safe-area-context';
+
+// codeblock-focus-start
+function Demo() {
+ const insets = useSafeAreaInsets();
+
+ return (
+
+ This is top text.
+ This is bottom text.
+
+ );
+}
+// codeblock-focus-end
+
+const Stack = createNativeStackNavigator();
+const Tab = createBottomTabNavigator();
+
+// codeblock-focus-start
+export default function App() {
+ return (
+
+
+ {/*(...) */}
+ // codeblock-focus-end
+
+
+ {() => (
+ null}
+ screenOptions={{ headerShown: false }}
+ >
+
+
+
+ )}
+
+
+
+ // codeblock-focus-start
+
+
+ );
+}
+// codeblock-focus-end
+```
+
+
+
+
+Make sure to wrap your app in `SafeAreaProvider` as per the instructions [here](https://github.com/th3rdwave/react-native-safe-area-context#usage).
+
+
+
+This will detect if the app is running on a device with notches, if so, ensure the content isn't hidden behind any hardware elements.
+
+## Landscape Mode
+
+Even if you're using the default navigation bar and tab bar - if your application works in landscape mode it's important to ensure your content isn't hidden behind the sensor cluster.
+
+
+
+To fix this you can, once again, apply safe area insets to your content. This will not conflict with the navigation bar nor the tab bar's default behavior in portrait mode.
+
+
+
+## Use the hook for more control
+
+In some cases you might need more control over which paddings are applied. For example, you can only apply the top and the bottom padding by changing the `style` object:
+
+
+
+
+```js name="useSafeAreaInsets hook" snack
+import * as React from 'react';
+import { Text, View } from 'react-native';
+import { createStaticNavigation } from '@react-navigation/native';
+import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+// codeblock-focus-start
+import {
+ SafeAreaProvider,
+ useSafeAreaInsets,
+} from 'react-native-safe-area-context';
+
+function Demo() {
+ const insets = useSafeAreaInsets();
+ return (
+
+ This is top text.
+ This is bottom text.
+
+ );
+}
+// codeblock-focus-end
+
+const MyTabs = createBottomTabNavigator({
+ initialRouteName: 'Analytics',
+ tabBar: () => null,
+ screenOptions: {
+ headerShown: false,
+ },
+ screens: {
+ Analytics: Demo,
+ Profile: Demo,
+ },
+});
+
+const RootStack = createNativeStackNavigator({
+ initialRouteName: 'Home',
+ screenOptions: {
+ headerShown: false,
+ },
+ screens: {
+ Home: MyTabs,
+ Settings: Demo,
+ },
+});
+
+// codeblock-focus-start
+
+const Navigation = createStaticNavigation(RootStack);
+
+export default function App() {
+ return (
+
+
+
+ );
+}
+// codeblock-focus-end
+```
+
+
+
+
+```js name="useSafeAreaInsets hook" snack
+import * as React from 'react';
+import { Text, View } from 'react-native';
+import { NavigationContainer } from '@react-navigation/native';
+import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
+import { createStackNavigator } from '@react-navigation/stack';
+// codeblock-focus-start
+import {
+ SafeAreaProvider,
+ useSafeAreaInsets,
+} from 'react-native-safe-area-context';
+
+function Demo() {
+ const insets = useSafeAreaInsets();
+ return (
+
+ This is top text.
+ This is bottom text.
+
+ );
+}
+// codeblock-focus-end
+
+const Stack = createStackNavigator();
+const Tab = createBottomTabNavigator();
+
+export default function App() {
+ return (
+
+
+
+
+ {() => (
+ null}
+ screenOptions={{ headerShown: false }}
+ >
+
+
+
+ )}
+
+
+
+
+
+
+ );
+}
+```
+
+
+
+
+Similarly, you could apply these paddings in `contentContainerStyle` of `FlatList` to have the content avoid the safe areas, but still show them under the statusbar and navigation bar when scrolling.
+
+## Summary
+
+- Use [`useSafeAreaInsets`](https://appandflow.github.io/react-native-safe-area-context/api/use-safe-area-insets) hook from `react-native-safe-area-context` instead of [`SafeAreaView`](https://reactnative.dev/docs/safeareaview) component
+- Don't wrap your whole app in `SafeAreaView`, instead apply the styles to content inside your screens
+- Apply only specific insets using the `useSafeAreaInsets` hook for more control
diff --git a/versioned_docs/version-8.x/header-buttons.md b/versioned_docs/version-8.x/header-buttons.md
new file mode 100755
index 00000000000..e9c86d7a76c
--- /dev/null
+++ b/versioned_docs/version-8.x/header-buttons.md
@@ -0,0 +1,189 @@
+---
+id: header-buttons
+title: Header buttons
+sidebar_label: Header buttons
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+Now that we know how to customize the look of our headers, let's make them sentient! Actually perhaps that's ambitious, let's just make them able to respond to our touches in very well-defined ways.
+
+## Adding a button to the header
+
+The most common way to interact with a header is by tapping on a button either to the left or the right of the title. Let's add a button to the right side of the header (one of the most difficult places to touch on your entire screen, depending on finger and phone size, but also a normal place to put buttons).
+
+```js name="Header button" snack static2dynamic
+import * as React from 'react';
+import { Text, View } from 'react-native';
+import { createStaticNavigation } from '@react-navigation/native';
+import {
+ createNativeStackNavigator,
+ createNativeStackScreen,
+} from '@react-navigation/native-stack';
+import { Button } from '@react-navigation/elements';
+
+function HomeScreen() {
+ return (
+
+ Home Screen
+
+ );
+}
+
+// codeblock-focus-start
+const MyStack = createNativeStackNavigator({
+ screens: {
+ Home: createNativeStackScreen({
+ screen: HomeScreen,
+ options: {
+ // highlight-start
+ headerRight: () => (
+
+ ),
+ // highlight-end
+ },
+ }),
+ },
+});
+// codeblock-focus-end
+
+const Navigation = createStaticNavigation(MyStack);
+
+export default function App() {
+ return ;
+}
+```
+
+
+
+When we define our button this way, the `this` variable in `options` is _not_ the `HomeScreen` instance, so you can't call `setState` or any instance methods on it. This is pretty important because it's common to want the buttons in your header to interact with the screen that the header belongs to. So, we will look how to do this next.
+
+:::tip
+
+Note that a community-developed library for rendering buttons in the header with the correct styling is available: [react-navigation-header-buttons](https://github.com/vonovak/react-navigation-header-buttons).
+
+:::
+
+## Header interaction with its screen component
+
+In some cases, components in the header need to interact with the screen component. For this use case, we need to use `navigation.setOptions` to update our options. By using `navigation.setOptions` inside the screen component, we get access to screen's props, state, context etc.
+
+```js name="Header button" snack static2dynamic
+import * as React from 'react';
+import { Text, View } from 'react-native';
+import {
+ createStaticNavigation,
+ useNavigation,
+} from '@react-navigation/native';
+import {
+ createNativeStackNavigator,
+ createNativeStackScreen,
+} from '@react-navigation/native-stack';
+import { Button } from '@react-navigation/elements';
+
+// codeblock-focus-start
+function HomeScreen() {
+ const navigation = useNavigation();
+ const [count, setCount] = React.useState(0);
+
+ React.useEffect(() => {
+ // Use `setOptions` to update the button that we previously specified
+ // Now the button includes an `onPress` handler to update the count
+ // highlight-start
+ navigation.setOptions({
+ headerRight: () => (
+
+ ),
+ });
+ // highlight-end
+ }, [navigation]);
+
+ return Count: {count};
+}
+
+const MyStack = createNativeStackNavigator({
+ screens: {
+ Home: createNativeStackScreen({
+ screen: HomeScreen,
+ options: {
+ // Add a placeholder button without the `onPress` to avoid flicker
+ // highlight-next-line
+ headerRight: () => ,
+ },
+ }),
+ },
+});
+// codeblock-focus-end
+
+const Navigation = createStaticNavigation(MyStack);
+
+export default function App() {
+ return ;
+}
+```
+
+
+
+Here we update the `headerRight` with a button with `onPress` handler that has access to the component's state and can update it.
+
+## Customizing the back button
+
+`createNativeStackNavigator` provides the platform-specific defaults for the back button. On iOS this includes a label next to the button, which shows the title of the previous screen when the title fits in the available space, otherwise it says "Back".
+
+You can change the label behavior with `headerBackTitle` and style it with `headerBackTitleStyle` ([read more](native-stack-navigator.md#headerbacktitle)).
+
+To customize the back button icon, you can use [`headerBackIcon`](native-stack-navigator.md#headerbackicon).
+
+
+
+
+```js
+const MyStack = createNativeStackNavigator({
+ screens: {
+ Home: {
+ screen: HomeScreen,
+ options: {
+ headerBackTitle: 'Custom Back',
+ headerBackTitleStyle: { fontSize: 30 },
+ },
+ },
+ },
+});
+```
+
+
+
+
+```js
+
+
+
+
+```
+
+
+
+
+
+
+## Overriding the back button
+
+The back button will be rendered automatically in a stack navigator whenever it is possible for the user to go back from their current screen — in other words, the back button will be rendered whenever there is more than one screen in the stack.
+
+Generally, this is what you want. But it's possible that in some circumstances that you want to customize the back button more than you can through the options mentioned above, in which case you can set the `headerLeft` option to a React Element that will be rendered, just as we did with `headerRight`. Alternatively, the `headerLeft` option also accepts a React Component, which can be used, for example, for overriding the onPress behavior of the back button. Read more about this in the [api reference](native-stack-navigator.md#headerleft).
+
+## Summary
+
+- You can set buttons in the header through the [`headerLeft`](elements.md#headerleft) and [`headerRight`](elements.md#headerright) properties in [`options`](screen-options.md).
+- The back button is fully customizable with `headerLeft`, but if you only want to change the title or image, there are other `options` for that — [`headerBackTitle`](native-stack-navigator.md#headerbacktitle), [`headerBackTitleStyle`](native-stack-navigator.md#headerbacktitlestyle), and [`headerBackIcon`](native-stack-navigator.md#headerbackicon).
+- You can use a callback for the options prop to access [`navigation`](navigation-object.md) and [`route`](route-object.md) objects.
diff --git a/versioned_docs/version-8.x/headers.md b/versioned_docs/version-8.x/headers.md
new file mode 100755
index 00000000000..fa03cf288c2
--- /dev/null
+++ b/versioned_docs/version-8.x/headers.md
@@ -0,0 +1,374 @@
+---
+id: headers
+title: Configuring the header bar
+sidebar_label: Configuring the header bar
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+We've seen how to configure the header title already, but let's go over that again before moving on to some other options — repetition is key to learning!
+
+## Setting the header title
+
+Each screen has `options` which is either an object or a function that returns an object, that contains various configuration options. The one we use for the header title is `title`, as shown in the following example.
+
+```js name="Setting header title" snack static2dynamic
+import * as React from 'react';
+import { Text, View } from 'react-native';
+import { createStaticNavigation } from '@react-navigation/native';
+import {
+ createNativeStackNavigator,
+ createNativeStackScreen,
+} from '@react-navigation/native-stack';
+
+function HomeScreen() {
+ return (
+
+ Home Screen
+
+ );
+}
+
+// codeblock-focus-start
+const MyStack = createNativeStackNavigator({
+ screens: {
+ Home: createNativeStackScreen({
+ screen: HomeScreen,
+ // highlight-start
+ options: {
+ title: 'My home',
+ },
+ // highlight-end
+ }),
+ },
+});
+// codeblock-focus-end
+
+const Navigation = createStaticNavigation(MyStack);
+
+export default function App() {
+ return ;
+}
+```
+
+
+
+## Using params in the title
+
+In order to use params in the title, we need to make `options` for the screen a function that returns a configuration object. If we make `options` a function then React Navigation will call it with an object containing `{ navigation, route }` - in this case, all we care about is `route`, which is the same object that is passed to your screen props as `route` prop. You may recall that we can get the params through `route.params`, and so we do this below to extract a param and use it as a title.
+
+```js name="Using params in the title" snack static2dynamic
+import * as React from 'react';
+import { Text, View } from 'react-native';
+import {
+ createStaticNavigation,
+ useNavigation,
+} from '@react-navigation/native';
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+import { Button } from '@react-navigation/elements';
+
+function HomeScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+
+
+ );
+}
+
+function ProfileScreen() {
+ return (
+
+ Profile Screen
+
+ );
+}
+
+// codeblock-focus-start
+const MyStack = createNativeStackNavigator({
+ screens: {
+ Home: createNativeStackScreen({
+ screen: HomeScreen,
+ options: {
+ title: 'My home',
+ },
+ }),
+ Profile: createNativeStackScreen({
+ screen: ProfileScreen,
+ // highlight-start
+ options: ({ route }) => ({
+ title: route.params.name,
+ }),
+ // highlight-end
+ }),
+ },
+});
+// codeblock-focus-end
+
+const Navigation = createStaticNavigation(MyStack);
+
+export default function App() {
+ return ;
+}
+```
+
+The argument that is passed in to the `options` function is an object with the following properties:
+
+- `navigation` - The [navigation object](navigation-object.md) for the screen.
+- `route` - The [route object](route-object.md) for the screen
+
+We only needed the `route` object in the above example but you may in some cases want to use `navigation` as well.
+
+## Updating `options` with `setOptions`
+
+It's often necessary to update the `options` configuration for the active screen from the mounted screen component itself. We can do this using `navigation.setOptions`
+
+```js name="Updating options" snack static2dynamic
+import * as React from 'react';
+import { Text, View } from 'react-native';
+import {
+ createStaticNavigation,
+ useNavigation,
+} from '@react-navigation/native';
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+import { Button } from '@react-navigation/elements';
+
+function HomeScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ Home Screen
+ // codeblock-focus-start
+
+ // codeblock-focus-end
+
+ );
+}
+
+const MyStack = createNativeStackNavigator({
+ screens: {
+ Home: {
+ screen: HomeScreen,
+ options: {
+ title: 'My home',
+ },
+ },
+ },
+});
+
+const Navigation = createStaticNavigation(MyStack);
+
+export default function App() {
+ return ;
+}
+```
+
+## Adjusting header styles
+
+There are three key properties to use when customizing the style of your header: `headerStyle`, `headerTintColor`, and `headerTitleStyle`.
+
+- `headerStyle`: a style object that will be applied to the view that wraps the header. If you set `backgroundColor` on it, that will be the color of your header.
+- `headerTintColor`: the back button and title both use this property as their color. In the example below, we set the tint color to white (`#fff`) so the back button and the header title would be white.
+- `headerTitleStyle`: if we want to customize the `fontFamily`, `fontWeight` and other `Text` style properties for the title, we can use this to do it.
+
+```js name="Header styles" snack static2dynamic
+import * as React from 'react';
+import { Text, View } from 'react-native';
+import { createStaticNavigation } from '@react-navigation/native';
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+
+function HomeScreen() {
+ return (
+
+ Home Screen
+
+ );
+}
+
+// codeblock-focus-start
+const MyStack = createNativeStackNavigator({
+ screens: {
+ Home: createNativeStackScreen({
+ screen: HomeScreen,
+ options: {
+ title: 'My home',
+ // highlight-start
+ headerStyle: {
+ backgroundColor: '#f4511e',
+ },
+ headerTintColor: '#fff',
+ headerTitleStyle: {
+ fontWeight: 'bold',
+ },
+ // highlight-end
+ },
+ }),
+ },
+});
+// codeblock-focus-end
+
+const Navigation = createStaticNavigation(MyStack);
+
+export default function App() {
+ return ;
+}
+```
+
+
+
+There are a couple of things to notice here:
+
+1. On iOS, the status bar text and icons are black, and this doesn't look great over a dark-colored background. We won't discuss it here, but you should be sure to configure the status bar to fit with your screen colors [as described in the status bar guide](status-bar.md).
+2. The configuration we set only applies to the home screen; when we navigate to the details screen, the default styles are back. We'll look at how to share `options` between screens now.
+
+## Sharing common `options` across screens
+
+It is common to want to configure the header in a similar way across many screens. For example, your company brand color might be red and so you want the header background color to be red and the tint color to be white. Conveniently, these are the colors we're using in our running example, and you'll notice that when you navigate to the `DetailsScreen` the colors go back to the defaults. Wouldn't it be awful if we had to copy the `options` header style properties from `Home` to `Details`, and for every single screen we use in our app? Thankfully, we do not. We can instead move the configuration up to the native stack navigator under `screenOptions`:
+
+```js name="Common screen options" snack static2dynamic
+import * as React from 'react';
+import { Text, View } from 'react-native';
+import {
+ createStaticNavigation,
+ useNavigation,
+} from '@react-navigation/native';
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+import { Button } from '@react-navigation/elements';
+
+function HomeScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ Home Screen
+
+
+ );
+}
+
+function DetailsScreen() {
+ return (
+
+ Details Screen
+
+ );
+}
+
+// codeblock-focus-start
+const MyStack = createNativeStackNavigator({
+ // highlight-start
+ screenOptions: {
+ headerStyle: {
+ backgroundColor: '#f4511e',
+ },
+ headerTintColor: '#fff',
+ headerTitleStyle: {
+ fontWeight: 'bold',
+ },
+ },
+ // highlight-end
+ screens: {
+ Home: createNativeStackScreen({
+ screen: HomeScreen,
+ }),
+ Details: createNativeStackScreen({
+ screen: DetailsScreen,
+ }),
+ },
+});
+// codeblock-focus-end
+
+const Navigation = createStaticNavigation(MyStack);
+
+export default function App() {
+ return ;
+}
+```
+
+Now, any screen that belongs to this navigator will have our wonderful branded styles. Surely though, there must be a way to override these options if we need to?
+
+## Replacing the title with a custom component
+
+Sometimes you need more control than just changing the text and styles of your title -- for example, you may want to render an image in place of the title, or make the title into a button. In these cases you can completely override the component used for the title and provide your own.
+
+```js name="Custom title" snack static2dynamic
+import * as React from 'react';
+import { Text, View, Image } from 'react-native';
+import { createStaticNavigation } from '@react-navigation/native';
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+
+function HomeScreen() {
+ return (
+
+ Home Screen
+
+ );
+}
+
+// codeblock-focus-start
+function LogoTitle() {
+ return (
+
+ );
+}
+
+const MyStack = createNativeStackNavigator({
+ screens: {
+ Home: {
+ screen: HomeScreen,
+ options: {
+ // highlight-next-line
+ headerTitle: (props) => ,
+ },
+ },
+ },
+});
+// codeblock-focus-end
+
+const Navigation = createStaticNavigation(MyStack);
+
+export default function App() {
+ return ;
+}
+```
+
+
+
+:::note
+
+You might be wondering, why `headerTitle` when we provide a component and not `title`, like before? The reason is that `headerTitle` is a property that is specific to headers, whereas `title` will be used for tab bars, drawers etc. as well. The `headerTitle` defaults to a `Text` component that displays the `title`.
+
+:::
+
+## Additional configuration
+
+You can read the full list of available `options` for screens inside of a native stack navigator in the [`createNativeStackNavigator` reference](native-stack-navigator.md#options).
+
+## Summary
+
+- You can customize the header inside of the [`options`](screen-options.md) property of your screens. Read the full list of options [in the API reference](native-stack-navigator.md#options).
+- The `options` property can be an object or a function. When it is a function, it is provided with an object with the [`navigation`](navigation-object.md) and [`route`](route-object.md) objects.
+- You can also specify shared [`screenOptions`](screen-options.md#screenoptions-prop-on-the-navigator) in the stack navigator configuration when you initialize it. This will apply to all screens in the navigator.
diff --git a/versioned_docs/version-8.x/hello-react-navigation.md b/versioned_docs/version-8.x/hello-react-navigation.md
new file mode 100755
index 00000000000..b9f20bd025a
--- /dev/null
+++ b/versioned_docs/version-8.x/hello-react-navigation.md
@@ -0,0 +1,327 @@
+---
+id: hello-react-navigation
+title: Hello React Navigation
+sidebar_label: Hello React Navigation
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+In a web browser, you can link to different pages using an anchor (``) tag. When the user clicks on a link, the URL is pushed to the browser history stack. When the user presses the back button, the browser pops the item from the top of the history stack, so the active page is now the previously visited page. React Native doesn't have a built-in idea of a global history stack like a web browser does -- this is where React Navigation enters the story.
+
+React Navigation's native stack navigator provides a way for your app to transition between screens and manage navigation history. If your app uses only one stack navigator then it is conceptually similar to how a web browser handles navigation state - your app pushes and pops items from the navigation stack as users interact with it, and this results in the user seeing different screens. A key difference between how this works in a web browser and in React Navigation is that React Navigation's native stack navigator provides the gestures and animations that you would expect on Android and iOS when navigating between routes in the stack.
+
+Let's start by demonstrating the most common navigator, `createNativeStackNavigator`.
+
+## Installing the native stack navigator library
+
+The libraries we've installed so far are the building blocks and shared foundations for navigators, and each navigator in React Navigation lives in its own library. To use the native stack navigator, we need to install [`@react-navigation/native-stack`](https://github.com/react-navigation/react-navigation/tree/main/packages/native-stack) :
+
+```bash npm2yarn
+npm install @react-navigation/native-stack
+```
+
+:::info
+
+`@react-navigation/native-stack` depends on `react-native-screens` and the other libraries that we installed in [Getting started](getting-started.md). If you haven't installed those yet, head over to that page and follow the installation instructions.
+
+:::
+
+## Installing the elements library
+
+The [`@react-navigation/elements`](elements.md) library provides a set of components that are designed to work well with React Navigation. We'll use a few of these components such as `Button` in this guide. So let's install it first:
+
+```bash npm2yarn
+npm install @react-navigation/elements
+```
+
+## Creating a native stack navigator
+
+`createNativeStackNavigator` is a function that takes a configuration object for the navigator including the list of screens to include.
+
+`createNativeStackScreen` is a function that takes a configuration object for a screen, where the `screen` property is the component to render for that screen. A screen is a React Component that renders the content displayed by the navigator, or a nested navigator which we'll learn about later.
+
+`createStaticNavigation` is a function that takes the navigator defined earlier and returns a component that can be rendered in the app. It's only called once in the app. Usually, we'd render the returned component at the root of our app, which is usually the component exported from `App.js`, `App.tsx` etc., or used with `AppRegistry.registerComponent`, `Expo.registerRootComponent` etc.
+
+```js name="Native Stack Example" snack static2dynamic
+import * as React from 'react';
+import { View, Text } from 'react-native';
+import { createStaticNavigation } from '@react-navigation/native';
+import {
+ createNativeStackNavigator,
+ createNativeStackScreen,
+} from '@react-navigation/native-stack';
+
+function HomeScreen() {
+ return (
+
+ Home Screen
+
+ );
+}
+
+const RootStack = createNativeStackNavigator({
+ screens: {
+ Home: createNativeStackScreen({
+ screen: HomeScreen,
+ }),
+ },
+});
+
+const Navigation = createStaticNavigation(RootStack);
+
+export default function App() {
+ return ;
+}
+```
+
+:::warning
+
+In a typical React Native app, the `Navigation` or `NavigationContainer` component should be only rendered once in your app at the root.
+
+:::
+
+
+
+If you run this code, you will see a screen with an empty navigation bar and a grey content area containing your `HomeScreen` component (shown above). The styles you see for the navigation bar and the content area are the default configuration for a stack navigator, we'll learn how to configure those later.
+
+:::tip
+
+The casing of the route name doesn't matter -- you can use lowercase `home` or capitalized `Home`, it's up to you. We prefer capitalizing our route names.
+
+:::
+
+## Configuring the navigator
+
+All of the route configuration is specified as props to our navigator. We haven't passed any props to our navigator, so it just uses the default configuration.
+
+Let's add a second screen to our native stack navigator and configure the `Home` screen to render first:
+
+```js name="Native Stack Example" snack static2dynamic
+import * as React from 'react';
+import { View, Text } from 'react-native';
+import { createStaticNavigation } from '@react-navigation/native';
+import {
+ createNativeStackNavigator,
+ createNativeStackScreen,
+} from '@react-navigation/native-stack';
+
+function HomeScreen() {
+ return (
+
+ Home Screen
+
+ );
+}
+
+function DetailsScreen() {
+ return (
+
+ Details Screen
+
+ );
+}
+
+// codeblock-focus-start
+const RootStack = createNativeStackNavigator({
+ // highlight-next-line
+ initialRouteName: 'Home',
+ screens: {
+ Home: createNativeStackScreen({
+ screen: HomeScreen,
+ }),
+ Details: createNativeStackScreen({
+ screen: DetailsScreen,
+ }),
+ },
+});
+// codeblock-focus-end
+
+const Navigation = createStaticNavigation(RootStack);
+
+export default function App() {
+ return ;
+}
+```
+
+Now our stack has two _routes_, a `Home` route and a `Details` route. A route can be specified by under the `screens` property. The name of the property under `screens` corresponds to the name of the route we will use to navigate, and the value corresponds to the component it'll render.
+
+Here, the `Home` route corresponds to the `HomeScreen` component, and the `Details` route corresponds to the `DetailsScreen` component. The initial route for the stack is the `Home` route. Try changing it to `Details` and reload the app (React Native's Fast Refresh won't update changes from `initialRouteName`, as you might expect), notice that you will now see the `Details` screen. Then change it back to `Home` and reload once more.
+
+## Specifying options
+
+Each screen in the navigator can specify some options for the navigator, such as the title to render in the header.
+
+To specify the options, we'll add an `options` property to the screen configuration:
+
+```js name="Options for Screen" snack static2dynamic
+import * as React from 'react';
+import { View, Text } from 'react-native';
+import { createStaticNavigation } from '@react-navigation/native';
+import {
+ createNativeStackNavigator,
+ createNativeStackScreen,
+} from '@react-navigation/native-stack';
+
+function HomeScreen() {
+ return (
+
+ Home Screen
+
+ );
+}
+
+function DetailsScreen() {
+ return (
+
+ Details Screen
+
+ );
+}
+
+// codeblock-focus-start
+const RootStack = createNativeStackNavigator({
+ initialRouteName: 'Home',
+ screens: {
+ Home: createNativeStackScreen({
+ screen: HomeScreen,
+ // highlight-start
+ options: {
+ title: 'Overview',
+ },
+ // highlight-end
+ }),
+ Details: createNativeStackScreen({
+ screen: DetailsScreen,
+ }),
+ },
+});
+// codeblock-focus-end
+
+const Navigation = createStaticNavigation(RootStack);
+
+export default function App() {
+ return ;
+}
+```
+
+Sometimes we will want to specify the same options for all of the screens in the navigator. For that, we can add a `screenOptions` property to the configuration:
+
+```js name="Common options for Screens" snack static2dynamic
+import * as React from 'react';
+import { View, Text } from 'react-native';
+import { createStaticNavigation } from '@react-navigation/native';
+import {
+ createNativeStackNavigator,
+ createNativeStackScreen,
+} from '@react-navigation/native-stack';
+
+function HomeScreen() {
+ return (
+
+ Home Screen
+
+ );
+}
+
+function DetailsScreen() {
+ return (
+
+ Details Screen
+
+ );
+}
+
+// codeblock-focus-start
+const RootStack = createNativeStackNavigator({
+ initialRouteName: 'Home',
+ // highlight-start
+ screenOptions: {
+ headerStyle: { backgroundColor: 'tomato' },
+ },
+ // highlight-end
+ screens: {
+ Home: createNativeStackScreen({
+ screen: HomeScreen,
+ options: {
+ title: 'Overview',
+ },
+ }),
+ Details: createNativeStackScreen({
+ screen: DetailsScreen,
+ }),
+ },
+});
+// codeblock-focus-end
+
+const Navigation = createStaticNavigation(RootStack);
+
+export default function App() {
+ return ;
+}
+```
+
+## Passing additional props
+
+
+
+
+Passing additional props to a screen is not supported in the static API.
+
+
+
+
+Sometimes we might want to pass additional props to a screen. We can do that with 2 approaches:
+
+1. Use [React context](https://react.dev/reference/react/useContext) and wrap the navigator with a context provider to pass data to the screens (recommended).
+2. Use a render callback for the screen instead of specifying a `component` prop:
+
+ ```js
+
+ // highlight-next-line
+ {(props) => }
+
+ ```
+
+:::warning
+
+By default, React Navigation applies optimizations to screen components to prevent unnecessary renders. Using a render callback removes those optimizations. So if you use a render callback, you'll need to ensure that you use [`React.memo`](https://react.dev/reference/react/memo) or [`React.PureComponent`](https://react.dev/reference/react/PureComponent) for your screen components to avoid performance issues.
+
+:::
+
+
+
+
+## What's next?
+
+The natural question at this point is: "how do I go from the `Home` route to the `Details` route?". That is covered in the [next section](navigating.md).
+
+
+Using with TypeScript
+
+If you are using TypeScript, you will need to specify the types accordingly. You can check [Type checking with TypeScript](typescript.md) after going through the fundamentals for more details. For now, we won't be covering TypeScript in the examples.
+
+
+
+## Summary
+
+
+
+
+- React Native doesn't have a built-in API for navigation like a web browser does. React Navigation provides this for you, along with the iOS and Android gestures and animations to transition between screens.
+- [`createNativeStackNavigator`](native-stack-navigator.md) is a function that takes the screens configuration and renders our content.
+- Each property under screens refers to the name of the route, and the value is the component to render for the route.
+- To specify what the initial route in a stack is, provide an [`initialRouteName`](navigator.md#initial-route-name) option for the navigator.
+- To specify screen-specific options, we can specify an [`options`](screen-options.md#options-prop-on-screen) property, and for common options, we can specify [`screenOptions`](screen-options.md#screenoptions-prop-on-the-navigator).
+
+
+
+
+- React Native doesn't have a built-in API for navigation like a web browser does. React Navigation provides this for you, along with the iOS and Android gestures and animations to transition between screens.
+- [`Stack.Navigator`](native-stack-navigator.md) is a component that takes route configuration as its children with additional props for configuration and renders our content.
+- Each [`Stack.Screen`](screen.md) component takes a [`name`](screen.md#name) prop which refers to the name of the route and [`component`](screen.md#component) prop which specifies the component to render for the route. These are the 2 required props.
+- To specify what the initial route in a stack is, provide an [`initialRouteName`](navigator.md#initial-route-name) as the prop for the navigator.
+- To specify screen-specific options, we can pass an [`options`](screen-options.md#options-prop-on-screen) prop to `Stack.Screen`, and for common options, we can pass [`screenOptions`](screen-options.md#screenoptions-prop-on-the-navigator) to `Stack.Navigator`.
+
+
+
diff --git a/versioned_docs/version-8.x/hiding-tabbar-in-screens.md b/versioned_docs/version-8.x/hiding-tabbar-in-screens.md
new file mode 100644
index 00000000000..345d36b62db
--- /dev/null
+++ b/versioned_docs/version-8.x/hiding-tabbar-in-screens.md
@@ -0,0 +1,197 @@
+---
+id: hiding-tabbar-in-screens
+title: Hiding tab bar in specific screens
+sidebar_label: Hiding tab bar in screens
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+Sometimes we may want to hide the tab bar in specific screens in a stack navigator nested in a tab navigator. Let's say we have 5 screens: `Home`, `Feed`, `Notifications`, `Profile` and `Settings`, and your navigation structure looks like this:
+
+
+
+
+```js name="Hiding tab bar in screens"
+const HomeStack = createNativeStackNavigator({
+ screens: {
+ Home: Home,
+ Profile: Profile,
+ Settings: Settings,
+ },
+});
+
+const MyTabs = createBottomTabNavigator({
+ screens: {
+ Home: HomeStack,
+ Feed: Feed,
+ Notifications: Notifications,
+ },
+});
+
+const Navigation = createStaticNavigation(MyTabs);
+
+export default function App() {
+ return ;
+}
+```
+
+
+
+
+```js
+function HomeStack() {
+ return (
+
+
+
+
+
+ );
+}
+
+function App() {
+ return (
+
+
+
+
+
+
+
+ );
+}
+```
+
+
+
+
+With this structure, when we navigate to the `Profile` or `Settings` screen, the tab bar will still stay visible over those screens.
+
+But if we want to show the tab bar only on the `Home`, `Feed` and `Notifications` screens, but not on the `Profile` and `Settings` screens, we'll need to change the navigation structure. The easiest way to achieve this is to nest the tab navigator inside the first screen of the stack instead of nesting stack inside tab navigator:
+
+
+
+
+```js name="Hiding tabbar" snack
+import * as React from 'react';
+import { Text, View } from 'react-native';
+import {
+ createStaticNavigation,
+ useNavigation,
+} from '@react-navigation/native';
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
+import { Button } from '@react-navigation/elements';
+
+function EmptyScreen() {
+ return ;
+}
+
+function Home() {
+ const navigation = useNavigation();
+
+ return (
+
+ Home Screen
+
+
+
+ );
+}
+
+// codeblock-focus-start
+const HomeTabs = createBottomTabNavigator({
+ screens: {
+ Home: Home,
+ Feed: EmptyScreen,
+ Notifications: EmptyScreen,
+ },
+});
+
+const RootStack = createNativeStackNavigator({
+ screens: {
+ Home: HomeTabs,
+ Profile: EmptyScreen,
+ Settings: EmptyScreen,
+ },
+});
+
+// codeblock-focus-end
+
+const Navigation = createStaticNavigation(RootStack);
+
+export default function App() {
+ return ;
+}
+```
+
+
+
+
+```js name="Hiding tabbar" snack
+import * as React from 'react';
+import { Text, View } from 'react-native';
+import { NavigationContainer, useNavigation } from '@react-navigation/native';
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
+import { Button } from '@react-navigation/elements';
+
+const Tab = createBottomTabNavigator();
+const Stack = createNativeStackNavigator();
+
+function EmptyScreen() {
+ return ;
+}
+
+function Home() {
+ const navigation = useNavigation();
+
+ return (
+
+ Home Screen
+
+
+
+ );
+}
+
+// codeblock-focus-start
+function HomeTabs() {
+ return (
+
+
+
+
+
+ );
+}
+
+function App() {
+ return (
+
+
+
+
+
+
+
+ );
+}
+// codeblock-focus-end
+
+export default App;
+```
+
+
+
+
+After re-organizing the navigation structure, now if we navigate to the `Profile` or `Settings` screens, the tab bar won't be visible over the screen anymore.
diff --git a/versioned_docs/version-8.x/limitations.md b/versioned_docs/version-8.x/limitations.md
new file mode 100755
index 00000000000..0958770e95f
--- /dev/null
+++ b/versioned_docs/version-8.x/limitations.md
@@ -0,0 +1,17 @@
+---
+id: limitations
+title: Limitations
+sidebar_label: Limitations
+---
+
+As a potential user of the library, it's important to know what you can and cannot do with it. Armed with this knowledge, you may choose to adopt a different library such as [`react-native-navigation`](https://github.com/wix/react-native-navigation) instead. We discuss the high level design decisions in the [pitch & anti-pitch](pitch.md) section, and here we will cover some of the use cases that are either not supported or are so difficult to do that they may as well be impossible. If any of the following limitations are dealbreakers for your app, React Navigation might not be for you.
+
+## Limited right-to-left (RTL) layout support
+
+We try to handle RTL layouts properly in React Navigation, however the team working on React Navigation is fairly small and we do not have the bandwidth or processes at the moment to test all changes against RTL layouts. So you might encounter issues with RTL layouts.
+
+If you like what React Navigation has to offer but are turned off by this constraint, we encourage you to get involved and take ownership of RTL layout support. Please reach out to us on Twitter: [@reactnavigation](https://twitter.com/reactnavigation).
+
+## Some platform-specific behavior
+
+React Navigation does not include support for the peek & pop feature available on devices with 3D touch.
diff --git a/versioned_docs/version-8.x/link.md b/versioned_docs/version-8.x/link.md
new file mode 100644
index 00000000000..93dbaeb0df1
--- /dev/null
+++ b/versioned_docs/version-8.x/link.md
@@ -0,0 +1,29 @@
+---
+id: link
+title: Link
+sidebar_label: Link
+---
+
+The `Link` component renders a component that can navigate to a screen on press. This renders a `` tag when used on the Web and uses a `Text` component on other platforms. It preserves the default behavior of anchor tags in the browser such as `Right click -> Open link in new tab"`, `Ctrl+Click`/`⌘+Click` etc. to provide a native experience.
+
+The path in the `href` for the `` tag is generated based on your [`linking` options](navigation-container.md#linking).
+
+Example:
+
+```js
+import { Link } from '@react-navigation/native';
+
+// ...
+
+function Home() {
+ return (
+
+ Go to Jane's profile
+
+ );
+}
+```
+
+If you want to use your own custom link component, you can use [`useLinkProps`](use-link-props.md) instead.
+
+The `Link` component accepts the [same props as `useLinkProps`](use-link-props.md#options)
diff --git a/versioned_docs/version-8.x/material-top-tab-navigator.md b/versioned_docs/version-8.x/material-top-tab-navigator.md
new file mode 100755
index 00000000000..2942f68776f
--- /dev/null
+++ b/versioned_docs/version-8.x/material-top-tab-navigator.md
@@ -0,0 +1,650 @@
+---
+id: material-top-tab-navigator
+title: Material Top Tabs Navigator
+sidebar_label: Material Top Tabs
+---
+
+A material-design themed tab bar on the top of the screen that lets you switch between different routes by tapping the tabs or swiping horizontally. Transitions are animated by default. Screen components for each route are mounted immediately.
+
+
+
+This wraps [`react-native-tab-view`](tab-view.md). If you want to use the tab view without React Navigation integration, use the library directly instead.
+
+## Installation
+
+To use this navigator, ensure that you have [`@react-navigation/native` and its dependencies (follow this guide)](getting-started.md), then install [`@react-navigation/material-top-tabs`](https://github.com/react-navigation/react-navigation/tree/main/packages/material-top-tabs):
+
+```bash npm2yarn
+npm install @react-navigation/material-top-tabs
+```
+
+The navigator depends on [`react-native-pager-view`](https://github.com/callstack/react-native-pager-view) for rendering the pages.
+
+
+
+
+If you have a Expo managed project, in your project directory, run:
+
+```bash
+npx expo install react-native-pager-view
+```
+
+
+
+
+If you have a bare React Native project, in your project directory, run:
+
+```bash npm2yarn
+npm install react-native-pager-view
+```
+
+
+
+
+If you're on a Mac and developing for iOS, you also need to install [pods](https://cocoapods.org/) to complete the linking.
+
+```bash
+npx pod-install ios
+```
+
+## Usage
+
+To use this navigator, import it from `@react-navigation/material-top-tabs`:
+
+```js name="Material Top Tab Navigator" snack static2dynamic
+import * as React from 'react';
+import { Text, View } from 'react-native';
+import {
+ createStaticNavigation,
+ useNavigation,
+} from '@react-navigation/native';
+import { Button } from '@react-navigation/elements';
+// codeblock-focus-start
+import { createMaterialTopTabNavigator } from '@react-navigation/material-top-tabs';
+
+// codeblock-focus-end
+function HomeScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ Home Screen
+
+
+ );
+}
+
+function ProfileScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ Profile Screen
+
+
+ );
+}
+
+// codeblock-focus-start
+const MyTabs = createMaterialTopTabNavigator({
+ screens: {
+ Home: HomeScreen,
+ Profile: ProfileScreen,
+ },
+});
+// codeblock-focus-end
+
+const Navigation = createStaticNavigation(MyTabs);
+
+export default function App() {
+ return ;
+}
+```
+
+## API Definition
+
+### Props
+
+In addition to the [common props](navigator.md#configuration) shared by all navigators, the material top tabs navigator component accepts the following additional props:
+
+#### `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.
+
+It supports the following values:
+
+- `firstRoute` - return to the first screen defined in the navigator (default)
+- `initialRoute` - return to initial screen passed in `initialRouteName` prop, if not passed, defaults to the first screen
+- `order` - return to screen defined before the focused screen
+- `history` - return to last visited screen in the navigator; if the same screen is visited multiple times, the older entries are dropped from the history
+- `none` - do not handle back button
+
+#### `tabBarPosition`
+
+Position of the tab bar in the tab view. Possible values are `'top'` and `'bottom'`. Defaults to `'top'`.
+
+#### `keyboardDismissMode`
+
+String indicating whether the keyboard gets dismissed in response to a drag gesture. Possible values are:
+
+- `'auto'` (default): the keyboard is dismissed when the index changes.
+- `'on-drag'`: the keyboard is dismissed when a drag begins.
+- `'none'`: drags do not dismiss the keyboard.
+
+#### `initialLayout`
+
+Object containing the initial height and width of the screens. Passing this will improve the initial rendering performance. For most apps, this is a good default:
+
+```js
+{
+ width: Dimensions.get('window').width;
+}
+```
+
+#### `style`
+
+Style to apply to the tab view container.
+
+#### `tabBar`
+
+Function that returns a React element to display as the tab bar.
+
+Example:
+
+```js name="Custom Tab Bar" snack static2dynamic
+import * as React from 'react';
+import { Animated, View, Platform, Text } from 'react-native';
+import {
+ createStaticNavigation,
+ useLinkBuilder,
+ useTheme,
+} from '@react-navigation/native';
+import { PlatformPressable } from '@react-navigation/elements';
+import { createMaterialTopTabNavigator } from '@react-navigation/material-top-tabs';
+
+function HomeScreen() {
+ return (
+
+ Home Screen
+
+ );
+}
+
+function SettingsScreen() {
+ return (
+
+ Settings Screen
+
+ );
+}
+
+// codeblock-focus-start
+function MyTabBar({ state, descriptors, navigation, position }) {
+ const { colors } = useTheme();
+ const { buildHref } = useLinkBuilder();
+
+ return (
+
+ {state.routes.map((route, index) => {
+ const { options } = descriptors[route.key];
+ const label =
+ options.tabBarLabel !== undefined
+ ? options.tabBarLabel
+ : options.title !== undefined
+ ? options.title
+ : route.name;
+
+ const isFocused = state.index === index;
+
+ const onPress = () => {
+ const event = navigation.emit({
+ type: 'tabPress',
+ target: route.key,
+ canPreventDefault: true,
+ });
+
+ if (!isFocused && !event.defaultPrevented) {
+ navigation.navigate(route.name, route.params);
+ }
+ };
+
+ const onLongPress = () => {
+ navigation.emit({
+ type: 'tabLongPress',
+ target: route.key,
+ });
+ };
+
+ const inputRange = state.routes.map((_, i) => i);
+ const opacity = position.interpolate({
+ inputRange,
+ outputRange: inputRange.map((i) => (i === index ? 1 : 0.5)),
+ });
+
+ return (
+
+
+ {label}
+
+
+ );
+ })}
+
+ );
+}
+
+const MyTabs = createMaterialTopTabNavigator({
+ tabBar: (props) => ,
+ screens: {
+ Home: HomeScreen,
+ Settings: SettingsScreen,
+ },
+});
+// codeblock-focus-end
+
+const Navigation = createStaticNavigation(MyTabs);
+
+export default function App() {
+ return ;
+}
+```
+
+This example will render a basic tab bar with labels.
+
+Note that you **cannot** use the `useNavigation` hook inside the `tabBar` since `useNavigation` is only available inside screens. You get a `navigation` prop for your `tabBar` which you can use instead:
+
+```js
+function MyTabBar({ navigation }) {
+ return (
+
+ );
+}
+```
+
+### Options
+
+The following [options](screen-options.md) can be used to configure the screens in the navigator:
+
+Example:
+
+```js name="Tab Navigator Options" snack static2dynamic
+import * as React from 'react';
+import { Text, View } from 'react-native';
+import { createStaticNavigation } from '@react-navigation/native';
+import { createMaterialTopTabNavigator } from '@react-navigation/material-top-tabs';
+
+function HomeScreen() {
+ return (
+
+ Home Screen
+
+ );
+}
+
+function SettingsScreen() {
+ return (
+
+ Settings Screen
+
+ );
+}
+
+function ProfileScreen() {
+ return (
+
+ Profile Screen
+
+ );
+}
+
+// codeblock-focus-start
+const MyTabs = createMaterialTopTabNavigator({
+ // highlight-start
+ screenOptions: {
+ tabBarLabelStyle: { fontSize: 12 },
+ tabBarItemStyle: { width: 100 },
+ tabBarStyle: { backgroundColor: 'powderblue' },
+ },
+ // highlight-end
+ screens: {
+ Home: HomeScreen,
+ Settings: SettingsScreen,
+ Profile: ProfileScreen,
+ },
+});
+// codeblock-focus-end
+
+const Navigation = createStaticNavigation(MyTabs);
+
+export default function App() {
+ return ;
+}
+```
+
+#### `title`
+
+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`](#tabbarshowlabel) option.
+
+#### `tabBarAccessibilityLabel`
+
+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.
+
+#### `tabBarAllowFontScaling`
+
+Whether label font should scale to respect Text Size accessibility settings.
+
+#### `tabBarShowLabel`
+
+Whether the tab label should be visible. Defaults to `true`.
+
+#### `tabBarIcon`
+
+Function that given `{ focused: boolean, color: string }` returns a React.Node, to display in the tab bar.
+
+#### `tabBarShowIcon`
+
+Whether the tab icon should be visible. Defaults to `false`.
+
+#### `tabBarBadge`
+
+Function that returns a React element to use as a badge for the tab.
+
+#### `tabBarIndicator`
+
+Function that returns a React element as the tab bar indicator.
+
+#### `tabBarIndicatorStyle`
+
+Style object for the tab bar indicator.
+
+#### `tabBarIndicatorContainerStyle`
+
+Style object for the view containing the tab bar indicator.
+
+#### `tabBarButtonTestID`
+
+ID to locate this tab button in tests.
+
+#### `tabBarActiveTintColor`
+
+Color for the icon and label in the active tab.
+
+#### `tabBarInactiveTintColor`
+
+Color for the icon and label in the inactive tabs.
+
+#### `tabBarPressColor`
+
+Color for material ripple (Android >= 5.0 only).
+
+#### `tabBarPressOpacity`
+
+Opacity for pressed tab (iOS and Android < 5.0 only).
+
+#### `tabBarBounces`
+
+Boolean indicating whether the tab bar bounces when overscrolling.
+
+#### `tabBarScrollEnabled`
+
+Boolean indicating whether to make the tab bar scrollable.
+
+If you set this to `true`, you should also specify a width in `tabBarItemStyle` to improve the performance of initial render.
+
+#### `tabBarLabelStyle`
+
+Style object for the tab label.
+
+#### `tabBarItemStyle`
+
+Style object for the individual tab items.
+
+#### `tabBarContentContainerStyle`
+
+Style object for the view containing the tab items.
+
+#### `tabBarStyle`
+
+Style object for the tab bar.
+
+#### `swipeEnabled`
+
+Boolean indicating whether to enable swipe gestures. Swipe gestures are enabled by default. Passing `false` will disable swipe gestures, but the user can still switch tabs by pressing the tab bar.
+
+#### `lazy`
+
+Whether this screen should be lazily rendered. When this is set to `true`, the screen will be rendered as it comes into the viewport. By default all screens are rendered to provide a smoother swipe experience. But you might want to defer the rendering of screens out of the viewport until the user sees them. To enable lazy rendering for this screen, set `lazy` to `true`.
+
+When you enable `lazy`, the lazy loaded screens will usually take some time to render when they come into the viewport. You can use the `lazyPlaceholder` prop to customize what the user sees during this short period.
+
+#### `lazyPreloadDistance`
+
+When `lazy` is enabled, you can specify how many adjacent screens should be preloaded in advance with this prop. This value defaults to `0` which means lazy pages are loaded as they come into the viewport.
+
+#### `lazyPlaceholder`
+
+Function that returns a React element to render if this screen hasn't been rendered yet. The `lazy` option also needs to be enabled for this to work.
+
+This view is usually only shown for a split second. Keep it lightweight.
+
+By default, this renders `null`.
+
+#### `sceneStyle`
+
+Style to apply to the view wrapping each screen. You can pass this to override some default styles such as overflow clipping.
+
+### Events
+
+The navigator can [emit events](navigation-events.md) on certain actions. Supported events are:
+
+#### `tabPress`
+
+This event is fired when the user presses the tab button for the current screen in the tab bar. By default a tab press does several things:
+
+- If the tab is not focused, tab press will focus that tab
+- If the tab is already focused:
+ - 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`:
+
+```js name="Tab Press Event" snack static2dynamic
+import * as React from 'react';
+import { Text, View } from 'react-native';
+import {
+ createStaticNavigation,
+ useNavigation,
+} from '@react-navigation/native';
+import { createMaterialTopTabNavigator } from '@react-navigation/material-top-tabs';
+
+function HomeScreen() {
+ const navigation = useNavigation();
+
+ // codeblock-focus-start
+ React.useEffect(() => {
+ const unsubscribe = navigation.addListener('tabPress', (e) => {
+ // Prevent default behavior
+ e.preventDefault();
+
+ // Do something manually
+ // ...
+ });
+
+ return unsubscribe;
+ }, [navigation]);
+ // codeblock-focus-end
+
+ return (
+
+ Home Screen
+
+ Tab press event is prevented
+
+
+ );
+}
+
+function SettingsScreen() {
+ return (
+
+ Settings Screen
+
+ );
+}
+
+const MyTabs = createMaterialTopTabNavigator({
+ screens: {
+ Home: HomeScreen,
+ Settings: SettingsScreen,
+ },
+});
+
+const Navigation = createStaticNavigation(MyTabs);
+
+export default function App() {
+ return ;
+}
+```
+
+If you have a custom tab bar, make sure to emit this event.
+
+:::note
+
+By default, tabs are rendered lazily. So if you add a listener inside a screen component, it won't receive the event until the screen is focused for the first time. If you need to listen to this event before the screen is focused, you can specify the [listener in the screen config](navigation-events.md#listeners-prop-on-screen) instead.
+
+:::
+
+#### `tabLongPress`
+
+This event is fired when the user presses the tab button for the current screen in the tab bar for an extended period.
+
+Example:
+
+```js
+React.useEffect(() => {
+ const unsubscribe = navigation.addListener('tabLongPress', (e) => {
+ // Do something
+ });
+
+ return unsubscribe;
+}, [navigation]);
+```
+
+### Helpers
+
+The tab navigator adds the following methods to the navigation object:
+
+#### `jumpTo`
+
+Navigates to an existing screen in the tab navigator. The method accepts following arguments:
+
+- `name` - _string_ - Name of the route to jump to.
+- `params` - _object_ - Screen params to pass to the destination route.
+
+```js name="Tab Navigator - jumpTo" snack static2dynamic
+import * as React from 'react';
+import { Text, View } from 'react-native';
+import {
+ createStaticNavigation,
+ useNavigation,
+} from '@react-navigation/native';
+import { Button } from '@react-navigation/elements';
+import { createMaterialTopTabNavigator } from '@react-navigation/material-top-tabs';
+
+function HomeScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ Home Screen
+
+
+ );
+}
+
+function ProfileScreen({ route }) {
+ return (
+
+ Profile Screen
+ {route.params?.name && (
+ Name: {route.params.name}
+ )}
+
+ );
+}
+
+const MyTabs = createMaterialTopTabNavigator({
+ screens: {
+ Home: HomeScreen,
+ Profile: ProfileScreen,
+ },
+});
+
+const Navigation = createStaticNavigation(MyTabs);
+
+export default function App() {
+ return ;
+}
+```
+
+### Hooks
+
+The material top tab navigator exports the following hooks:
+
+#### `useTabAnimation`
+
+This hook returns an object containing an animated value that represents the current position of the tabs. This can be used to animate elements based on the swipe position of the tabs, such as the tab indicator:
+
+```js
+import { Animated } from 'react-native';
+import { useTabAnimation } from '@react-navigation/material-top-tabs';
+
+function MyView() {
+ const { position } = useTabAnimation();
+
+ return (
+
+ );
+}
+```
diff --git a/versioned_docs/version-8.x/migration-guides.md b/versioned_docs/version-8.x/migration-guides.md
new file mode 100644
index 00000000000..86ac82af8c8
--- /dev/null
+++ b/versioned_docs/version-8.x/migration-guides.md
@@ -0,0 +1,15 @@
+---
+id: migration-guides
+title: Migration Guides
+sidebar_label: Migration Guides
+---
+
+This page contains links to pages that will guide you through the process of upgrading React Navigation:
+
+- [Upgrading from 7.x to 8.x](../version-8.x/upgrading-from-7.x.md)
+- [Upgrading from 6.x to 7.x](../version-7.x/upgrading-from-6.x.md)
+- [Upgrading from 5.x to 6.x](../version-6.x/upgrading-from-5.x.md)
+- [Upgrading from 4.x to 5.x](../version-5.x/upgrading-from-4.x.md)
+- [Upgrading from 3.x to 4.x](../version-4.x/upgrading-from-3.x.md)
+
+If you're upgrading from a version older by multiple major releases, please refer to the migration guides of all the versions in between when upgrading. We recommend configuring TypeScript for your React Navigation setup to make it easier to upgrade as you'll get type errors.
diff --git a/versioned_docs/version-8.x/modal.md b/versioned_docs/version-8.x/modal.md
new file mode 100755
index 00000000000..b97ab63ad42
--- /dev/null
+++ b/versioned_docs/version-8.x/modal.md
@@ -0,0 +1,127 @@
+---
+id: modal
+title: Opening a modal
+sidebar_label: Opening a modal
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+
+
+A modal displays content that temporarily blocks interactions with the main view.
+
+A modal is like a popup — it usually has a different transition animation, and is intended to focus on one particular interaction or piece of content.
+
+## Creating a stack with modal screens
+
+```js name="Modal" snack static2dynamic
+import * as React from 'react';
+import { View, Text } from 'react-native';
+import {
+ createStaticNavigation,
+ useNavigation,
+} from '@react-navigation/native';
+import { createStackNavigator } from '@react-navigation/stack';
+import { Button } from '@react-navigation/elements';
+
+function HomeScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ This is the home screen!
+
+
+ );
+}
+
+function ModalScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ This is a modal!
+
+
+ );
+}
+
+function DetailsScreen() {
+ return (
+
+ Details
+
+ );
+}
+
+// codeblock-focus-start
+const HomeStack = createStackNavigator({
+ screens: {
+ Home: {
+ screen: HomeScreen,
+ options: {
+ headerShown: false,
+ },
+ },
+ Details: {
+ screen: DetailsScreen,
+ options: {
+ headerShown: false,
+ },
+ },
+ },
+});
+
+const RootStack = createStackNavigator({
+ groups: {
+ Home: {
+ screens: {
+ App: {
+ screen: HomeStack,
+ options: { title: 'My App' },
+ },
+ },
+ },
+ // highlight-start
+ Modal: {
+ screenOptions: {
+ presentation: 'modal',
+ },
+ screens: {
+ MyModal: ModalScreen,
+ },
+ },
+ // highlight-end
+ },
+});
+
+const Navigation = createStaticNavigation(RootStack);
+
+export default function App() {
+ return ;
+}
+// codeblock-focus-end
+```
+
+
+
+Here, we are creating 2 groups of screens using the `RootStack.Group` component. The first group is for our regular screens, and the second group is for our modal screens. For the modal group, we have specified `presentation: 'modal'` in `screenOptions`. This will apply this option to all the screens inside the group. This option will change the animation for the screens to animate from bottom-to-top rather than right to left. The `presentation` option for stack navigator can be either `card` (default) or `modal`. The `modal` behavior slides the screen in from the bottom and allows the user to swipe down from the top to dismiss it on iOS.
+
+Instead of specifying this option for a group, it's also possible to specify it for a single screen using the `options` prop on `RootStack.Screen`.
+
+## Summary
+
+- To change the type of transition on a stack navigator you can use the [`presentation`](native-stack-navigator.md#presentation) option.
+- When `presentation` is set to `modal`, the screens behave like a modal, i.e. they have a bottom to top transition and may show part of the previous screen in the background.
+- Setting `presentation: 'modal'` on a group makes all the screens in the group modals, so to use non-modal transitions on other screens, we add another group with the default configuration.
+
+## Best practices
+
+Since modals are intended to be on top of other content, there are a couple of things to keep in mind when using modals:
+
+- Avoid nesting them inside other navigators like tab or drawer. Modal screens should be defined as part of the root stack.
+- Modal screens should be the last in the stack - avoid pushing regular screens on top of modals.
+- The first screen in a stack appears as a regular screen even if configured as a modal, since there is no screen before it to show behind. So always make sure that modal screens are pushed on top of a regular screen or another modal screen.
diff --git a/versioned_docs/version-8.x/more-resources.md b/versioned_docs/version-8.x/more-resources.md
new file mode 100755
index 00000000000..8e71db8cf3a
--- /dev/null
+++ b/versioned_docs/version-8.x/more-resources.md
@@ -0,0 +1,15 @@
+---
+id: more-resources
+title: More resources
+sidebar_label: More resources
+---
+
+## Talks
+
+- [Mobile App Development with React Native at Harvard Extension School](https://cs50.harvard.edu/mobile/2018/): Lecture 6 covers React Navigation, includes exercises, slides, and video.
+
+- [Mobile Navigation at React Alicante](https://www.youtube.com/watch?v=GBhdooVxX6Q): An overview and comparison of the approaches taken by react-native-navigation and react-navigation.
+
+- [It all starts with navigation at React Native EU](https://www.youtube.com/watch?v=Z0Jl1KCWiag): Explains the evolution of React Native navigation libraries over time and the problems that required building native APIs to solve and what those solutions were.
+
+- [React Navigation at React Amsterdam](https://www.youtube.com/watch?v=wJJZ9Od8MjM): An introduction to React Navigation.
diff --git a/versioned_docs/version-8.x/multiple-drawers.md b/versioned_docs/version-8.x/multiple-drawers.md
new file mode 100644
index 00000000000..604a9ad71df
--- /dev/null
+++ b/versioned_docs/version-8.x/multiple-drawers.md
@@ -0,0 +1,426 @@
+---
+id: multiple-drawers
+title: Multiple drawers
+sidebar_label: Multiple drawers
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+Sometimes we want to have multiple drawers on the same screen: one on the left and one on the right. This can be achieved in 2 ways:
+
+1. By using [`react-native-drawer-layout`](drawer-layout.md) directly (Recommended).
+2. By [nesting](nesting-navigators.md) 2 [drawer navigators](drawer-navigator.md).
+
+## Using `react-native-drawer-layout`
+
+When we have multiple drawers, only one of them shows the list of screens. The second drawer may often be used to show some additional information such as the list of users etc.
+
+In such cases, we can use [`react-native-drawer-layout`](drawer-layout.md) directly to render the second drawer. The drawer navigator will be used to render the first drawer and can be nested inside the second drawer:
+
+
+
+
+```js
+import * as React from 'react';
+import { View } from 'react-native';
+import { Drawer } from 'react-native-drawer-layout';
+import { createDrawerNavigator } from '@react-navigation/drawer';
+import {
+ createStaticNavigation,
+ useNavigation,
+} from '@react-navigation/native';
+import { Button } from '@react-navigation/elements';
+
+function HomeScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+
+
+ );
+}
+
+const LeftDrawerScreen = createDrawerNavigator({
+ screenOptions: {
+ drawerPosition: 'left',
+ },
+ screens: {
+ Home: HomeScreen,
+ },
+});
+
+function RightDrawerScreen() {
+ const [rightDrawerOpen, setRightDrawerOpen] = React.useState(false);
+
+ return (
+ setRightDrawerOpen(true)}
+ onClose={() => setRightDrawerOpen(false)}
+ drawerPosition="right"
+ renderDrawerContent={() => <>{/* Right drawer content */}>}
+ >
+
+
+ );
+}
+
+const Navigation = createStaticNavigation(RightDrawerScreen);
+
+export default function App() {
+ return ;
+}
+```
+
+
+
+
+```js
+import * as React from 'react';
+import { View } from 'react-native';
+import { Drawer } from 'react-native-drawer-layout';
+import { useNavigation } from '@react-navigation/native';
+import { createDrawerNavigator } from '@react-navigation/drawer';
+import { Button } from '@react-navigation/elements';
+
+function HomeScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+
+
+ );
+}
+
+const LeftDrawer = createDrawerNavigator();
+
+const LeftDrawerScreen = () => {
+ return (
+
+
+
+ );
+};
+
+function RightDrawerScreen() {
+ const [rightDrawerOpen, setRightDrawerOpen] = React.useState(false);
+
+ return (
+ setRightDrawerOpen(true)}
+ onClose={() => setRightDrawerOpen(false)}
+ drawerPosition="right"
+ renderDrawerContent={() => <>{/* Right drawer content */}>}
+ >
+
+
+ );
+}
+
+export default function App() {
+ return (
+
+
+
+ );
+}
+```
+
+
+
+
+But there is one problem. When we call `navigation.openDrawer()` in our `HomeScreen`, it always opens the left drawer. We don't have access to the right drawer via the `navigation` object since it's not a navigator.
+
+To solve this, we need to use context API to pass down a function to control the right drawer:
+
+
+
+
+```js
+import * as React from 'react';
+import { View } from 'react-native';
+import { Drawer } from 'react-native-drawer-layout';
+import { createDrawerNavigator } from '@react-navigation/drawer';
+import {
+ useNavigation,
+ createStaticNavigation,
+} from '@react-navigation/native';
+import { Button } from '@react-navigation/elements';
+
+const RightDrawerContext = React.createContext();
+
+function HomeScreen() {
+ const { openRightDrawer } = React.useContext(RightDrawerContext);
+ const navigation = useNavigation();
+
+ return (
+
+
+
+
+ );
+}
+
+const LeftDrawerScreen = createDrawerNavigator({
+ screenOptions: {
+ drawerPosition: 'left',
+ },
+ screens: {
+ Home: HomeScreen,
+ },
+});
+
+function RightDrawerScreen() {
+ const [rightDrawerOpen, setRightDrawerOpen] = React.useState(false);
+
+ const value = React.useMemo(
+ () => ({
+ openRightDrawer: () => setRightDrawerOpen(true),
+ closeRightDrawer: () => setRightDrawerOpen(false),
+ }),
+ []
+ );
+
+ return (
+ setRightDrawerOpen(true)}
+ onClose={() => setRightDrawerOpen(false)}
+ drawerPosition="right"
+ renderDrawerContent={() => <>{/* Right drawer content */}>}
+ >
+
+
+
+
+ );
+}
+
+const Navigation = createStaticNavigation(RightDrawerScreen);
+
+export default function App() {
+ return ;
+}
+```
+
+
+
+
+```js
+import * as React from 'react';
+import { View } from 'react-native';
+import { Drawer } from 'react-native-drawer-layout';
+import { useNavigation } from '@react-navigation/native';
+import { createDrawerNavigator } from '@react-navigation/drawer';
+import { Button } from '@react-navigation/elements';
+
+const RightDrawerContext = React.createContext();
+
+function HomeScreen() {
+ const navigation = useNavigation();
+ const { openRightDrawer } = React.useContext(RightDrawerContext);
+
+ return (
+
+
+
+
+ );
+}
+
+const LeftDrawer = createDrawerNavigator();
+
+const LeftDrawerScreen = () => {
+ return (
+
+
+
+ );
+};
+
+function RightDrawerScreen() {
+ const [rightDrawerOpen, setRightDrawerOpen] = React.useState(false);
+
+ const value = React.useMemo(
+ () => ({
+ openRightDrawer: () => setRightDrawerOpen(true),
+ closeRightDrawer: () => setRightDrawerOpen(false),
+ }),
+ []
+ );
+
+ return (
+ setRightDrawerOpen(true)}
+ onClose={() => setRightDrawerOpen(false)}
+ drawerPosition="right"
+ renderDrawerContent={() => <>{/* Right drawer content */}>}
+ >
+
+
+
+
+ );
+}
+
+export default function App() {
+ return (
+
+
+
+ );
+}
+```
+
+
+
+
+Here, we are using the `RightDrawerContext` to pass down the `openRightDrawer` function to the `HomeScreen`. Then we use `openRightDrawer` to open the right drawer.
+
+## Nesting 2 drawer navigators
+
+An alternative approach is to nest 2 [drawer navigators](drawer-navigator.md) inside each other. This is not recommended since it requires creating an additional screen and more nesting - which can make navigating and type checking more verbose. But this can be useful if both navigators include multiple screens.
+
+Here we have 2 drawer navigators nested inside each other, one is positioned on left and the other on the right:
+
+```js name="Multiple drawers" snack static2dynamic
+import * as React from 'react';
+import { View } from 'react-native';
+import { createDrawerNavigator } from '@react-navigation/drawer';
+import {
+ createStaticNavigation,
+ useNavigation,
+} from '@react-navigation/native';
+import { Button } from '@react-navigation/elements';
+
+function HomeScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+
+
+ );
+}
+
+const LeftDrawerScreen = createDrawerNavigator({
+ screenOptions: {
+ drawerPosition: 'left',
+ },
+ screens: {
+ Home: HomeScreen,
+ },
+});
+
+const RightDrawerScreen = createDrawerNavigator({
+ screenOptions: {
+ drawerPosition: 'right',
+ headerShown: false,
+ },
+ screens: {
+ HomeDrawer: LeftDrawerScreen,
+ },
+});
+
+const Navigation = createStaticNavigation(RightDrawerScreen);
+
+export default function App() {
+ return ;
+}
+```
+
+
+
+But there is one problem. When we call `navigation.openDrawer()` in our `HomeScreen`, it always opens the left drawer since it's the immediate parent of the screen.
+
+To solve this, we need to use [`navigation.getParent`](navigation-object.md#getparent) to refer to the right drawer which is the parent of the left drawer. So our code would look like:
+
+```js
+
+
+```
+
+However, this means that our button needs to know about the parent navigators, which isn't ideal. If our button is further nested inside other navigators, it'd need multiple `getParent()` calls. To solve this, we can pass the name of the screen where the navigator is to the `getParent` method to directly refer to the desired navigator.
+
+In this case:
+
+- `navigation.getParent('Home').openDrawer()` opens the left drawer since `'Home'` is a screen in the left drawer navigator.
+- `navigation.getParent('HomeDrawer').openDrawer()` opens the right drawer since `'HomeDrawer'` is a screen in the right drawer navigator.
+
+To customize the contents of the drawer, we can use the [`drawerContent` prop](drawer-navigator.md#drawercontent) to pass in a function that renders a custom component.
+
+The final code would look like this:
+
+```js name="Multiple drawers navigators" snack static2dynamic
+import * as React from 'react';
+import { Text, View } from 'react-native';
+import { createDrawerNavigator } from '@react-navigation/drawer';
+import {
+ createStaticNavigation,
+ useNavigation,
+} from '@react-navigation/native';
+import { Button } from '@react-navigation/elements';
+
+function HomeScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+
+
+
+ );
+}
+
+function RightDrawerContent() {
+ return (
+
+ This is the right drawer
+
+ );
+}
+
+const LeftDrawer = createDrawerNavigator({
+ screenOptions: {
+ drawerPosition: 'left',
+ },
+ screens: {
+ Home: HomeScreen,
+ },
+});
+
+const RightDrawer = createDrawerNavigator({
+ drawerContent: (props) => ,
+ screenOptions: {
+ drawerPosition: 'right',
+ headerShown: false,
+ },
+ screens: {
+ HomeDrawer: LeftDrawer,
+ },
+});
+
+const Navigation = createStaticNavigation(RightDrawer);
+
+export default function App() {
+ return ;
+}
+```
+
+## Summary
+
+- To have multiple drawers, you can use [`react-native-drawer-layout`](drawer-layout.md) directly in combination with a drawer navigator.
+- The [`drawerPosition`](drawer-layout.md#drawerposition) prop can be used to position the drawer on the right.
+- The methods to control the drawer can be passed down using context API when using [`react-native-drawer-layout`](drawer-layout.md).
+- When nesting multiple navigators, you can use [`navigation.getParent`](navigation-object.md#getparent) with a screen name to refer to the desired drawer.
diff --git a/versioned_docs/version-8.x/native-stack-navigator.md b/versioned_docs/version-8.x/native-stack-navigator.md
new file mode 100755
index 00000000000..17fac9c8b95
--- /dev/null
+++ b/versioned_docs/version-8.x/native-stack-navigator.md
@@ -0,0 +1,1716 @@
+---
+id: native-stack-navigator
+title: Native Stack Navigator
+sidebar_label: Native Stack
+---
+
+Native Stack Navigator provides a way for your app to transition between screens where each new screen is placed on top of a stack.
+
+
+
+
+
+This navigator uses the native APIs `UINavigationController` on iOS and `Fragment` on Android so that navigation built with `createNativeStackNavigator` will behave exactly the same and have the same performance characteristics as apps built natively on top of those APIs. It also offers basic Web support using [`react-native-web`](https://github.com/necolas/react-native-web).
+
+One thing to keep in mind is that while `@react-navigation/native-stack` offers native performance and exposes native features such as large title on iOS etc., it may not be as customizable as [`@react-navigation/stack`](stack-navigator.md) depending on your needs. So if you need more customization than what's possible in this navigator, consider using `@react-navigation/stack` instead - which is a more customizable JavaScript based implementation.
+
+## Installation
+
+To use this navigator, ensure that you have [`@react-navigation/native` and its dependencies (follow this guide)](getting-started.md), then install [`@react-navigation/native-stack`](https://github.com/react-navigation/react-navigation/tree/main/packages/native-stack):
+
+```bash npm2yarn
+npm install @react-navigation/native-stack
+```
+
+## Usage
+
+To use this navigator, import it from `@react-navigation/native-stack`:
+
+```js name="Native Stack Navigator" snack static2dynamic
+import * as React from 'react';
+import { Text, View } from 'react-native';
+import {
+ createStaticNavigation,
+ useNavigation,
+} from '@react-navigation/native';
+import { Button } from '@react-navigation/elements';
+// codeblock-focus-start
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+
+// codeblock-focus-end
+function HomeScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ Home Screen
+
+
+ );
+}
+
+function ProfileScreen() {
+ return (
+
+ Profile Screen
+
+ );
+}
+
+// codeblock-focus-start
+const MyStack = createNativeStackNavigator({
+ screens: {
+ Home: HomeScreen,
+ Profile: ProfileScreen,
+ },
+});
+// codeblock-focus-end
+
+const Navigation = createStaticNavigation(MyStack);
+
+export default function App() {
+ return ;
+}
+```
+
+:::info
+
+If you encounter any bugs while using `createNativeStackNavigator`, please open issues on [`react-native-screens`](https://github.com/software-mansion/react-native-screens) rather than the `react-navigation` repository!
+
+:::
+
+## API Definition
+
+### Props
+
+The native stack navigator accepts the [common props](navigator.md#configuration) shared by all navigators.
+
+### Options
+
+The following [options](screen-options.md) can be used to configure the screens in the navigator:
+
+#### `title`
+
+String that can be used as a fallback for `headerTitle`.
+
+#### `statusBarAnimation`
+
+Sets the status bar animation (similar to the `StatusBar` component). Defaults to `fade` on iOS and `none` on Android.
+
+Supported values:
+
+- `"fade"`
+- `"none"`
+- `"slide"`
+
+On Android, setting either `fade` or `slide` will set the transition of status bar color. On iOS, this option applies to appereance animation of the status bar.
+
+Requires setting `View controller-based status bar appearance -> YES` (or removing the config) in your `Info.plist` file.
+
+Only supported on Android and iOS.
+
+#### `statusBarHidden`
+
+Whether the status bar should be hidden on this screen.
+
+Requires setting `View controller-based status bar appearance -> YES` (or removing the config) in your `Info.plist` file.
+
+Only supported on Android and iOS.
+
+#### `statusBarStyle`
+
+Sets the status bar color (similar to the `StatusBar` component).
+
+Supported values:
+
+- `"auto"` (iOS only)
+- `"inverted"` (iOS only)
+- `"dark"`
+- `"light"`
+
+Defaults to `auto` on iOS and `light` on Android.
+
+Requires setting `View controller-based status bar appearance -> YES` (or removing the config) in your `Info.plist` file.
+
+Only supported on Android and iOS.
+
+#### `statusBarBackgroundColor`
+
+:::warning
+
+This option is deprecated and will be removed in a future release (for apps targeting Android SDK 35 or above edge-to-edge mode is enabled by default
+and it is expected that the edge-to-edge will be enforced in future SDKs, see [here](https://developer.android.com/about/versions/15/behavior-changes-15#ux) for more information).
+
+:::
+
+Sets the background color of the status bar (similar to the `StatusBar` component).
+
+Only supported on Android.
+
+#### `statusBarTranslucent`
+
+:::warning
+
+This option is deprecated and will be removed in a future release (for apps targeting Android SDK 35 or above edge-to-edge mode is enabled by default
+and it is expected that the edge-to-edge will be enforced in future SDKs, see [here](https://developer.android.com/about/versions/15/behavior-changes-15#ux) for more information).
+
+:::
+
+Sets the translucency of the status bar (similar to the `StatusBar` component). Defaults to `false`.
+
+Only supported on Android.
+
+#### `contentStyle`
+
+Style object for the scene content.
+
+#### `animationMatchesGesture`
+
+Whether the gesture to dismiss should use animation provided to `animation` prop. Defaults to `false`.
+
+Doesn't affect the behavior of screens presented modally.
+
+Only supported on iOS.
+
+#### `fullScreenGestureEnabled`
+
+Whether the gesture to dismiss should work on the whole screen. Using gesture to dismiss with this option results in the same transition animation as `simple_push`. This behavior can be changed by setting `customAnimationOnGesture` prop. Achieving the default iOS animation isn't possible due to platform limitations. Defaults to `false`.
+
+Doesn't affect the behavior of screens presented modally.
+
+Only supported on iOS.
+
+#### `fullScreenGestureShadowEnabled`
+
+Whether the full screen dismiss gesture has shadow under view during transition. Defaults to `true`.
+
+This does not affect the behavior of transitions that don't use gestures enabled by `fullScreenGestureEnabled` prop.
+
+#### `gestureEnabled`
+
+Whether you can use gestures to dismiss this screen. Defaults to `true`. Only supported on iOS.
+
+#### `animationTypeForReplace`
+
+The type of animation to use when this screen replaces another screen. Defaults to `push`.
+
+Supported values:
+
+- `push`: the new screen will perform push animation.
+
+
+
+- `pop`: the new screen will perform pop animation.
+
+
+
+#### `animation`
+
+How the screen should animate when pushed or popped.
+
+Only supported on Android and iOS.
+
+Supported values:
+
+- `default`: use the platform default animation
+
+
+- `fade`: fade screen in or out
+
+
+- `fade_from_bottom`: fade the new screen from bottom
+
+
+- `flip`: flip the screen, requires `presentation: "modal"` (iOS only)
+
+
+- `simple_push`: default animation, but without shadow and native header transition (iOS only, uses default animation on Android)
+
+
+- `slide_from_bottom`: slide in the new screen from bottom
+
+
+- `slide_from_right`: slide in the new screen from right (Android only, uses default animation on iOS)
+
+
+- `slide_from_left`: slide in the new screen from left (Android only, uses default animation on iOS)
+
+
+- `none`: don't animate the screen
+
+
+#### `presentation`
+
+How should the screen be presented.
+
+Only supported on Android and iOS.
+
+Supported values:
+
+- `card`: the new screen will be pushed onto a stack, which means the default animation will be slide from the side on iOS, the animation on Android will vary depending on the OS version and theme.
+
+
+- `modal`: the new screen will be presented modally. this also allows for a nested stack to be rendered inside the screen.
+
+
+- `transparentModal`: the new screen will be presented modally, but in addition, the previous screen will stay so that the content below can still be seen if the screen has translucent background.
+
+
+- `containedModal`: will use "UIModalPresentationCurrentContext" modal style on iOS and will fallback to "modal" on Android.
+
+
+- `containedTransparentModal`: will use "UIModalPresentationOverCurrentContext" modal style on iOS and will fallback to "transparentModal" on Android.
+
+
+- `fullScreenModal`: will use "UIModalPresentationFullScreen" modal style on iOS and will fallback to "modal" on Android. A screen using this presentation style can't be dismissed by gesture.
+
+
+- `formSheet`: will use "BottomSheetBehavior" on Android and "UIModalPresentationFormSheet" modal style on iOS.
+
+
+
+##### Using Form Sheet
+
+To use Form Sheet for your screen, add `presentation: 'formSheet'` to the `options`.
+
+```js name="Form Sheet" snack static2dynamic
+import * as React from 'react';
+import { Text, View } from 'react-native';
+import {
+ createStaticNavigation,
+ useNavigation,
+} from '@react-navigation/native';
+import { Button } from '@react-navigation/elements';
+
+// codeblock-focus-start
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+
+// codeblock-focus-end
+
+function HomeScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ Home Screen
+
+
+ );
+}
+
+function ProfileScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ Profile Screen
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam accumsan
+ euismod enim, quis porta ligula egestas sed. Maecenas vitae consequat
+ odio, at dignissim lorem. Ut euismod eros ac mi ultricies, vel pharetra
+ tortor commodo. Interdum et malesuada fames ac ante ipsum primis in
+ faucibus. Nullam at urna in metus iaculis aliquam at sed quam. In
+ ullamcorper, ex ut facilisis commodo, urna diam posuere urna, at
+ condimentum mi orci ac ipsum. In hac habitasse platea dictumst. Donec
+ congue pharetra ipsum in finibus. Nulla blandit finibus turpis, non
+ vulputate elit viverra a. Curabitur in laoreet nisl.
+
+
+
+ );
+}
+
+// codeblock-focus-start
+const MyStack = createNativeStackNavigator({
+ screens: {
+ Home: {
+ screen: HomeScreen,
+ },
+ Profile: {
+ screen: ProfileScreen,
+ options: {
+ presentation: 'formSheet',
+ headerShown: false,
+ sheetAllowedDetents: 'fitToContents',
+ },
+ },
+ },
+});
+// codeblock-focus-end
+
+const Navigation = createStaticNavigation(MyStack);
+
+export default function App() {
+ return ;
+}
+```
+
+:::warning
+
+Due to technical issues in platform component integration with `react-native`, `presentation: 'formSheet'` has limited support for `flex: 1`.
+
+On Android, using `flex: 1` on a top-level content container passed to a `formSheet` with `showAllowedDetents: 'fitToContents'` causes the sheet to not display at all, leaving only the dimmed background visible. This is because it is the sheet, not the parent who is source of the size. Setting fixed values for `sheetAllowedDetents`, e.g. `[0.4, 0.9]`, works correctly (content is aligned for the highest detent).
+
+On iOS, `flex: 1` with `showAllowedDetents: 'fitToContents'` works properly but setting a fixed value for `showAllowedDetents` causes the screen to not respect the `flex: 1` style - the height of the container does not fill the `formSheet` fully, but rather inherits intrinsic size of its contents. This tradeoff is _currently_ necessary to prevent ["sheet flickering" problem on iOS](https://github.com/software-mansion/react-native-screens/issues/1722).
+
+If you don't use `flex: 1` but the content's height is less than max screen height, the rest of the sheet might become translucent or use the default theme background color (you can see this happening on the screenshots in the descrption of [this PR](https://github.com/software-mansion/react-native-screens/pull/2462)). To match the sheet to the background of your content, set `backgroundColor` in the `contentStyle` prop of the given screen.
+
+On Android, there are also some problems with getting nested ScrollViews to work properly. The solution is to set `nestedScrollEnabled` on the `ScrollView`, but this does not work if the content's height is less than the `ScrollView`'s height. Please see [this PR](https://github.com/facebook/react-native/pull/44099) for details and suggested [workaround](https://github.com/facebook/react-native/pull/44099#issuecomment-2058469661).
+
+On Android, nested stack and `headerShown` prop are not currently supported for screens with `presentation: 'formSheet'`.
+
+:::
+
+#### `sheetAllowedDetents`
+
+:::note
+
+Works only when `presentation` is set to `formSheet`.
+
+:::
+
+
+
+Describes heights where a sheet can rest.
+
+Supported values:
+
+- `fitToContents` - intents to set the sheet height to the height of its contents.
+- Array of fractions, e.g. `[0.25, 0.5, 0.75]`:
+ - Heights should be described as fraction (a number from `[0, 1]` interval) of screen height / maximum detent height.
+ - The array **must** be sorted in ascending order. This invariant is verified only in developement mode, where violation results in error.
+ - iOS accepts any number of detents, while **Android is limited to three** - any surplus values, beside first three are ignored.
+
+Defaults to `[1.0]`.
+
+Only supported on Android and iOS.
+
+#### `sheetElevation`
+
+:::note
+
+Works only when `presentation` is set to `formSheet`.
+
+:::
+
+
+
+Integer value describing elevation of the sheet, impacting shadow on the top edge of the sheet.
+
+Not dynamic - changing it after the component is rendered won't have an effect.
+
+Defaults to `24`.
+
+Only supported on Android.
+
+#### `sheetExpandsWhenScrolledToEdge`
+
+:::note
+
+Works only when `presentation` is set to `formSheet`.
+
+:::
+
+
+
+Whether the sheet should expand to larger detent when scrolling.
+
+Defaults to `true`.
+
+Only supported on iOS.
+
+:::warning
+
+Please note that for this interaction to work, the ScrollView must be "first-subview-chain" descendant of the Screen component. This restriction is due to platform requirements.
+
+:::
+
+#### `sheetCornerRadius`
+
+:::note
+
+Works only when `presentation` is set to `formSheet`.
+
+:::
+
+
+
+The corner radius that the sheet will try to render with.
+
+If set to non-negative value it will try to render sheet with provided radius, else it will apply system default.
+
+If left unset, system default is used.
+
+Only supported on Android and iOS.
+
+#### `sheetInitialDetentIndex`
+
+:::note
+
+Works only when `presentation` is set to `formSheet`.
+
+:::
+
+
+
+**Index** of the detent the sheet should expand to after being opened.
+
+If the specified index is out of bounds of `sheetAllowedDetents` array, in dev environment more errors will be thrown, in production the value will be reset to default value.
+
+Additionaly there is `last` value available, when set the sheet will expand initially to last (largest) detent.
+
+Defaults to `0` - which represents first detent in the detents array.
+
+Only supported on Android and iOS.
+
+#### `sheetGrabberVisible`
+
+:::note
+
+Works only when `presentation` is set to `formSheet`.
+
+:::
+
+
+
+Boolean indicating whether the sheet shows a grabber at the top.
+
+Defaults to `false`.
+
+Only supported on iOS.
+
+#### `sheetLargestUndimmedDetentIndex`
+
+:::note
+
+Works only when `presentation` is set to `formSheet`.
+
+:::
+
+
+
+The largest sheet detent for which a view underneath won't be dimmed.
+
+This prop can be set to an number, which indicates index of detent in `sheetAllowedDetents` array for which there won't be a dimming view beneath the sheet.
+
+Additionaly there are following options available:
+
+- `none` - there will be dimming view for all detents levels,
+- `last` - there won't be a dimming view for any detent level.
+
+Defaults to `none`, indicating that the dimming view should be always present.
+
+Only supported on Android and iOS.
+
+#### `orientation`
+
+The display orientation to use for the screen.
+
+Supported values:
+
+- `default` - resolves to "all" without "portrait_down" on iOS. On Android, this lets the system decide the best orientation.
+- `all`: all orientations are permitted.
+- `portrait`: portrait orientations are permitted.
+- `portrait_up`: right-side portrait orientation is permitted.
+- `portrait_down`: upside-down portrait orientation is permitted.
+- `landscape`: landscape orientations are permitted.
+- `landscape_left`: landscape-left orientation is permitted.
+- `landscape_right`: landscape-right orientation is permitted.
+
+Only supported on Android and iOS.
+
+#### `autoHideHomeIndicator`
+
+Boolean indicating whether the home indicator should prefer to stay hidden. Defaults to `false`.
+
+Only supported on iOS.
+
+#### `gestureDirection`
+
+Sets the direction in which you should swipe to dismiss the screen.
+
+Supported values:
+
+- `vertical` – dismiss screen vertically
+- `horizontal` – dismiss screen horizontally (default)
+
+When using `vertical` option, options `fullScreenGestureEnabled: true`, `customAnimationOnGesture: true` and `animation: 'slide_from_bottom'` are set by default.
+
+Only supported on iOS.
+
+#### `animationDuration`
+
+Changes the duration (in milliseconds) of `slide_from_bottom`, `fade_from_bottom`, `fade` and `simple_push` transitions on iOS. Defaults to `500`.
+
+The duration is not customizable for:
+
+- Screens with `default` and `flip` animations
+- Screens with `presentation` set to `modal`, `formSheet`, `pageSheet` (regardless of animation)
+
+Only supported on iOS.
+
+#### `navigationBarColor`
+
+:::warning
+
+This option is deprecated and will be removed in a future release (for apps targeting Android SDK 35 or above edge-to-edge mode is enabled by default
+and it is expected that the edge-to-edge will be enforced in future SDKs, see [here](https://developer.android.com/about/versions/15/behavior-changes-15#ux) for more information).
+
+:::
+
+Sets the navigation bar color. Defaults to initial status bar color.
+
+Only supported on Android.
+
+#### `navigationBarHidden`
+
+Boolean indicating whether the navigation bar should be hidden. Defaults to `false`.
+
+Only supported on Android.
+
+#### `freezeOnBlur`
+
+Boolean indicating whether to prevent inactive screens from re-rendering. Defaults to `false`.
+Defaults to `true` when `enableFreeze()` from `react-native-screens` package is run at the top of the application.
+
+Only supported on iOS and Android.
+
+#### `scrollEdgeEffects`
+
+Configures the scroll edge effect for the _content ScrollView_ (the ScrollView that is present in first descendants chain of the Screen).
+Depending on values set, it will blur the scrolling content below certain UI elements (e.g. header items, search bar) for the specified edge of the ScrollView.
+When set in nested containers, i.e. Native Stack inside Native Bottom Tabs, or the other way around, the ScrollView will use only the innermost one's config.
+
+Edge effects can be configured for each edge separately. The following values are currently supported:
+
+- `automatic` - the automatic scroll edge effect style,
+- `hard` - a scroll edge effect with a hard cutoff and dividing line,
+- `soft` - a soft-edged scroll edge effect,
+- `hidden` - no scroll edge effect.
+
+Defaults to `automatic` for each edge.
+
+:::note
+
+Using both `blurEffect` and `scrollEdgeEffects` (>= iOS 26) simultaneously may cause overlapping effects.
+
+:::
+
+Only supported on iOS, starting from iOS 26.
+
+### Header related options
+
+The navigator supports following options to configure the header:
+
+#### `headerBackButtonMenuEnabled`
+
+Boolean indicating whether to show the menu on longPress of iOS >= 14 back button. Defaults to `true`.
+
+Only supported on iOS.
+
+
+
+#### `headerBackVisible`
+
+Whether the back button is visible in the header. You can use it to show a back button alongside `headerLeft` if you have specified it.
+
+This will have no effect on the first screen in the stack.
+
+#### `headerBackTitle`
+
+Title string used by the back button on iOS. Defaults to the previous scene's title, "Back" or arrow icon depending on the available space. See `headerBackButtonDisplayMode` to read about limitations and customize the behavior.
+
+Use `headerBackButtonDisplayMode: "minimal"` to hide it.
+
+Only supported on iOS.
+
+
+
+#### `headerBackButtonDisplayMode`
+
+How the back button displays icon and title.
+
+Supported values:
+
+- "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).
+- "minimal" – Always displays only the icon without a title.
+
+The space-aware behavior is disabled when:
+
+- The iOS version is 13 or lower
+- Custom font family or size is set (e.g. with `headerBackTitleStyle`)
+- Back button menu is disabled (e.g. with `headerBackButtonMenuEnabled`)
+
+In such cases, a static title and icon are always displayed.
+
+Only supported on iOS.
+
+#### `headerBackTitleStyle`
+
+Style object for header back title. Supported properties:
+
+- `fontFamily`
+- `fontSize`
+
+Only supported on iOS.
+
+
+
+Example:
+
+```js
+headerBackTitleStyle: {
+ fontSize: 14,
+ fontFamily: 'Georgia',
+},
+```
+
+#### `headerBackIcon`
+
+Icon to display in the header as the icon in the back button. Defaults to back icon image for the platform:
+
+- A chevron on iOS
+- An arrow on Android
+
+Currently only supports image sources.
+
+Example:
+
+```js
+headerBackIcon: {
+ type: 'image',
+ source: require('./path/to/icon.png'),
+}
+```
+
+#### `headerLargeStyle`
+
+Style of the header when a large title is shown. The large title is shown if `headerLargeTitleEnabled` is `true` and the edge of any scrollable content reaches the matching edge of the header.
+
+Supported properties:
+
+- backgroundColor
+
+Only supported on iOS.
+
+
+
+#### `headerLargeTitleEnabled`
+
+Whether to enable header with large title which collapses to regular header on scroll.
+Defaults to `false`.
+
+For large title to collapse on scroll, the content of the screen should be wrapped in a scrollable view such as `ScrollView` or `FlatList`. If the scrollable area doesn't fill the screen, the large title won't collapse on scroll. You also need to specify `contentInsetAdjustmentBehavior="automatic"` in your `ScrollView`, `FlatList` etc.
+
+Only supported on iOS.
+
+
+
+#### `headerLargeTitleShadowVisible`
+
+Whether drop shadow of header is visible when a large title is shown.
+
+#### `headerLargeTitleStyle`
+
+Style object for large title in header. Supported properties:
+
+- `fontFamily`
+- `fontSize`
+- `fontWeight`
+- `color`
+
+Only supported on iOS.
+
+
+
+Example:
+
+```js
+ headerLargeTitleStyle: {
+ fontFamily: 'Georgia',
+ fontSize: 22,
+ fontWeight: '500',
+ color: 'blue',
+ },
+```
+
+#### `headerStyle`
+
+Style object for header. Supported properties:
+
+- `backgroundColor`
+
+
+
+#### `headerShadowVisible`
+
+Whether to hide the elevation shadow (Android) or the bottom border (iOS) on the header.
+
+Android:
+
+
+iOS:
+
+
+#### `headerTransparent`
+
+Boolean indicating whether the navigation bar is translucent.
+
+Defaults to `false`. Setting this to `true` makes the header absolutely positioned - so that the header floats over the screen so that it overlaps the content underneath, and changes the background color to `transparent` unless specified in `headerStyle`.
+
+This is useful if you want to render a semi-transparent header or a blurred background.
+
+Note that if you don't want your content to appear under the header, you need to manually add a top margin to your content. React Navigation won't do it automatically.
+
+To get the height of the header, you can use [`HeaderHeightContext`](elements.md#headerheightcontext) with [React's Context API](https://react.dev/reference/react/useContext#contextconsumer) or [`useHeaderHeight`](elements.md#useheaderheight).
+
+#### `headerBlurEffect`
+
+Blur effect for the translucent header. The `headerTransparent` option needs to be set to `true` for this to work.
+
+Supported values:
+
+- `extraLight`
+
+- `light`
+
+
+- `dark`
+
+
+- `regular`
+
+
+- `prominent`
+
+
+- `systemUltraThinMaterial`
+
+
+- `systemThinMaterial`
+
+
+- `systemMaterial`
+
+
+- `systemThickMaterial`
+
+
+- `systemChromeMaterial`
+
+
+- `systemUltraThinMaterialLight`
+
+
+- `systemThinMaterialLight`
+
+
+- `systemMaterialLight`
+
+
+- `systemThickMaterialLight`
+
+
+- `systemChromeMaterialLight`
+
+
+- `systemUltraThinMaterialDark`
+
+- `systemThinMaterialDark`
+
+
+- `systemMaterialDark`
+
+- `systemThickMaterialDark`
+
+
+- `systemChromeMaterialDark`
+
+
+:::note
+
+Using both `blurEffect` and `scrollEdgeEffects` (>= iOS 26) simultaneously may cause overlapping effects.
+
+:::
+
+Only supported on iOS.
+
+#### `headerBackground`
+
+Function which returns a React Element to render as the background of the header. This is useful for using backgrounds such as an image or a gradient.
+
+
+
+Example:
+
+```js
+ headerBackground: () => (
+
+ ),
+```
+
+#### `headerTintColor`
+
+Tint color for the header. Changes the color of back button and title.
+
+
+
+#### `headerLeft`
+
+Function which returns a React Element to display on the left side of the header. This replaces the back button. See `headerBackVisible` to show the back button along side left element. It receives the following properties in the arguments:
+
+- `tintColor` - The tint color to apply. Defaults to the [theme](themes.md)'s primary color.
+- `canGoBack` - Boolean indicating whether there is a screen to go back to.
+- `label` - Label text for the button. Usually the title of the previous screen.
+- `href` - The `href` to use for the anchor tag on web
+
+
+
+Example:
+
+```js
+ headerLeft: () => (
+
+ ),
+ headerBackVisible: true,
+ headerBackTitle: 'Back',
+```
+
+#### `unstable_headerLeftItems`
+
+:::warning
+
+This option is experimental and may change in a minor release.
+
+:::
+
+Function which returns an array of items to display as on the left side of the header. This will override `headerLeft` if both are specified. It receives the following properties in the arguments:
+
+- `tintColor` - The tint color to apply. Defaults to the [theme](themes.md)'s primary color.
+- `canGoBack` - Boolean indicating whether there is a screen to go back to.
+
+Example:
+
+```js
+unstable_headerRightItems: () => [
+ {
+ type: 'button',
+ title: 'Edit',
+ onPress: () => {
+ // Do something
+ },
+ },
+],
+```
+
+See [Header items](#header-items) for more information.
+
+Only supported on iOS.
+
+#### `headerRight`
+
+Function which returns a React Element to display on the right side of the header. It receives the following properties in the arguments:
+
+- `tintColor` - The tint color to apply. Defaults to the [theme](themes.md)'s primary color.
+- `canGoBack` - Boolean indicating whether there is a screen to go back to.
+
+
+
+Example:
+
+```js
+headerRight: () => ;
+```
+
+#### `unstable_headerRightItems`
+
+:::warning
+
+This option is experimental and may change in a minor release.
+
+:::
+
+Function which returns an array of items to display as on the right side of the header. This will override `headerRight` if both are specified. It receives the following properties in the arguments:
+
+- `tintColor` - The tint color to apply. Defaults to the [theme](themes.md)'s primary color.
+- `canGoBack` - Boolean indicating whether there is a screen to go back to.
+
+Example:
+
+```js
+unstable_headerRightItems: () => [
+ {
+ type: 'button',
+ title: 'Edit',
+ onPress: () => {
+ // Do something
+ },
+ },
+],
+```
+
+See [Header items](#header-items) for more information.
+
+Only supported on iOS.
+
+#### `headerTitle`
+
+String or a function that returns a React Element to be used by the header. Defaults to `title` or name of the screen.
+
+When a function is passed, it receives `tintColor` and`children` in the options object as an argument. The title string is passed in `children`.
+
+Note that if you render a custom element by passing a function, animations for the title won't work.
+
+#### `headerTitleAlign`
+
+How to align the header title. Possible values:
+
+- `left`
+
+
+- `center`
+
+
+Defaults to `left` on platforms other than iOS.
+
+Not supported on iOS. It's always `center` on iOS and cannot be changed.
+
+#### `headerTitleStyle`
+
+Style object for header title. Supported properties:
+
+- `fontFamily`
+- `fontSize`
+- `fontWeight`
+- `color`
+
+
+
+Example:
+
+```js
+ headerTitleStyle: {
+ color: 'blue',
+ fontSize: 22,
+ fontFamily: 'Georgia',
+ fontWeight: 300,
+ },
+```
+
+#### `headerSearchBarOptions`
+
+Options to render a native search bar. Search bars are rarely static so normally it is controlled by passing an object to `headerSearchBarOptions` navigation option in the component's body.
+
+On iOS, you also need to specify `contentInsetAdjustmentBehavior="automatic"` in your `ScrollView`, `FlatList` etc. If you don't have a `ScrollView`, specify `headerTransparent: false`.
+
+Example:
+
+```js
+React.useLayoutEffect(() => {
+ navigation.setOptions({
+ headerSearchBarOptions: {
+ // search bar options
+ },
+ });
+}, [navigation]);
+```
+
+Supported properties are:
+
+##### `ref`
+
+Ref to manipulate the search input imperatively. It contains the following methods:
+
+- `focus` - focuses the search bar
+- `blur` - removes focus from the search bar
+- `setText` - sets the search bar's content to given value
+- `clearText` - removes any text present in the search bar input field
+- `cancelSearch` - cancel the search and close the search bar
+- `toggleCancelButton` - depending on passed boolean value, hides or shows cancel button (only supported on iOS)
+
+##### `autoCapitalize`
+
+Controls whether the text is automatically auto-capitalized as it is entered by the user.
+Possible values:
+
+- `systemDefault`
+- `none`
+- `words`
+- `sentences`
+- `characters`
+
+Defaults to `systemDefault` which is the same as `sentences` on iOS and `none` on Android.
+
+##### `autoFocus`
+
+Whether to automatically focus search bar when it's shown. Defaults to `false`.
+
+Only supported on Android.
+
+##### `barTintColor`
+
+The search field background color. By default bar tint color is translucent.
+
+Only supported on iOS.
+
+
+
+##### `tintColor`
+
+The color for the cursor caret and cancel button text.
+
+Only supported on iOS.
+
+
+
+##### `cancelButtonText`
+
+The text to be used instead of default `Cancel` button text.
+
+Only supported on iOS. **Deprecated** starting from iOS 26.
+
+##### `disableBackButtonOverride`
+
+Whether the back button should close search bar's text input or not. Defaults to `false`.
+
+Only supported on Android.
+
+##### `hideNavigationBar`
+
+Boolean indicating whether to hide the navigation bar during searching.
+
+If left unset, system default is used.
+
+Only supported on iOS.
+
+##### `hideWhenScrolling`
+
+Boolean indicating whether to hide the search bar when scrolling. Defaults to `true`.
+
+Only supported on iOS.
+
+##### `inputType`
+
+The type of the input. Defaults to `"text"`.
+
+Supported values:
+
+- `"text"`
+- `"phone"`
+- `"number"`
+- `"email"`
+
+Only supported on Android.
+
+##### `obscureBackground`
+
+Boolean indicating whether to obscure the underlying content with semi-transparent overlay.
+
+If left unset, system default is used.
+
+Only supported on iOS.
+
+##### `placement`
+
+Controls preferred placement of the search bar. Defaults to `automatic`.
+
+Supported values:
+
+- `automatic`
+- `stacked`
+- `inline` (**deprecated** starting from iOS 26, it is mapped to `integrated`)
+- `integrated` (available starting from iOS 26, on prior versions it is mapped to `inline`)
+- `integratedButton` (available starting from iOS 26, on prior versions it is mapped to `inline`)
+- `integratedCentered` (available starting from iOS 26, on prior versions it is mapped to `inline`)
+
+Only supported on iOS.
+
+##### `allowToolbarIntegration`
+
+Boolean indicating whether the system can place the search bar among other toolbar items on iPhone.
+
+Set this prop to `false` to prevent the search bar from appearing in the toolbar when `placement` is `automatic`, `integrated`, `integratedButton` or `integratedCentered`.
+
+Defaults to `true`. If `placement` is set to `stacked`, the value of this prop will be overridden with `false`.
+
+Only supported on iOS, starting from iOS 26.
+
+##### `placeholder`
+
+Text displayed when search field is empty.
+
+##### `textColor`
+
+The color of the text in the search field.
+
+
+
+##### `hintTextColor`
+
+The color of the hint text in the search field.
+
+Only supported on Android.
+
+
+
+##### `headerIconColor`
+
+The color of the search and close icons shown in the header
+
+Only supported on Android.
+
+
+
+##### `shouldShowHintSearchIcon`
+
+Whether to show the search hint icon when search bar is focused. Defaults to `true`.
+
+Only supported on Android.
+
+##### `onBlur`
+
+A callback that gets called when search bar has lost focus.
+
+##### `onCancelButtonPress`
+
+A callback that gets called when the cancel button is pressed.
+
+##### `onChangeText`
+
+A callback that gets called when the text changes. It receives the current text value of the search bar.
+
+Example:
+
+```js
+const [search, setSearch] = React.useState('');
+
+React.useLayoutEffect(() => {
+ navigation.setOptions({
+ headerSearchBarOptions: {
+ onChangeText: (event) => setSearch(event.nativeEvent.text),
+ },
+ });
+}, [navigation]);
+```
+
+#### `headerShown`
+
+Whether to show the header. The header is shown by default. Setting this to `false` hides the header.
+
+#### `header`
+
+Custom header to use instead of the default header.
+
+This accepts a function that returns a React Element to display as a header. The function receives an object containing the following properties as the argument:
+
+- `navigation` - The navigation object for the current screen.
+- `route` - The route object for the current screen.
+- `options` - The options for the current screen
+- `back` - Options for the back button, contains an object with a `title` property to use for back button label.
+
+Example:
+
+```js
+import { getHeaderTitle } from '@react-navigation/elements';
+
+// ..
+
+header: ({ navigation, route, options, back }) => {
+ const title = getHeaderTitle(options, route.name);
+
+ return (
+ : undefined
+ }
+ style={options.headerStyle}
+ />
+ );
+};
+```
+
+To set a custom header for all the screens in the navigator, you can specify this option in the `screenOptions` prop of the navigator.
+
+Note that if you specify a custom header, the native functionality such as large title, search bar etc. won't work.
+
+### Events
+
+The navigator can [emit events](navigation-events.md) on certain actions. Supported events are:
+
+#### `transitionStart`
+
+This event is fired when the transition animation starts for the current screen.
+
+Event data:
+
+- `e.data.closing` - Boolean indicating whether the screen is being opened or closed.
+
+Example:
+
+```js
+React.useEffect(() => {
+ const unsubscribe = navigation.addListener('transitionStart', (e) => {
+ // Do something
+ });
+
+ return unsubscribe;
+}, [navigation]);
+```
+
+#### `transitionEnd`
+
+This event is fired when the transition animation ends for the current screen.
+
+Event data:
+
+- `e.data.closing` - Boolean indicating whether the screen was opened or closed.
+
+Example:
+
+```js
+React.useEffect(() => {
+ const unsubscribe = navigation.addListener('transitionEnd', (e) => {
+ // Do something
+ });
+
+ return unsubscribe;
+}, [navigation]);
+```
+
+#### `gestureCancel`
+
+This event is fired when a swipe back gesture is canceled on iOS.
+
+Example:
+
+```js
+React.useEffect(() => {
+ const unsubscribe = navigation.addListener('gestureCancel', (e) => {
+ // Do something
+ });
+
+ return unsubscribe;
+}, [navigation]);
+```
+
+Only supported on iOS.
+
+#### `sheetDetentChange`
+
+This event is fired when a screen with `presentation: 'formSheet'` changes its detent.
+
+Event data:
+
+- `e.data.index` - The current detent index in the `sheetAllowedDetents` array.
+- `e.data.stable` - On Android, `false` means the user is dragging the sheet or it is settling. On iOS, this is always `true`.
+
+Example:
+
+```js
+React.useEffect(() => {
+ const unsubscribe = navigation.addListener('sheetDetentChange', (e) => {
+ console.log('Detent index:', e.data.index);
+ console.log('Is stable:', e.data.stable);
+ });
+
+ return unsubscribe;
+}, [navigation]);
+```
+
+Only supported on Android and iOS.
+
+### Helpers
+
+The native stack navigator adds the following methods to the navigation object:
+
+#### `replace`
+
+Replaces the current screen with a new screen in the stack. The method accepts the following arguments:
+
+- `name` - _string_ - Name of the route to push onto the stack.
+- `params` - _object_ - Screen params to pass to the destination route.
+
+```js
+navigation.replace('Profile', { owner: 'Michaś' });
+```
+
+#### `push`
+
+Pushes a new screen to the top of the stack and navigate to it. The method accepts the following arguments:
+
+- `name` - _string_ - Name of the route to push onto the stack.
+- `params` - _object_ - Screen params to pass to the destination route.
+
+```js
+navigation.push('Profile', { owner: 'Michaś' });
+```
+
+#### `pop`
+
+Pops the current screen from the stack and navigates back to the previous screen. It takes one optional argument (`count`), which allows you to specify how many screens to pop back by.
+
+```js
+navigation.pop();
+```
+
+#### `popTo`
+
+Navigates back to a previous screen in the stack by popping screens after it. The method accepts the following arguments:
+
+- `name` - _string_ - Name of the route to navigate to.
+- `params` - _object_ - Screen params to pass to the destination route.
+- `options` - Options object containing the following properties:
+ - `merge` - _boolean_ - Whether params should be merged with the existing route params, or replace them (when navigating to an existing screen). Defaults to `false`.
+
+If a matching screen is not found in the stack, this will pop the current screen and add a new screen with the specified name and params.
+
+```js
+navigation.popTo('Profile', { owner: 'Michaś' });
+```
+
+#### `popToTop`
+
+Pops all of the screens in the stack except the first one and navigates to it.
+
+```js
+navigation.popToTop();
+```
+
+### Hooks
+
+The native stack navigator exports the following hooks:
+
+#### `useAnimatedHeaderHeight`
+
+The hook returns an animated value representing the height of the header. This is similar to [`useHeaderHeight`](elements.md#useheaderheight) but returns an animated value that changed as the header height changes, e.g. when expanding or collapsing large title or search bar on iOS.
+
+It can be used to animated content along with header height changes.
+
+```js
+import { Animated } from 'react-native';
+import { useAnimatedHeaderHeight } from '@react-navigation/native-stack';
+
+const MyView = () => {
+ const headerHeight = useAnimatedHeaderHeight();
+
+ return (
+
+ );
+};
+```
+
+## Header items
+
+The [`unstable_headerLeftItems`](#unstable_headerleftitems) and [`unstable_headerRightItems`](#unstable_headerrightitems) options allow you to add header items to the left and right side of the header respectively. This items can show native buttons, menus or custom React elements.
+
+On iOS 26+, the header right items can also be collapsed into an overflow menu by the system when there is not enough space to show all items. Note that custom elements (with `type: 'custom'`) won't be collapsed into the overflow menu.
+
+
+
+
+
+There are 3 categories of items that can be displayed in the header:
+
+### Action
+
+A regular button that performs an action when pressed, or shows a menu.
+
+Common properties:
+
+- `type`: Must be `button` or `menu`.
+- `label`: Label of the item. The label is not shown if `icon` is specified. However, it is used by screen readers, or if the header items get collapsed due to lack of space.
+- `labelStyle`: Style object for the label. Supported properties:
+ - `fontFamily`
+ - `fontSize`
+ - `fontWeight`
+ - `color`
+- `icon`: Optional icon to show instead of the label.
+
+ The icon can be an image:
+
+ ```js
+ {
+ type: 'image',
+ source: require('./path/to/image.png'),
+ }
+ ```
+
+ Or a [SF Symbols](https://developer.apple.com/sf-symbols/) name:
+
+ ```js
+ {
+ type: 'sfSymbol',
+ name: 'heart',
+ }
+ ```
+
+- `variant`: Visual variant of the button. Supported values:
+ - `plain` (default)
+ - `done`
+ - `prominent`
+- `tintColor`: Tint color to apply to the item.
+- `disabled`: Whether the item is disabled.
+- `width`: Width of the item.
+- `hidesSharedBackground` (iOS 26+): Whether the background this item may share with other items should be hidden. Setting this to `true` hides the liquid glass background.
+- `sharesBackground` (iOS 26+): Whether this item can share a background with other items.
+- `identifier` (iOS 26+) - An identifier used to match items across transitions.
+- `badge` (iOS 26+): An optional badge to display alongside the item. Supported properties:
+ - `value`: The value to display in the badge. It can be a string or a number.
+ - `style`: Style object for the badge. Supported properties:
+ - `fontFamily`
+ - `fontSize`
+ - `fontWeight`
+ - `color`
+- `accessibilityLabel`: Accessibility label for the item.
+- `accessibilityHint`: Accessibility hint for the item.
+
+Supported properties when `type` is `button`:
+
+- `onPress`: Function to call when the button is pressed.
+- `selected`: Whether the button is in a selected state.
+
+Example:
+
+```js
+unstable_headerRightItems: () => [
+ {
+ type: 'button',
+ label: 'Edit',
+ icon: {
+ type: 'sfSymbol',
+ name: 'pencil',
+ },
+ onPress: () => {
+ // Do something
+ },
+ },
+],
+```
+
+Supported properties when `type` is `menu`:
+
+- `changesSelectionAsPrimaryAction`: Whether the menu is a selection menu. Tapping an item in a selection menu will add a checkmark to the selected item. Defaults to `false`.
+- `menu`: An object containing the menu items. It contains the following properties:
+ - `title`: Optional title to show on top of the menu.
+ - `items`: An array of menu items. A menu item can be either an `action` or a `submenu`.
+ - `action`: An object with the following properties:
+ - `type`: Must be `action`.
+ - `label`: Label of the menu item.
+ - `icon`: Optional icon to show alongside the label. The icon can be a [SF Symbols](https://developer.apple.com/sf-symbols/) name:
+
+ ```js
+ {
+ type: 'sfSymbol',
+ name: 'trash',
+ }
+ ```
+
+ - `onPress`: Function to call when the menu item is pressed.
+ - `state`: Optional state of the menu item. Supported values:
+ - `on`
+ - `off`
+ - `mixed`
+ - `disabled`: Whether the menu item is disabled.
+ - `destructive`: Whether the menu item is styled as destructive.
+ - `hidden`: Whether the menu item is hidden.
+ - `keepsMenuPresented`: Whether to keep the menu open after selecting this item. Defaults to `false`.
+ - `discoverabilityLabel`: An elaborated title that explains the purpose of the action.
+
+ - `submenu`: An object with the following properties:
+ - `type`: Must be `submenu`.
+ - `label`: Label of the submenu item.
+ - `icon`: Optional icon to show alongside the label. The icon can be a [SF Symbols](https://developer.apple.com/sf-symbols/) name:
+
+ ```js
+ {
+ type: 'sfSymbol',
+ name: 'pencil',
+ }
+ ```
+
+ - `items`: An array of menu items (can be either `action` or `submenu`).
+
+Example:
+
+```js
+unstable_headerRightItems: () => [
+ {
+ type: 'menu',
+ label: 'Options',
+ icon: {
+ type: 'sfSymbol',
+ name: 'ellipsis',
+ },
+ menu: {
+ title: 'Options',
+ items: [
+ {
+ type: 'action',
+ label: 'Edit',
+ icon: {
+ type: 'sfSymbol',
+ name: 'pencil',
+ },
+ onPress: () => {
+ // Do something
+ },
+ },
+ {
+ type: 'submenu',
+ label: 'More',
+ items: [
+ {
+ type: 'action',
+ label: 'Delete',
+ destructive: true,
+ onPress: () => {
+ // Do something
+ },
+ },
+ ],
+ },
+ ],
+ },
+ },
+],
+```
+
+### Spacing
+
+An item to add spacing between other items in the header.
+
+Supported properties:
+
+- `type`: Must be `spacing`.
+- `spacing`: Amount of spacing to add.
+
+```js
+unstable_headerRightItems: () => [
+ {
+ type: 'button',
+ label: 'Edit',
+ onPress: () => {
+ // Do something
+ },
+ },
+ {
+ type: 'spacing',
+ spacing: 10,
+ },
+ {
+ type: 'button',
+ label: 'Delete',
+ onPress: () => {
+ // Do something
+ },
+ },
+],
+```
+
+### Custom
+
+A custom item to display any React Element in the header.
+
+Supported properties:
+
+- `type`: Must be `custom`.
+- `element`: A React Element to display as the item.
+- `hidesSharedBackground`: Whether the background this item may share with other items in the bar should be hidden. Setting this to `true` hides the liquid glass background on iOS 26+.
+
+Example:
+
+```js
+unstable_headerRightItems: () => [
+ {
+ type: 'custom',
+ element: ,
+ },
+],
+```
+
+The advantage of using this over [`headerLeft`](#headerleft) or [`headerRight`](#headerright) options is that it supports features like shared background on iOS 26+.
diff --git a/versioned_docs/version-8.x/navigating-without-navigation-prop.md b/versioned_docs/version-8.x/navigating-without-navigation-prop.md
new file mode 100755
index 00000000000..0200c83209a
--- /dev/null
+++ b/versioned_docs/version-8.x/navigating-without-navigation-prop.md
@@ -0,0 +1,359 @@
+---
+id: navigating-without-navigation-prop
+title: Navigating without the navigation prop
+sidebar_label: Navigation Ref
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+Sometimes you need to trigger a navigation action from places where you do not have access to the `navigation` object, such as a Redux middleware. For such cases, you can dispatch navigation actions use a [`ref` on the navigation container](navigation-container.md#ref).
+
+**Do not** use the `ref` if:
+
+- You need to navigate from inside a component without needing to pass the `navigation` prop down, see [`useNavigation`](use-navigation.md) instead. The `ref` behaves differently, and many helper methods specific to screens aren't available.
+- You need to handle deep links or universal links. Doing this with the `ref` has many edge cases. See [configuring links](configuring-links.md) for more information on handling deep linking.
+- You need to integrate with third party libraries, such as push notifications, branch etc. See [Integrating with other tools](deep-linking.md#integrating-with-other-tools) instead.
+
+**Do** use the `ref` if:
+
+- You use a state management library such as Redux, where you need to dispatch navigation actions from a middleware.
+
+Note that it's usually better to trigger navigation from user actions such as button presses, rather than from a Redux middleware. Navigating on user action makes the app feel more responsive and provides better UX. So consider this before using the `ref` for navigation. The `ref` is an escape hatch for scenarios that can't be handled with the existing APIs and should only be used in rare situations.
+
+## Usage
+
+You can get access to the root navigation object through a `ref` and pass it to the `RootNavigation` which we will later use to navigate.
+
+
+
+
+```js
+import { createStaticNavigation } from '@react-navigation/native';
+import { navigationRef } from './RootNavigation';
+
+/* ... */
+
+const Navigation = createStaticNavigation(RootStack);
+
+export default function App() {
+ return ;
+}
+```
+
+
+
+
+```js
+import { NavigationContainer } from '@react-navigation/native';
+import { navigationRef } from './RootNavigation';
+
+export default function App() {
+ return (
+ {/* ... */}
+ );
+}
+```
+
+
+
+
+In the next step, we define `RootNavigation`, which is a simple module with functions that dispatch user-defined navigation actions.
+
+```js
+// RootNavigation.js
+
+import { createNavigationContainerRef } from '@react-navigation/native';
+
+export const navigationRef = createNavigationContainerRef();
+
+export function navigate(name, params) {
+ if (navigationRef.isReady()) {
+ navigationRef.navigate(name, params);
+ }
+}
+
+// add other navigation functions that you need and export them
+```
+
+Then, in any of your javascript modules, import the `RootNavigation` and call functions which you exported from it. You may use this approach outside of your React components and, in fact, it works as well when used from within them.
+
+
+
+
+```js name="Using navigate in any js module" snack
+import * as React from 'react';
+import { View, Text } from 'react-native';
+import {
+ createStaticNavigation,
+ createNavigationContainerRef,
+} from '@react-navigation/native';
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+import { Button } from '@react-navigation/elements';
+
+const navigationRef = createNavigationContainerRef();
+
+// codeblock-focus-start
+function navigate(name, params) {
+ if (navigationRef.isReady()) {
+ navigationRef.navigate(name, params);
+ }
+}
+
+// Example of usage in any of js modules
+//import * as RootNavigation from './path/to/RootNavigation.js';
+
+// ...
+
+// RootNavigation.navigate('ChatScreen', { userName: 'Lucy' });
+
+function Home() {
+ return (
+
+
+
+ );
+}
+// codeblock-focus-end
+
+function Settings({ route }) {
+ return (
+
+ Hello {route.params.userName}
+
+
+ );
+}
+
+const RootStack = createNativeStackNavigator({
+ screens: {
+ Home: Home,
+ Settings: Settings,
+ },
+});
+
+const Navigation = createStaticNavigation(RootStack);
+
+export default function App() {
+ return ;
+}
+```
+
+
+
+
+```js name="Using navigate in any js module" snack
+import * as React from 'react';
+import { View, Text } from 'react-native';
+import {
+ NavigationContainer,
+ createNavigationContainerRef,
+} from '@react-navigation/native';
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+import { Button } from '@react-navigation/elements';
+
+const navigationRef = createNavigationContainerRef();
+
+// codeblock-focus-start
+function navigate(name, params) {
+ if (navigationRef.isReady()) {
+ navigationRef.navigate(name, params);
+ }
+}
+
+// Example of usage in any of js modules
+//import * as RootNavigation from './path/to/RootNavigation.js';
+
+// ...
+
+// RootNavigation.navigate('ChatScreen', { userName: 'Lucy' });
+
+function Home() {
+ return (
+
+
+
+ );
+}
+// codeblock-focus-end
+
+function Settings({ route }) {
+ return (
+
+ Hello {route.params.userName}
+
+
+ );
+}
+
+const RootStack = createNativeStackNavigator();
+
+export default function App() {
+ return (
+
+
+
+
+
+
+ );
+}
+```
+
+
+
+
+Apart from `navigate`, you can add other navigation actions:
+
+```js
+import { StackActions } from '@react-navigation/native';
+
+// ...
+
+export function push(...args) {
+ if (navigationRef.isReady()) {
+ navigationRef.dispatch(StackActions.push(...args));
+ }
+}
+```
+
+Note that a stack navigators needs to be rendered to handle this action. You may want to check the [docs for nesting](nesting-navigators.md#navigating-to-a-screen-in-a-nested-navigator) for more details.
+
+When writing tests, you may mock the navigation functions, and make assertions on whether the correct functions are called with the correct parameters.
+
+## Handling initialization
+
+When using this pattern, you need to keep few things in mind to avoid navigation from failing in your app.
+
+- The `ref` is set only after the navigation container renders, this can be async when handling deep links
+- A navigator needs to be rendered to be able to handle actions, the `ref` won't be ready without a navigator
+
+If you try to navigate without rendering a navigator or before the navigator finishes mounting, it will print an error and do nothing. So you'll need to add an additional check to decide what to do until your app mounts.
+
+For an example, consider the following scenario, you have a screen somewhere in the app, and that screen dispatches a redux action on `useEffect`/`componentDidMount`. You are listening for this action in your middleware and try to perform navigation when you get it. This will throw an error, because by this time, the parent navigator hasn't finished mounting and isn't ready. Parent's `useEffect`/`componentDidMount` is always called **after** child's `useEffect`/`componentDidMount`.
+
+To avoid this, you can use the `isReady()` method available on the ref as shown in the above examples.
+
+
+
+
+```js name="Handling navigation init" snack
+import * as React from 'react';
+import { Text, View } from 'react-native';
+import {
+ createStaticNavigation,
+ createNavigationContainerRef,
+} from '@react-navigation/native';
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+import { Button } from '@react-navigation/elements';
+
+// codeblock-focus-start
+const navigationRef = createNavigationContainerRef();
+
+function navigate(name, params) {
+ if (navigationRef.isReady()) {
+ // Perform navigation if the react navigation is ready to handle actions
+ navigationRef.navigate(name, params);
+ } else {
+ // You can decide what to do if react navigation is not ready
+ // You can ignore this, or add these actions to a queue you can call later
+ }
+}
+// codeblock-focus-end
+
+function Home() {
+ return (
+
+ Home
+
+
+ );
+}
+
+function Profile() {
+ return (
+
+ Profile
+
+ );
+}
+
+const RootStack = createNativeStackNavigator({
+ screens: {
+ Home: Home,
+ Profile: Profile,
+ },
+});
+
+const Navigation = createStaticNavigation(RootStack);
+
+export default function App() {
+ return ;
+}
+```
+
+
+
+
+```js name="Handling navigation init" snack
+import * as React from 'react';
+import { Text, View } from 'react-native';
+import {
+ NavigationContainer,
+ createNavigationContainerRef,
+} from '@react-navigation/native';
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+import { Button } from '@react-navigation/elements';
+
+const Stack = createNativeStackNavigator();
+// codeblock-focus-start
+const navigationRef = createNavigationContainerRef();
+
+function navigate(name, params) {
+ if (navigationRef.isReady()) {
+ // Perform navigation if the react navigation is ready to handle actions
+ navigationRef.navigate(name, params);
+ } else {
+ // You can decide what to do if react navigation is not ready
+ // You can ignore this, or add these actions to a queue you can call later
+ }
+}
+// codeblock-focus-end
+
+function Home() {
+ return (
+
+ Home
+
+
+ );
+}
+
+function Profile() {
+ return (
+
+ Profile
+
+ );
+}
+
+export default function App() {
+ return (
+
+
+
+
+
+
+ );
+}
+```
+
+
+
+
+If you're unsure if a navigator is rendered, you can call `navigationRef.current.getRootState()`, and it'll return a valid state object if any navigators are rendered, otherwise it will return `undefined`.
diff --git a/versioned_docs/version-8.x/navigating.md b/versioned_docs/version-8.x/navigating.md
new file mode 100755
index 00000000000..38344bdb2b3
--- /dev/null
+++ b/versioned_docs/version-8.x/navigating.md
@@ -0,0 +1,403 @@
+---
+id: navigating
+title: Moving between screens
+sidebar_label: Moving between screens
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+In the previous section, we defined a stack navigator with two routes (`Home` and `Details`), but we didn't learn how to let a user navigate from `Home` to `Details` (although we did learn how to change the _initial_ route in our code, but forcing our users to clone our repository and change the route in our code in order to see another screen is arguably among the worst user experiences one could imagine).
+
+If this was a web browser, we'd be able to write something like this:
+
+```js
+Go to Details
+```
+
+Another way to write this would be:
+
+```js
+ {
+ window.location.href = 'details.html';
+ }}
+>
+ Go to Details
+
+```
+
+We'll do something similar to the latter, but rather than using a `window.location` global, we'll use the `navigation` object that's accessible in our screen components.
+
+## Navigating to a new screen
+
+```js name="Navigating to a new screen" snack static2dynamic
+// codeblock-focus-start
+import * as React from 'react';
+import { View, Text } from 'react-native';
+import {
+ createStaticNavigation,
+ useNavigation,
+} from '@react-navigation/native';
+import {
+ createNativeStackNavigator,
+ createNativeStackScreen,
+} from '@react-navigation/native-stack';
+import { Button } from '@react-navigation/elements';
+
+function HomeScreen() {
+ // highlight-next-line
+ const navigation = useNavigation();
+
+ return (
+
+ Home Screen
+ // highlight-start
+
+ // highlight-end
+
+ );
+}
+
+// ... other code from the previous section
+// codeblock-focus-end
+
+function DetailsScreen() {
+ return (
+
+ Details Screen
+
+ );
+}
+
+const RootStack = createNativeStackNavigator({
+ initialRouteName: 'Home',
+ screens: {
+ Home: createNativeStackScreen({
+ screen: HomeScreen,
+ }),
+ Details: createNativeStackScreen({
+ screen: DetailsScreen,
+ }),
+ },
+});
+
+const Navigation = createStaticNavigation(RootStack);
+
+export default function App() {
+ return ;
+}
+```
+
+
+
+Let's break this down:
+
+- `navigation` - the `navigation` object is returned from the [`useNavigation`](use-navigation.md) hook (more about this later in ["The navigation object in depth"](navigation-object.md)).
+- `navigate('Details')` - we call the `navigate` function (on the `navigation` object — naming is hard!) with the name of the route that we'd like to move the user to.
+
+:::note
+
+If we call `navigation.navigate` with a route name that we haven't defined in a navigator, it'll print an error in development builds and nothing will happen in production builds. Said another way, we can only navigate to routes that have been defined on our navigator — we cannot navigate to an arbitrary component.
+
+:::
+
+So we now have a stack with two routes: 1) the `Home` route 2) the `Details` route. What would happen if we navigated to the `Details` route again, from the `Details` screen?
+
+## Navigate to a screen multiple times
+
+```js name="Navigate to a screen multiple times" snack static2dynamic
+import * as React from 'react';
+import { View, Text } from 'react-native';
+import {
+ createStaticNavigation,
+ useNavigation,
+} from '@react-navigation/native';
+import {
+ createNativeStackNavigator,
+ createNativeStackScreen,
+} from '@react-navigation/native-stack';
+import { Button } from '@react-navigation/elements';
+
+function HomeScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ Home Screen
+
+
+ );
+}
+
+// codeblock-focus-start
+function DetailsScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ Details Screen
+ // highlight-start
+
+ // highlight-end
+
+ );
+}
+// codeblock-focus-end
+
+const RootStack = createNativeStackNavigator({
+ initialRouteName: 'Home',
+ screens: {
+ Home: createNativeStackScreen({
+ screen: HomeScreen,
+ }),
+ Details: createNativeStackScreen({
+ screen: DetailsScreen,
+ }),
+ },
+});
+
+const Navigation = createStaticNavigation(RootStack);
+
+export default function App() {
+ return ;
+}
+```
+
+If you run this code, you'll notice that when you tap "Go to Details... again", it doesn't do anything! This is because we are already on the Details route. The `navigate` function roughly means "go to this screen", and if you are already on that screen then it makes sense that it would do nothing.
+
+Let's suppose that we actually _want_ to add another details screen. This is pretty common in cases where you pass in some unique data to each route (more on that later when we talk about `params`!). To do this, we can change `navigate` to `push`. This allows us to express the intent to add another route regardless of the existing navigation history.
+
+```js name="Navigate to a screen multiple times" snack static2dynamic
+import * as React from 'react';
+import { View, Text } from 'react-native';
+import {
+ createStaticNavigation,
+ useNavigation,
+} from '@react-navigation/native';
+import {
+ createNativeStackNavigator,
+ createNativeStackScreen,
+} from '@react-navigation/native-stack';
+import { Button } from '@react-navigation/elements';
+
+function HomeScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ Home Screen
+
+
+ );
+}
+
+function DetailsScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ Details Screen
+ // codeblock-focus-start
+
+ // codeblock-focus-end
+
+ );
+}
+
+const RootStack = createNativeStackNavigator({
+ initialRouteName: 'Home',
+ screens: {
+ Home: createNativeStackScreen({
+ screen: HomeScreen,
+ }),
+ Details: createNativeStackScreen({
+ screen: DetailsScreen,
+ }),
+ },
+});
+
+const Navigation = createStaticNavigation(RootStack);
+
+export default function App() {
+ return ;
+}
+```
+
+
+
+Each time you call `push` we add a new route to the navigation stack. When you call `navigate` it only pushes a new route if you're not already on that route.
+
+## Going back
+
+The header provided by the native stack navigator will automatically include a back button when it is possible to go back from the active screen (if there is only one screen in the navigation stack, there is nothing that you can go back to, and so there is no back button).
+
+Sometimes you'll want to be able to programmatically trigger this behavior, and for that, you can use `navigation.goBack()`.
+
+```js name="Going back" snack static2dynamic
+import * as React from 'react';
+import { View, Text } from 'react-native';
+import {
+ createStaticNavigation,
+ useNavigation,
+} from '@react-navigation/native';
+import {
+ createNativeStackNavigator,
+ createNativeStackScreen,
+} from '@react-navigation/native-stack';
+import { Button } from '@react-navigation/elements';
+
+function HomeScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ Home Screen
+
+
+ );
+}
+
+// codeblock-focus-start
+function DetailsScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ Details Screen
+
+ // highlight-start
+
+ // highlight-end
+
+ );
+}
+// codeblock-focus-end
+
+const RootStack = createNativeStackNavigator({
+ initialRouteName: 'Home',
+ screens: {
+ Home: createNativeStackScreen({
+ screen: HomeScreen,
+ }),
+ Details: createNativeStackScreen({
+ screen: DetailsScreen,
+ }),
+ },
+});
+
+const Navigation = createStaticNavigation(RootStack);
+
+export default function App() {
+ return ;
+}
+```
+
+
+
+:::note
+
+On Android, React Navigation hooks in to the hardware back button and fires the `goBack()` function for you when the user presses it, so it behaves as the user would expect.
+
+:::
+
+Another common requirement is to be able to go back _multiple_ screens -- for example, if you are several screens deep in a stack and want to dismiss all of them to go back to the first screen. In this case, we know that we want to go back to `Home` so we can use `popTo('Home')`. Another alternative would be `navigation.popToTop()`, which goes back to the first screen in the stack.
+
+```js name="Going back to specific screen" snack static2dynamic
+import * as React from 'react';
+import { View, Text } from 'react-native';
+import {
+ createStaticNavigation,
+ useNavigation,
+} from '@react-navigation/native';
+import {
+ createNativeStackNavigator,
+ createNativeStackScreen,
+} from '@react-navigation/native-stack';
+import { Button } from '@react-navigation/elements';
+
+function HomeScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ Home Screen
+
+
+ );
+}
+
+// codeblock-focus-start
+function DetailsScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ Details Screen
+
+
+ // highlight-start
+
+
+ // highlight-end
+
+ );
+}
+// codeblock-focus-end
+
+const RootStack = createNativeStackNavigator({
+ initialRouteName: 'Home',
+ screens: {
+ Home: createNativeStackScreen({
+ screen: HomeScreen,
+ }),
+ Details: createNativeStackScreen({
+ screen: DetailsScreen,
+ }),
+ },
+});
+
+const Navigation = createStaticNavigation(RootStack);
+
+export default function App() {
+ return ;
+}
+```
+
+
+
+## Summary
+
+- [`navigation.navigate('RouteName')`](navigation-object.md#navigate) pushes a new route to the native stack navigator if you're not already on that route.
+- We can call [`navigation.push('RouteName')`](stack-actions.md#push) as many times as we like and it will continue pushing routes.
+- The header bar will automatically show a back button, but you can programmatically go back by calling [`navigation.goBack()`](navigation-object.md#goback). On Android, the hardware back button just works as expected.
+- You can go back to an existing screen in the stack with [`navigation.popTo('RouteName')`](stack-actions.md#popto), and you can go back to the first screen in the stack with [`navigation.popToTop()`](stack-actions.md#poptotop).
+- The [`navigation`](navigation-object.md) object is available to all screen components with the [`useNavigation`](use-navigation.md) hook.
diff --git a/versioned_docs/version-8.x/navigation-actions.md b/versioned_docs/version-8.x/navigation-actions.md
new file mode 100755
index 00000000000..32b41af317c
--- /dev/null
+++ b/versioned_docs/version-8.x/navigation-actions.md
@@ -0,0 +1,1004 @@
+---
+id: navigation-actions
+title: CommonActions reference
+sidebar_label: CommonActions
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+A navigation action is an object containing at least a `type` property. Internally, the action can be handled by [routers](custom-routers.md) with the `getStateForAction` method to return a new state from an existing [navigation state](navigation-state.md).
+
+Each navigation actions can contain at least the following properties:
+
+- `type` (required) - A string that represents the name of the action.
+- `payload` (options) - An object containing additional information about the action. For example, it will contain `name` and `params` for `navigate`.
+- `source` (optional) - The key of the route which should be considered as the source of the action. This is used for some actions to determine which route to apply the action on. By default, `navigation.dispatch` adds the key of the route that dispatched the action.
+- `target` (optional) - The key of the [navigation state](navigation-state.md) the action should be applied on.
+
+It's important to highlight that dispatching a navigation action doesn't throw any error when the action is unhandled (similar to when you dispatch an action that isn't handled by a reducer in redux and nothing happens).
+
+## Common actions
+
+The library exports several action creators under the `CommonActions` namespace. You should use these action creators instead of writing action objects manually.
+
+### navigate
+
+The `navigate` action allows to navigate to a specific route. It takes the following arguments:
+
+- `name` - _string_ - A destination name of the screen in the current or a parent navigator.
+- `params` - _object_ - Params to use for the destination route.
+- `options` - Options object containing the following properties:
+ - `merge` - _boolean_ - Whether params should be merged with the existing route params, or replace them (when navigating to an existing screen). Defaults to `false`.
+ - `pop` - _boolean_ - Whether screens should be popped to navigate to a matching screen in the stack. Defaults to `false`.
+
+```js name="Common actions navigate" snack static2dynamic
+import * as React from 'react';
+import { View, Text } from 'react-native';
+import { Button } from '@react-navigation/elements';
+import {
+ createStaticNavigation,
+ useNavigation,
+ CommonActions,
+} from '@react-navigation/native';
+import { createStackNavigator } from '@react-navigation/stack';
+
+function HomeScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ Home!
+
+
+ );
+}
+
+function ProfileScreen({ route }) {
+ const navigation = useNavigation();
+ return (
+
+ Profile!
+ {route.params.user}'s profile
+
+ );
+}
+
+const RootStack = createStackNavigator({
+ screens: {
+ Home: HomeScreen,
+ Profile: ProfileScreen,
+ },
+});
+
+const Navigation = createStaticNavigation(RootStack);
+
+export default function App() {
+ return ;
+}
+```
+
+In a stack navigator ([stack](stack-navigator.md) or [native stack](native-stack-navigator.md)), calling `navigate` with a screen name will have the following behavior:
+
+- If you're already on a screen with the same name, it will update its params and not push a new screen.
+- If you're on a different screen, it will push the new screen onto the stack.
+- If the [`getId`](screen.md#id) prop is specified, and another screen in the stack has the same ID, it will bring that screen to focus and update its params instead.
+
+
+Advanced usage
+
+The `navigate` action can also accepts an object as the argument with the following properties:
+
+- `name` - _string_ - A destination name of the screen in the current or a parent navigator
+- `params` - _object_ - Params to use for the destination route.
+- `merge` - _boolean_ - Whether params should be merged with the existing route params, or replace them (when navigating to an existing screen). Defaults to `false`.
+- `pop` - _boolean_ - Whether screens should be popped to navigate to a matching screen in the stack. Defaults to `false`.
+- `path` - _string_ - The path (from deep link or universal link) to associate with the screen.
+
+This is primarily used internally to associate a path with a screen when it's from a URL.
+
+
+
+### reset
+
+The `reset` action allows to reset the [navigation state](navigation-state.md) to the given state. It takes the following arguments:
+
+- `state` - _object_ - The new [navigation state](navigation-state.md) object to use.
+
+```js name="Common actions reset" snack static2dynamic
+import * as React from 'react';
+import { View, Text } from 'react-native';
+import { Button } from '@react-navigation/elements';
+import {
+ createStaticNavigation,
+ useNavigation,
+ CommonActions,
+} from '@react-navigation/native';
+import { createStackNavigator } from '@react-navigation/stack';
+
+function HomeScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ Home!
+
+
+ );
+}
+
+function ProfileScreen({ route }) {
+ const navigation = useNavigation();
+ return (
+
+ Profile!
+ {route.params.user}'s profile
+
+
+ );
+}
+
+const RootStack = createStackNavigator({
+ screens: {
+ Home: HomeScreen,
+ Profile: ProfileScreen,
+ },
+});
+
+const Navigation = createStaticNavigation(RootStack);
+
+export default function App() {
+ return ;
+}
+```
+
+The state object specified in `reset` replaces the existing [navigation state](navigation-state.md) with the new one. This means that if you provide new route objects without a key, or route objects with a different key, it'll remove the existing screens for those routes and add new screens.
+
+If you want to preserve the existing screens but only want to modify the state, you can pass a function to `dispatch` where you can get the existing state. Then you can change it as you like (make sure not to mutate the existing state, but create new state object for your changes). and return a `reset` action with the desired state:
+
+```js
+import { CommonActions } from '@react-navigation/native';
+
+navigation.dispatch((state) => {
+ // Remove all the screens after `Profile`
+ const index = state.routes.findIndex((r) => r.name === 'Profile');
+ const routes = state.routes.slice(0, index + 1);
+
+ return CommonActions.reset({
+ ...state,
+ routes,
+ index: routes.length - 1,
+ });
+});
+```
+
+:::warning
+
+Consider the navigator's state object to be internal and subject to change in a minor release. Avoid using properties from the [navigation state](navigation-state.md) state object except `index` and `routes`, unless you really need it. If there is some functionality you cannot achieve without relying on the structure of the state object, please open an issue.
+
+:::
+
+#### Rewriting the history with `reset`
+
+Since the `reset` action can update the navigation state with a new state object, it can be used to rewrite the navigation history. However, rewriting the history to alter the back stack is not recommended in most cases:
+
+- It can lead to a confusing user experience, as users expect to be able to go back to the screen they were on before.
+- When supporting the Web platform, the browser's history will still reflect the old navigation state, so users will see the old screen if they use the browser's back button - resulting in 2 different experiences depending on which back button the user presses.
+
+So if you have such a use case, consider a different approach - e.g. updating the history once the user navigates back to the screen that has changed.
+
+### goBack
+
+The `goBack` action creator allows to go back to the previous route in history. It doesn't take any arguments.
+
+```js name="Common actions goBack" snack static2dynamic
+import * as React from 'react';
+import { View, Text } from 'react-native';
+import { Button } from '@react-navigation/elements';
+import {
+ createStaticNavigation,
+ useNavigation,
+ CommonActions,
+} from '@react-navigation/native';
+import { createStackNavigator } from '@react-navigation/stack';
+
+function HomeScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ Home!
+
+
+ );
+}
+
+function ProfileScreen({ route }) {
+ const navigation = useNavigation();
+ return (
+
+ Profile!
+ {route.params.user}'s profile
+
+
+ );
+}
+
+const RootStack = createStackNavigator({
+ screens: {
+ Home: HomeScreen,
+ Profile: ProfileScreen,
+ },
+});
+
+const Navigation = createStaticNavigation(RootStack);
+
+export default function App() {
+ return ;
+}
+```
+
+If you want to go back from a particular route, you can add a `source` property referring to the route key and a `target` property referring to the `key` of the navigator which contains the route:
+
+```js name="Common actions goBack" snack static2dynamic
+import * as React from 'react';
+import { View, Text } from 'react-native';
+import { Button } from '@react-navigation/elements';
+import {
+ createStaticNavigation,
+ useNavigation,
+ CommonActions,
+} from '@react-navigation/native';
+import { createStackNavigator } from '@react-navigation/stack';
+
+function HomeScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ Home!
+
+
+ );
+}
+
+function ProfileScreen({ route }) {
+ const navigation = useNavigation();
+ return (
+
+ Profile!
+ {route.params.user}'s profile
+
+
+ );
+}
+
+const RootStack = createStackNavigator({
+ screens: {
+ Home: HomeScreen,
+ Profile: ProfileScreen,
+ },
+});
+
+const Navigation = createStaticNavigation(RootStack);
+
+export default function App() {
+ return ;
+}
+```
+
+By default, the key of the route that dispatched the action is passed as the `source` property and the `target` property is `undefined`.
+
+### preload
+
+The `preload` action allows preloading a screen in the background before navigating to it. It takes the following arguments:
+
+- `name` - _string_ - A destination name of the screen in the current or a parent navigator.
+- `params` - _object_ - Params to use for the destination route.
+
+```js name="Common actions preload" snack static2dynamic
+import * as React from 'react';
+import { View, Text } from 'react-native';
+import {
+ createStaticNavigation,
+ useNavigation,
+ CommonActions,
+} from '@react-navigation/native';
+import { createStackNavigator } from '@react-navigation/stack';
+import { Button } from '@react-navigation/elements';
+
+function HomeScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ Home!
+
+
+
+ );
+}
+
+function ProfileScreen({ route }) {
+ const navigation = useNavigation();
+ const [startTime] = React.useState(Date.now());
+ const [endTime, setEndTime] = React.useState(null);
+
+ React.useEffect(() => {
+ const unsubscribe = navigation.addListener('focus', () => {
+ setEndTime(Date.now());
+ });
+
+ return () => {
+ unsubscribe();
+ };
+ }, [navigation]);
+
+ return (
+
+ Profile!
+ {route.params.user}'s profile
+ Preloaded for: {endTime ? endTime - startTime : 'N/A'}ms
+
+ );
+}
+
+const RootStack = createStackNavigator({
+ screens: {
+ Home: HomeScreen,
+ Profile: ProfileScreen,
+ },
+});
+
+const Navigation = createStaticNavigation(RootStack);
+
+export default function App() {
+ return ;
+}
+```
+
+Preloading a screen means that the screen will be rendered in the background. All the components in the screen will be mounted and the `useEffect` hooks will be called. This can be useful when you want to improve the perceived performance by hiding the delay in mounting heavy components or loading data.
+
+Depending on the navigator, `preload` may work differently:
+
+- In a stack navigator ([stack](stack-navigator.md), [native stack](native-stack-navigator.md)), the screen will be rendered off-screen and animated in when you navigate to it. If [`getId`](screen.md#id) is specified, it'll be used for the navigation to identify the preloaded screen.
+- In a tab or drawer navigator ([bottom tabs](bottom-tab-navigator.md), [material top tabs](material-top-tab-navigator.md), [drawer](drawer-navigator.md), etc.), the existing screen will be rendered as if `lazy` was set to `false`. Calling `preload` on a screen that is already rendered will not have any effect.
+
+When a screen is preloaded in a stack navigator, it will have a few limitations:
+
+- It can't dispatch navigation actions (e.g. `navigate`, `goBack`, etc.).
+- It can't update options with `navigation.setOptions`.
+- It can't listen to events from the navigator (e.g. `focus`, `tabPress`, etc.).
+
+The `navigation` object will be updated once you navigate to the screen. So if you have an event listener in a `useEffect` hook, and have a dependency on `navigation`, it will add any listeners when the screen is navigated to:
+
+```js
+React.useEffect(() => {
+ const unsubscribe = navigation.addListener('tabPress', () => {
+ // do something
+ });
+
+ return () => {
+ unsubscribe();
+ };
+}, [navigation]);
+```
+
+Similarly, for dispatching actions or updating options, you can check if the screen is focused before doing so:
+
+```js
+if (navigation.isFocused()) {
+ navigation.setOptions({ title: 'Updated title' });
+}
+```
+
+### setParams
+
+The `setParams` action allows to replace params for a certain route. It takes the following arguments:
+
+- `params` - _object_ - required - New params to be merged into existing route params.
+
+```js name="Common actions setParams" snack static2dynamic
+import * as React from 'react';
+import { View, Text } from 'react-native';
+import { Button } from '@react-navigation/elements';
+import {
+ createStaticNavigation,
+ useNavigation,
+ CommonActions,
+} from '@react-navigation/native';
+import { createStackNavigator } from '@react-navigation/stack';
+
+function HomeScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ Home!
+
+
+ );
+}
+
+function ProfileScreen({ route }) {
+ const navigation = useNavigation();
+ return (
+
+ Profile!
+ {route.params.user}'s profile
+
+
+ );
+}
+
+const RootStack = createStackNavigator({
+ screens: {
+ Home: HomeScreen,
+ Profile: ProfileScreen,
+ },
+});
+
+const Navigation = createStaticNavigation(RootStack);
+
+export default function App() {
+ return ;
+}
+```
+
+If you want to replace params for a particular route, you can add a `source` property referring to the route key:
+
+```js name="Common actions setParams" snack static2dynamic
+import * as React from 'react';
+import { View, Text } from 'react-native';
+import { Button } from '@react-navigation/elements';
+import {
+ createStaticNavigation,
+ useNavigation,
+ CommonActions,
+} from '@react-navigation/native';
+import { createStackNavigator } from '@react-navigation/stack';
+
+function HomeScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ Home!
+
+
+ );
+}
+
+function ProfileScreen({ route }) {
+ const navigation = useNavigation();
+ return (
+
+ Profile!
+ {route.params.user}'s profile
+
+
+ );
+}
+
+const RootStack = createStackNavigator({
+ screens: {
+ Home: HomeScreen,
+ Profile: ProfileScreen,
+ },
+});
+
+const Navigation = createStaticNavigation(RootStack);
+
+export default function App() {
+ return ;
+}
+```
+
+If the `source` property is explicitly set to `undefined`, it'll replace the params for the focused route.
+
+### replaceParams
+
+The `replaceParams` action allows to replace params for a certain route. It takes the following arguments:
+
+- `params` - _object_ - required - New params to use for the route.
+
+```js name="Common actions replaceParams" snack static2dynamic
+import * as React from 'react';
+import { View, Text } from 'react-native';
+import { Button } from '@react-navigation/elements';
+import {
+ createStaticNavigation,
+ useNavigation,
+ CommonActions,
+} from '@react-navigation/native';
+import { createStackNavigator } from '@react-navigation/stack';
+
+function HomeScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ Home!
+
+
+ );
+}
+
+function ProfileScreen({ route }) {
+ const navigation = useNavigation();
+ return (
+
+ Profile!
+ {route.params.user}'s profile
+
+
+ );
+}
+
+const RootStack = createStackNavigator({
+ screens: {
+ Home: HomeScreen,
+ Profile: ProfileScreen,
+ },
+});
+
+const Navigation = createStaticNavigation(RootStack);
+
+export default function App() {
+ return ;
+}
+```
+
+If you want to replace params for a particular route, you can add a `source` property referring to the route key:
+
+```js name="Common actions replaceParams" snack static2dynamic
+import * as React from 'react';
+import { View, Text } from 'react-native';
+import { Button } from '@react-navigation/elements';
+import {
+ createStaticNavigation,
+ useNavigation,
+ CommonActions,
+} from '@react-navigation/native';
+import { createStackNavigator } from '@react-navigation/stack';
+
+function HomeScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ Home!
+
+
+
+ );
+}
+
+function ProfileScreen({ route }) {
+ const navigation = useNavigation();
+ return (
+
+ Profile!
+ {route.params.user}'s profile
+
+
+ );
+}
+
+const RootStack = createStackNavigator({
+ screens: {
+ Home: HomeScreen,
+ Profile: ProfileScreen,
+ },
+});
+
+const Navigation = createStaticNavigation(RootStack);
+
+export default function App() {
+ return ;
+}
+```
+
+If the `source` property is explicitly set to `undefined`, it'll replace the params for the focused route.
+
+### pushParams
+
+The `pushParams` action allows to add a new entry to the history stack with new params. It takes the following arguments:
+
+- `params` - _object_ - required - New params to use for the route.
+
+```js name="Common actions pushParams" snack static2dynamic
+import * as React from 'react';
+import { View, Text } from 'react-native';
+import { Button } from '@react-navigation/elements';
+import {
+ createStaticNavigation,
+ useNavigation,
+ CommonActions,
+} from '@react-navigation/native';
+import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
+
+function ProductListScreen({ route }) {
+ const navigation = useNavigation();
+ const filter = route.params?.filter || 'all';
+
+ return (
+
+ Product List
+ Filter: {filter}
+
+
+
+
+ );
+}
+
+function SettingsScreen() {
+ return (
+
+ Settings
+
+ );
+}
+
+const HomeTabs = createBottomTabNavigator({
+ screenOptions: {
+ backBehavior: 'history',
+ },
+ screens: {
+ ProductList: ProductListScreen,
+ Settings: SettingsScreen,
+ },
+});
+
+const Navigation = createStaticNavigation(HomeTabs);
+
+export default function App() {
+ return ;
+}
+```
+
+Unlike `setParams`, the `pushParams` action does not merge the new params with the existing ones. Instead, it uses the new params object as-is and adds a new entry to the history stack.
+
+The action works in all navigators, such as stack, tab, and drawer. This allows adding a new entry to the history stack without needing to push a new screen instance.
+
+This can be useful in various scenarios:
+
+- 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.
+
+If you want to push params for a particular route, you can add a `source` property referring to the route key:
+
+```js
+navigation.dispatch({
+ ...CommonActions.pushParams({ filter: 'popular' }),
+ source: route.key,
+});
+```
+
+If the `source` property is explicitly set to `undefined`, it'll push params for the focused route.
diff --git a/versioned_docs/version-8.x/navigation-container.md b/versioned_docs/version-8.x/navigation-container.md
new file mode 100644
index 00000000000..536dd908ae2
--- /dev/null
+++ b/versioned_docs/version-8.x/navigation-container.md
@@ -0,0 +1,1122 @@
+---
+id: navigation-container
+title: NavigationContainer
+sidebar_label: NavigationContainer
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+The `NavigationContainer` is responsible for managing your app's navigation state and linking your top-level navigator to the app environment.
+
+The container takes care of platform specific integration and provides various useful functionality:
+
+1. Deep link integration with the [`linking`](#linking) prop.
+2. Notify state changes for [screen tracking](screen-tracking.md), [state persistence](state-persistence.md) etc.
+3. Handle system back button on Android by using the [`BackHandler`](https://reactnative.dev/docs/backhandler) API from React Native.
+
+Usage:
+
+
+
+
+When using the static API, the component returned by [`createStaticNavigation`](static-configuration.md#createstaticnavigation) is equivalent to the `NavigationContainer` component.
+
+```js
+import { createStaticNavigation } from '@react-navigation/native';
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+
+const Stack = createNativeStackNavigator({
+ screens: {
+ /* ... */
+ },
+});
+
+const Navigation = createStaticNavigation(Stack);
+
+export default function App() {
+ return ;
+}
+```
+
+
+
+
+```js
+import { NavigationContainer } from '@react-navigation/native';
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+
+const Stack = createNativeStackNavigator();
+
+export default function App() {
+ return (
+
+ {/* ... */}
+
+ );
+}
+```
+
+
+
+
+## Ref
+
+It's possible to pass a [`ref`](https://react.dev/learn/referencing-values-with-refs) to the container to get access to various helper methods, for example, dispatch navigation actions. This should be used in rare cases when you don't have access to the [`navigation` object](navigation-object.md), such as a Redux middleware.
+
+Example:
+
+
+
+
+```js name="Using refs" snack
+import * as React from 'react';
+import { View, Text } from 'react-native';
+import { Button } from '@react-navigation/elements';
+// codeblock-focus-start
+import {
+ createStaticNavigation,
+ useNavigationContainerRef,
+} from '@react-navigation/native';
+// codeblock-focus-end
+import { createStackNavigator } from '@react-navigation/stack';
+
+const Stack = createStackNavigator({
+ initialRouteName: 'Empty',
+ screens: {
+ Empty: () => ,
+ Home: HomeScreen,
+ },
+});
+
+function HomeScreen() {
+ return (
+
+ Home Screen
+
+ );
+}
+
+const Navigation = createStaticNavigation(Stack);
+
+// codeblock-focus-start
+
+export default function App() {
+ // highlight-next-line
+ const navigationRef = useNavigationContainerRef(); // You can also use a regular ref with `React.useRef()`
+
+ return (
+
+
+
+
+ );
+}
+// codeblock-focus-end
+```
+
+
+
+
+```js name="Using refs" snack
+import * as React from 'react';
+import { View, Text } from 'react-native';
+import { Button } from '@react-navigation/elements';
+// codeblock-focus-start
+import {
+ NavigationContainer,
+ useNavigationContainerRef,
+} from '@react-navigation/native';
+// codeblock-focus-end
+import { createStackNavigator } from '@react-navigation/stack';
+
+const Stack = createStackNavigator();
+
+function HomeScreen() {
+ return (
+
+ Home Screen
+
+ );
+}
+
+// codeblock-focus-start
+
+export default function App() {
+ // highlight-next-line
+ const navigationRef = useNavigationContainerRef(); // You can also use a regular ref with `React.useRef()`
+
+ return (
+
+
+
+
+ } />
+
+
+
+
+ );
+}
+// codeblock-focus-end
+```
+
+
+
+
+If you're using a regular ref object, keep in mind that the ref may be initially `null` in some situations (such as when linking is enabled). To make sure that the ref is initialized, you can use the [`onReady`](#onready) callback to get notified when the navigation container finishes mounting.
+
+Check how to setup `ref` with TypeScript [here](typescript.md#annotating-ref-on-navigationcontainer).
+
+See the [Navigating without the navigation prop](navigating-without-navigation-prop.md) guide for more details.
+
+### Methods on the ref
+
+The ref object includes all of the common navigation methods such as `navigate`, `goBack` etc. See [docs for `CommonActions`](navigation-actions.md) for more details.
+
+Example:
+
+```js
+navigationRef.navigate(name, params);
+```
+
+All of these methods will act as if they were called inside the currently focused screen. It's important note that there must be a navigator rendered to handle these actions.
+
+In addition to these methods, the ref object also includes the following special methods:
+
+#### `isReady`
+
+The `isReady` method returns a `boolean` indicating whether the navigation tree is ready. The navigation tree is ready when the `NavigationContainer` contains at least one navigator and all of the navigators have finished mounting.
+
+This can be used to determine whether it's safe to dispatch navigation actions without getting an error. See [handling initialization](navigating-without-navigation-prop.md#handling-initialization) for more details.
+
+#### `resetRoot`
+
+The `resetRoot` method lets you reset the state of the navigation tree to the specified state object:
+
+```js
+navigationRef.resetRoot({
+ index: 0,
+ routes: [{ name: 'Profile' }],
+});
+```
+
+Unlike the `reset` method, this acts on the root navigator instead of navigator of the currently focused screen.
+
+#### `getRootState`
+
+The `getRootState` method returns a [navigation state](navigation-state.md) object containing the navigation states for all navigators in the navigation tree:
+
+```js
+const state = navigationRef.getRootState();
+```
+
+Note that the returned `state` object will be `undefined` if there are no navigators currently rendered.
+
+#### `getCurrentRoute`
+
+The `getCurrentRoute` method returns the route object for the currently focused screen in the whole navigation tree:
+
+```js
+const route = navigationRef.getCurrentRoute();
+```
+
+Note that the returned `route` object will be `undefined` if there are no navigators currently rendered.
+
+#### `getCurrentOptions`
+
+The `getCurrentOptions` method returns the options for the currently focused screen in the whole navigation tree:
+
+```js
+const options = navigationRef.getCurrentOptions();
+```
+
+Note that the returned `options` object will be `undefined` if there are no navigators currently rendered.
+
+#### `addListener`
+
+The `addListener` method lets you listen to the following events:
+
+##### `ready`
+
+The event is triggered when the navigation tree is ready. This is useful for cases where you want to wait until the navigation tree is mounted:
+
+```js
+const unsubscribe = navigationRef.addListener('ready', () => {
+ // Get the initial state of the navigation tree
+ console.log(navigationRef.getRootState());
+});
+```
+
+This is analogous to the [`onReady`](#onready) method.
+
+##### `state`
+
+The event is triggered whenever the [navigation state](navigation-state.md) changes in any navigator in the navigation tree:
+
+```js
+const unsubscribe = navigationRef.addListener('state', (e) => {
+ // You can get the raw navigation state (partial state object of the root navigator)
+ console.log(e.data.state);
+
+ // Or get the full state object with `getRootState()`
+ console.log(navigationRef.getRootState());
+});
+```
+
+This is analogous to the [`onStateChange`](#onstatechange) method. The only difference is that the `e.data.state` object might contain partial state object unlike the `state` argument in `onStateChange` which will always contain the full state object.
+
+##### `options`
+
+The event is triggered whenever the options change for the currently focused screen in the navigation tree:
+
+```js
+const unsubscribe = navigationRef.addListener('options', (e) => {
+ // You can get the new options for the currently focused screen
+ console.log(e.data.options);
+});
+```
+
+## Props
+
+### `initialState`
+
+Prop that accepts initial state for the navigator. This can be useful for cases such as deep linking, state persistence etc.
+
+Example:
+
+
+
+
+```js
+
+```
+
+
+
+
+```js
+
+ {/* ... */}
+
+```
+
+
+
+
+See [Navigation state reference](navigation-state.md) for more details on the structure of the state object.
+
+Providing a custom initial state object will override the initial state object obtained via linking configuration or from browser's URL. If you're providing an initial state object, make sure that you don't pass it on web and that there's no deep link to handle.
+
+Example:
+
+```js
+const initialUrl = await Linking.getInitialURL();
+
+if (Platform.OS !== 'web' && initialUrl == null) {
+ // Only restore state if there's no deep link and we're not on web
+}
+```
+
+See [state persistence guide](state-persistence.md) for more details on how to persist and restore state.
+
+### `onStateChange`
+
+:::warning
+
+Consider the navigator's state object to be internal and subject to change in a minor release. Avoid using properties from the [navigation state](navigation-state.md) state object except `index` and `routes`, unless you really need it. If there is some functionality you cannot achieve without relying on the structure of the state object, please open an issue.
+
+:::
+
+Function that gets called every time [navigation state](navigation-state.md) changes. It receives the new navigation state as the argument.
+
+You can use it to track the focused screen, persist the navigation state etc.
+
+Example:
+
+
+
+```js
+ console.log('New state is', state)}
+/>
+```
+
+
+
+
+```js
+ console.log('New state is', state)}
+>
+ {/* ... */}
+
+```
+
+
+
+
+### `onReady`
+
+Function which is called after the navigation container and all its children finish mounting for the first time. You can use it for:
+
+- Making sure that the `ref` is usable. See [docs regarding initialization of the ref](navigating-without-navigation-prop.md#handling-initialization) for more details.
+- Hiding your native splash screen
+
+Example:
+
+
+
+
+```js
+ console.log('Navigation container is ready')}
+/>
+```
+
+
+
+
+```js
+ console.log('Navigation container is ready')}
+>
+ {/* ... */}
+
+```
+
+
+
+
+This callback won't fire if there are no navigators rendered inside the container.
+
+The current status can be obtained with the [`isReady`](#isready) method on the ref.
+
+### `onUnhandledAction`
+
+Function which is called when a navigation action is not handled by any of the navigators.
+
+By default, React Navigation will show a development-only error message when an action is not handled. You can override the default behavior by providing a custom function.
+
+Example:
+
+
+
+
+```js
+ console.error('Unhandled action', action)}
+/>
+```
+
+
+
+
+```js
+ console.error('Unhandled action', action)}
+>
+ {/* ... */}
+
+```
+
+
+
+
+### `linking`
+
+Configuration for linking integration used for deep linking, URL support in browsers etc.
+
+Example:
+
+
+
+
+```js
+import { createStaticNavigation } from '@react-navigation/native';
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+
+const RootStack = createNativeStackNavigator({
+ screens: {
+ Home: {
+ screen: Home,
+ linking: {
+ path: 'feed/:sort',
+ },
+ },
+ },
+});
+
+const Navigation = createStaticNavigation(RootStack);
+
+function App() {
+ const linking = {
+ prefixes: ['https://example.com', 'example://'],
+ };
+
+ return (
+ Loading...}
+ />
+ );
+}
+```
+
+
+
+
+```js
+import { NavigationContainer } from '@react-navigation/native';
+
+function App() {
+ const linking = {
+ prefixes: ['https://example.com', 'example://'],
+ config: {
+ screens: {
+ Home: 'feed/:sort',
+ },
+ },
+ };
+
+ return (
+ Loading...}
+ >
+ {/* content */}
+
+ );
+}
+```
+
+
+
+See [configuring links guide](configuring-links.md) for more details on how to configure deep links and URL integration.
+
+#### Options
+
+##### `linking.prefixes`
+
+URL prefixes to handle. You can provide multiple prefixes to support custom schemes as well as [universal links](https://developer.apple.com/ios/universal-links/).
+
+Only URLs matching these prefixes will be handled. The prefix will be stripped from the URL before parsing.
+
+Example:
+
+
+
+
+```js
+Loading...}
+/>
+```
+
+
+
+
+```js
+
+ {/* content */}
+
+```
+
+
+
+
+This is only supported on iOS and Android.
+
+##### `linking.config`
+
+Config to fine-tune how to parse the path.
+
+When using dynamic API, the config object should represent the structure of the navigators in the app.
+
+See the [configuring links guide](configuring-links.md) for more details on how to configure deep links and URL integration.
+
+##### `linking.enabled`
+
+Optional boolean to enable or disable the linking integration. Defaults to `true` if the `linking` prop is specified.
+
+When using the static API, it's possible to pass `'auto'` to automatically generate the config based on the navigator's structure. See the [configuring links guide](configuring-links.md) for more details.
+
+##### `linking.getInitialURL`
+
+By default, linking integrates with React Native's `Linking` API and uses `Linking.getInitialURL()` to provide built-in support for deep linking. However, you might also want to handle links from other sources, such as [Branch](https://help.branch.io/developers-hub/docs/react-native), or push notifications using [Firebase](https://rnfirebase.io/messaging/notifications) etc.
+
+You can provide a custom `getInitialURL` function where you can return the link which we should use as the initial URL. The `getInitialURL` function should return a `string` if there's a URL to handle, otherwise `undefined`.
+
+For example, you could do something like following to handle both deep linking and [Firebase notifications](https://rnfirebase.io/messaging/notifications):
+
+
+
+
+```js
+import messaging from '@react-native-firebase/messaging';
+
+;
+```
+
+
+
+
+```js
+import messaging from '@react-native-firebase/messaging';
+
+
+ {/* content */}
+;
+```
+
+
+
+
+This option is not available on Web.
+
+##### `linking.subscribe`
+
+Similar to [`getInitialURL`](#linkinggetinitialurl), you can provide a custom `subscribe` function to handle any incoming links instead of the default deep link handling. The `subscribe` function will receive a listener as the argument and you can call it with a URL string whenever there's a new URL to handle. It should return a cleanup function where you can unsubscribe from any event listeners that you have setup.
+
+For example, you could do something like following to handle both deep linking and [Firebase notifications](https://rnfirebase.io/messaging/notifications):
+
+
+
+
+```js
+import messaging from '@react-native-firebase/messaging';
+
+ listener(url);
+
+ // Listen to incoming links from deep linking
+ const subscription = Linking.addEventListener('url', onReceiveURL);
+
+ // Listen to firebase push notifications
+ const unsubscribeNotification = messaging().onNotificationOpenedApp(
+ (message) => {
+ const url = message.data?.url;
+
+ if (url) {
+ // Any custom logic to check whether the URL needs to be handled
+ //...
+
+ // Call the listener to let React Navigation handle the URL
+ listener(url);
+ }
+ }
+ );
+
+ return () => {
+ // Clean up the event listeners
+ subscription.remove();
+ unsubscribeNotification();
+ };
+ },
+ // highlight-end
+ }}
+/>
+```
+
+
+
+
+```js
+import messaging from '@react-native-firebase/messaging';
+
+ listener(url);
+
+ // Listen to incoming links from deep linking
+ const subscription = Linking.addEventListener('url', onReceiveURL);
+
+ // Listen to firebase push notifications
+ const unsubscribeNotification = messaging().onNotificationOpenedApp(
+ (message) => {
+ const url = message.data?.url;
+
+ if (url) {
+ // Any custom logic to check whether the URL needs to be handled
+ //...
+
+ // Call the listener to let React Navigation handle the URL
+ listener(url);
+ }
+ }
+ );
+
+ return () => {
+ // Clean up the event listeners
+ subscription.remove();
+ unsubscribeNotification();
+ };
+ },
+ // highlight-end
+ }}
+>
+ {/* content */}
+;
+```
+
+
+
+
+This option is not available on Web.
+
+##### `linking.getStateFromPath`
+
+React Navigation handles deep links and [URLs on Web](web-support.md) by parsing the path to a [navigation state](navigation-state.md) object based on the [linking config](#linkingconfig). You can optionally override the way the parsing happens by providing your own `getStateFromPath` function.
+
+Example:
+
+
+
+
+```js
+
+```
+
+
+
+
+```js
+
+ {/* content */}
+
+```
+
+
+
+
+##### `linking.getPathFromState`
+
+On Web, React Navigation automatically updates the [URL in the browser's address bar](web-support.md) to match the current navigation state by serializing the state to a path based on the [linking config](#linkingconfig). You can optionally override the way the serialization happens by providing your own `getPathFromState` function.
+
+If you provide a custom [`getStateFromPath`](#linkinggetstatefrompath), you should also provide a custom `getPathFromState` to ensure that the parsing and serialization are consistent with each other for Web support to work correctly.
+
+Example:
+
+
+
+
+```js
+
+```
+
+
+
+
+```js
+
+ {/* content */}
+
+```
+
+
+
+
+##### `linking.getActionFromState`
+
+The state parsed with [`getStateFromPath`](#linkinggetstatefrompath) is used as the initial state of the navigator. But for subsequent deep links and URLs, the state is converted to a navigation action. Typically it is a [`navigate`](navigation-actions.md#navigate) action.
+
+You can provide a custom `getActionFromState` function to customize how the state is converted to an action.
+
+Example:
+
+
+
+
+```js
+
+```
+
+
+
+
+```js
+
+ {/* content */}
+
+```
+
+
+
+
+### `fallback`
+
+React Element to use as a fallback while we resolve deep links. Defaults to `null`.
+
+
+
+
+```js
+Loading...}
+/>
+```
+
+
+
+
+```js
+Loading...}
+>
+ {/* content */}
+
+```
+
+
+
+
+If you have a native splash screen, please use [`onReady`](#onready) instead of `fallback` prop.
+
+### `documentTitle`
+
+By default, React Navigation automatically updates the document title on Web to match the `title` option of the focused screen. You can disable it or customize it using this prop. It accepts a configuration object with the following options:
+
+#### `documentTitle.enabled`
+
+Whether document title handling should be enabled. Defaults to `true`.
+
+#### `documentTitle.formatter`
+
+Custom formatter to use if you want to customize the title text. Defaults to:
+
+```js
+(options, route) => options?.title ?? route?.name;
+```
+
+Example:
+
+
+
+
+```js
+
+ `${options?.title ?? route?.name} - My Cool App`,
+ }}
+ // highlight-end
+/>
+```
+
+
+
+
+```js
+
+ `${options?.title ?? route?.name} - My Cool App`,
+ }}
+ // highlight-end
+>
+ {/* content */}
+
+```
+
+
+
+
+### `theme`
+
+Custom theme to use for the navigation components such as the header, tab bar etc. See [theming guide](themes.md) for more details and usage guide.
+
+### `direction`
+
+The direction of the text configured in the app. Defaults to `'rtl'` when `I18nManager.getConstants().isRTL` returns `true`, otherwise `'ltr'`.
+
+Supported values:
+
+- `'ltr'`: Left-to-right text direction for languages like English, French etc.
+- `'rtl'`: Right-to-left text direction for languages like Arabic, Hebrew etc.
+
+Example:
+
+
+
+
+```js
+
+```
+
+
+
+
+```js
+
+ {/* content */}
+
+```
+
+
+
+
+This is used in various navigators to adjust the content according to the text direction, for example, the drawer in the [drawer navigator](drawer-navigator.md) is positioned on the right side in RTL languages.
+
+This prop informs React Navigation about the text direction in the app, it doesn't change the text direction by itself. If you intend to support RTL languages, it's important to set this prop to the correct value that's configured in the app. If it doesn't match the actual text direction, the layout might be incorrect.
+
+On the Web, it may also be necessary to set the `dir` attribute on the root element of the app to ensure that the text direction is correct:
+
+```html
+
+
+
+```
+
+The `direction` will be available to use in your own components via the `useLocale` hook:
+
+```js
+import { useLocale } from '@react-navigation/native';
+
+function MyComponent() {
+ const { direction } = useLocale();
+
+ // Use the direction
+}
+```
+
+### `navigationInChildEnabled`
+
+:::warning
+
+This prop exists for backward compatibility reasons. It's not recommended to use it in new projects. It will be removed in a future release.
+
+:::
+
+In previous versions of React Navigation, it was possible to navigate to a screen in a nested navigator without specifying the name of the parent screen, i.e. `navigation.navigate(ScreenName)` instead of `navigation.navigate(ParentScreenName, { screen: ScreenName })`.
+
+However, it has a few issues:
+
+- It only works if the navigator is already mounted - making navigation coupled to other logic.
+- It doesn't work with the TypeScript types.
+
+The `navigationInChildEnabled` prop allows you to opt-in to this behavior to make it easier to migrate legacy code. It's disabled by default.
+
+For new code, see [navigating to a screen in a nested navigator](nesting-navigators.md#navigating-to-a-screen-in-a-nested-navigator) instead.
+
+## Independent navigation containers
+
+:::warning
+
+This is an advanced use case. Don't use this unless you are 100% sure that you need it.
+
+:::
+
+In most apps, there will be only a single `NavigationContainer`. Nesting multiple `NavigationContainer`s will throw an error. However, in rare cases, it may be useful to have multiple independent navigation trees, e.g. including a mini-app inside a larger app.
+
+You can wrap the nested `NavigationContainer` with the `NavigationIndependentTree` component to make it independent from the parent navigation tree:
+
+
+
+
+```js
+import {
+ createStaticNavigation,
+ NavigationIndependentTree,
+} from '@react-navigation/native';
+
+/* content */
+
+const Navigation = createStaticNavigation(RootStack);
+
+function NestedApp() {
+ return (
+ // highlight-start
+
+
+
+ // highlight-end
+ );
+}
+```
+
+
+
+
+```js
+import {
+ NavigationContainer,
+ NavigationIndependentTree,
+} from '@react-navigation/native';
+
+function NestedApp() {
+ return (
+ // highlight-start
+
+ {/* content */}
+
+ // highlight-end
+ );
+}
+```
+
+
+
+
+Doing this disconnects any children navigators from the parent container and doesn't allow navigation between them.
+
+Avoid using this if you need to integrate with third-party components such as modals or bottom sheets. Consider using a [custom navigator](custom-navigators.md) instead.
diff --git a/versioned_docs/version-8.x/navigation-context.md b/versioned_docs/version-8.x/navigation-context.md
new file mode 100755
index 00000000000..afb0b0153f1
--- /dev/null
+++ b/versioned_docs/version-8.x/navigation-context.md
@@ -0,0 +1,77 @@
+---
+id: navigation-context
+title: NavigationContext
+sidebar_label: NavigationContext
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+`NavigationContext` provides the `navigation` object (same object as the [navigation](navigation-object.md) prop). In fact, [useNavigation](use-navigation.md) uses this context to get the `navigation` prop.
+
+Most of the time, you won't use `NavigationContext` directly, as the provided `useNavigation` covers most use cases. But just in case you have something else in mind, `NavigationContext` is available for you to use.
+
+Example:
+
+```js name="Navigation context" snack static2dynamic
+import * as React from 'react';
+import { View, Text } from 'react-native';
+import { Button } from '@react-navigation/elements';
+// codeblock-focus-start
+import { NavigationContext } from '@react-navigation/native';
+// codeblock-focus-end
+import {
+ useNavigation,
+ createStaticNavigation,
+} from '@react-navigation/native';
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+
+function HomeScreen() {
+ return ;
+}
+
+// codeblock-focus-start
+
+function SomeComponent() {
+ // We can access navigation object via context
+ const navigation = React.useContext(NavigationContext);
+ // codeblock-focus-end
+
+ return (
+
+ Some component inside HomeScreen
+
+
+ );
+ // codeblock-focus-start
+}
+// codeblock-focus-end
+
+function ProfileScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+
+
+ );
+}
+
+const RootStack = createNativeStackNavigator({
+ initialRouteName: 'Home',
+ screens: {
+ Home: HomeScreen,
+ Profile: ProfileScreen,
+ },
+});
+
+const Navigation = createStaticNavigation(RootStack);
+
+function App() {
+ return ;
+}
+
+export default App;
+```
diff --git a/versioned_docs/version-8.x/navigation-events.md b/versioned_docs/version-8.x/navigation-events.md
new file mode 100644
index 00000000000..2aabd1458fb
--- /dev/null
+++ b/versioned_docs/version-8.x/navigation-events.md
@@ -0,0 +1,473 @@
+---
+id: navigation-events
+title: Navigation events
+sidebar_label: Navigation events
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+You can listen to various events emitted by React Navigation to get notified of certain events, and in some cases, override the default action. There are few core events such as `focus`, `blur` etc. (documented below) that work for every navigator, as well as navigator specific events that work only for certain navigators.
+
+Apart from the core events, each navigator can emit their own custom events. For example, stack navigator emits `transitionStart` and `transitionEnd` events, tab navigator emits `tabPress` event etc. You can find details about the events emitted on the individual navigator's documentation.
+
+## Core events
+
+Following are the events available in every navigator:
+
+### `focus`
+
+This event is emitted when the screen comes into focus.
+
+For most cases, the [`useFocusEffect`](use-focus-effect.md) hook might be appropriate than adding the listener manually. See [this guide](function-after-focusing-screen.md) for more details to decide which API you should use.
+
+### `blur`
+
+This event is emitted when the screen goes out of focus.
+
+:::note
+
+In some cases, such as going back from a screen in [native-stack navigator](native-stack-navigator.md), the screen may not receive the `blur` event as the screen is unmounted immediately. For cleaning up resources, it's recommended to use the cleanup function of [`useFocusEffect`](use-focus-effect.md) hook instead that considers both blur and unmounting of the screen.
+
+:::
+
+### `state`
+
+This event is emitted when the navigator's state changes. This event receives the navigator's state in the event data (`event.data.state`).
+
+### `beforeRemove`
+
+This event is emitted when the user is leaving the screen due to a navigation action. It is possible to prevent the user from leaving the screen by calling `e.preventDefault()` in the event listener.
+
+```js
+React.useEffect(
+ () =>
+ navigation.addListener('beforeRemove', (e) => {
+ if (!hasUnsavedChanges) {
+ return;
+ }
+
+ // Prevent default behavior of leaving the screen
+ e.preventDefault();
+
+ // Prompt the user before leaving the screen
+ Alert.alert(
+ 'Discard changes?',
+ 'You have unsaved changes. Are you sure to discard them and leave the screen?',
+ [
+ {
+ text: "Don't leave",
+ style: 'cancel',
+ onPress: () => {
+ // Do nothing
+ },
+ },
+ {
+ text: 'Discard',
+ style: 'destructive',
+ // If the user confirmed, then we dispatch the action we blocked earlier
+ // This will continue the action that had triggered the removal of the screen
+ onPress: () => navigation.dispatch(e.data.action),
+ },
+ ]
+ );
+ }),
+ [navigation, hasUnsavedChanges]
+);
+```
+
+:::warning
+
+Preventing the action in this event doesn't work properly with [`@react-navigation/native-stack`](native-stack-navigator.md). We recommend using the [`usePreventRemove` hook](preventing-going-back.md) instead.
+
+:::
+
+## Listening to events
+
+There are multiple ways to listen to events from the navigators. Each callback registered as an event listener receives an event object as its argument. The event object contains few properties:
+
+- `data` - Additional data regarding the event passed by the navigator. This can be `undefined` if no data was passed.
+- `target` - The route key for the screen that should receive the event. For some events, this maybe `undefined` if the event wasn't related to a specific screen.
+- `preventDefault` - For some events, there may be a `preventDefault` method on the event object. Calling this method will prevent the default action performed by the event (such as switching tabs on `tabPress`). Support for preventing actions are only available for certain events like `tabPress` and won't work for all events.
+
+You can listen to events with the following APIs:
+
+### `navigation.addListener`
+
+Inside a screen, you can add listeners on the `navigation` object with the `addListener` method. The `addListener` method takes 2 arguments: type of the event, and a callback to be called on the event. It returns a function that can be called to unsubscribe from the event.
+
+Example:
+
+```js
+const unsubscribe = navigation.addListener('tabPress', (e) => {
+ // Prevent default action
+ e.preventDefault();
+});
+```
+
+Normally, you'd add an event listener in `React.useEffect` for function components. For example:
+
+
+
+
+```js name="navigation.addListener with focus" snack
+import * as React from 'react';
+import { View, Text } from 'react-native';
+import { Button } from '@react-navigation/elements';
+import {
+ createStaticNavigation,
+ useNavigation,
+} from '@react-navigation/native';
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+
+function SettingsScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ Settings Screen
+
+
+ );
+}
+
+// codeblock-focus-start
+function ProfileScreen() {
+ const navigation = useNavigation();
+
+ React.useEffect(() => {
+ const unsubscribe = navigation.addListener('focus', () => {
+ // Screen was focused
+ });
+ return unsubscribe;
+ }, [navigation]);
+
+ React.useEffect(() => {
+ const unsubscribe = navigation.addListener('blur', () => {
+ // Screen was unfocused
+ });
+ return unsubscribe;
+ }, [navigation]);
+
+ // Rest of the component
+ // codeblock-focus-end
+ return (
+
+ Profile Screen
+
+
+ );
+ // codeblock-focus-start
+}
+// codeblock-focus-end
+
+const SettingsStack = createNativeStackNavigator({
+ screens: {
+ Settings: SettingsScreen,
+ Profile: ProfileScreen,
+ },
+});
+
+const Navigation = createStaticNavigation(SettingsStack);
+
+export default function App() {
+ return ;
+}
+```
+
+
+
+
+```js name="navigation.addListener with focus" snack
+import * as React from 'react';
+import { View, Text } from 'react-native';
+import { Button } from '@react-navigation/elements';
+import { NavigationContainer, useNavigation } from '@react-navigation/native';
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+
+function SettingsScreen({ navigation }) {
+ return (
+
+ Settings Screen
+
+
+ );
+}
+
+// codeblock-focus-start
+function ProfileScreen({ navigation }) {
+ React.useEffect(() => {
+ const unsubscribe = navigation.addListener('focus', () => {
+ // Screen was focused
+ });
+ return unsubscribe;
+ }, [navigation]);
+
+ React.useEffect(() => {
+ const unsubscribe = navigation.addListener('blur', () => {
+ // Screen was unfocused
+ });
+ return unsubscribe;
+ }, [navigation]);
+
+ // Rest of the component
+ // codeblock-focus-end
+ return (
+
+ Profile Screen
+
+
+ );
+ // codeblock-focus-start
+}
+// codeblock-focus-end
+
+const SettingsStack = createNativeStackNavigator();
+
+export default function App() {
+ return (
+
+
+
+
+
+
+ );
+}
+```
+
+
+
+
+The `unsubscribe` function can be returned as the cleanup function in the effect.
+
+For class components, you can add the event in the `componentDidMount` lifecycle method and unsubscribe in `componentWillUnmount`:
+
+```js
+class Profile extends React.Component {
+ componentDidMount() {
+ this._unsubscribe = navigation.addListener('focus', () => {
+ // do something
+ });
+ }
+
+ componentWillUnmount() {
+ this._unsubscribe();
+ }
+
+ render() {
+ // Content of the component
+ }
+}
+```
+
+Keep in mind that you can only listen to events from the immediate navigator with `addListener`. For example, if you try to add a listener in a screen that's inside a stack that's nested in a tab, it won't get the `tabPress` event. If you need to listen to an event from a parent navigator, you may use [`navigation.getParent`](navigation-object.md#getparent) to get a reference to the parent screen's navigation object and add a listener.
+
+```js
+const unsubscribe = navigation
+ .getParent('MyTabs')
+ .addListener('tabPress', (e) => {
+ // Do something
+ });
+```
+
+Here `'MyTabs'` refers to the name of the parent screen in the tab navigator whose event you want to listen to.
+
+:::warning
+
+The component needs to be rendered for the listeners to be added in `useEffect` or `componentDidMount`. Navigators such as [bottom tabs](bottom-tab-navigator.md) and [drawer](drawer-navigator.md) lazily render the screen after navigating to it. So if your listener is not being called, double check that the component is rendered.
+
+:::
+
+### `listeners` prop on `Screen`
+
+Sometimes you might want to add a listener from the component where you defined the navigator rather than inside the screen. You can use the `listeners` prop on the `Screen` component to add listeners. The `listeners` prop takes an object with the event names as keys and the listener callbacks as values.
+
+Example:
+
+
+
+
+```js
+const Tab = createBottomTabNavigatior({
+ screens: {
+ Chat: {
+ screen: Chat,
+ listeners: {
+ tabPress: (e) => {
+ // Prevent default action
+ e.preventDefault;
+ },
+ },
+ },
+ },
+});
+```
+
+
+
+
+```js
+ {
+ // Prevent default action
+ e.preventDefault();
+ },
+ }}
+/>
+```
+
+
+
+
+You can also pass a callback which returns the object with listeners. It'll receive `navigation` and `route` as the arguments.
+
+Example:
+
+
+
+```js
+const Tab = createBottomTabNavigatior({
+ screens: {
+ Chat: {
+ screen: Chat,
+ listeners: ({ navigation, route }) => ({
+ tabPress: (e) => {
+ // Prevent default action
+ e.preventDefault;
+
+ // Do something with the `navigation` object
+ navigation.navigate('AnotherPlace');
+ },
+ }),
+ },
+ },
+});
+```
+
+
+
+
+```js
+ ({
+ tabPress: (e) => {
+ // Prevent default action
+ e.preventDefault();
+
+ // Do something with the `navigation` object
+ navigation.navigate('AnotherPlace');
+ },
+ })}
+/>
+```
+
+
+
+
+### `screenListeners` prop on the navigator
+
+You can pass a prop named `screenListeners` to the navigator component, where you can specify listeners for events from all screens for this navigator. This can be useful if you want to listen to specific events regardless of the screen, or want to listen to common events such as `state` which is emitted to all screens.
+
+Example:
+
+
+
+
+```js
+const Stack = createNativeStackNavigator({
+ screenListeners: {
+ state: (e) => {
+ // Do something with the state
+ console.log('state changed', e.data);
+ },
+ },
+ screens: {
+ Home: HomeScreen,
+ Profile: ProfileScreen,
+ },
+});
+```
+
+
+
+
+```js
+ {
+ // Do something with the state
+ console.log('state changed', e.data);
+ },
+ }}
+>
+
+
+
+```
+
+
+
+
+Similar to `listeners`, you can also pass a function to `screenListeners`. The function will receive the [`navigation` object](navigation-object.md) and the [`route` object](route-object.md) for each screen. This can be useful if you need access to the `navigation` object.
+
+
+
+
+```js
+const Tab = createBottomTabNavigatior({
+ screenListeners: ({ navigation }) => ({
+ state: (e) => {
+ // Do something with the state
+ console.log('state changed', e.data);
+
+ // Do something with the `navigation` object
+ if (!navigation.canGoBack()) {
+ console.log("we're on the initial screen");
+ }
+ },
+ }),
+ screens: {
+ Home: HomeScreen,
+ Profile: ProfileScreen,
+ },
+});
+```
+
+
+
+
+```js
+ ({
+ state: (e) => {
+ // Do something with the state
+ console.log('state changed', e.data);
+
+ // Do something with the `navigation` object
+ if (!navigation.canGoBack()) {
+ console.log("we're on the initial screen");
+ }
+ },
+ })}
+>
+
+
+
+```
+
+
+
diff --git a/versioned_docs/version-8.x/navigation-lifecycle.md b/versioned_docs/version-8.x/navigation-lifecycle.md
new file mode 100755
index 00000000000..3df4f301bba
--- /dev/null
+++ b/versioned_docs/version-8.x/navigation-lifecycle.md
@@ -0,0 +1,343 @@
+---
+id: navigation-lifecycle
+title: Navigation lifecycle
+sidebar_label: Navigation lifecycle
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+In a previous section, we worked with a stack navigator that has two screens (`Home` and `Profile`) and learned how to use `navigation.navigate('RouteName')` to navigate between the screens.
+
+An important question in this context is: what happens with `Home` when we navigate away from it, or when we come back to it? How does a screen find out that a user is leaving it or coming back to it?
+
+If you are coming to react-navigation from a web background, you may assume that when the user navigates from route `A` to route `B`, `A` will unmount (its `componentWillUnmount` is called) and `A` will mount again when the user comes back to it. While these React lifecycle methods are still valid and are used in React Navigation, their usage differs from the web. This is driven by the more complex needs of mobile navigation.
+
+## Example scenario
+
+Consider a stack navigator with 2 screens: `Home` and `Profile`. When we first render the navigator, the `Home` screen is mounted, i.e. its `useEffect` or `componentDidMount` is called. When we navigate to `Profile`, now `Profile` is mounted and its `useEffect` or `componentDidMount` is called. But nothing happens to `Home` - it remains mounted in the stack. The cleanup function returned by `useEffect` or `componentWillUnmount` is not called.
+
+When we go back from `Profile` to `Home`, `Profile` is unmounted and its `useEffect` cleanup or `componentWillUnmount` is called. But `Home` is not mounted again - it remained mounted the whole time - and its `useEffect` or `componentDidMount` is not called.
+
+Similar results can be observed (in combination) with other navigators as well. Consider a tab navigator with two tabs, where each tab is a stack navigator:
+
+```js name="Navigation lifecycle" snack static2dynamic
+import * as React from 'react';
+import { Text, View } from 'react-native';
+import {
+ createStaticNavigation,
+ useNavigation,
+} from '@react-navigation/native';
+import {
+ createNativeStackNavigator,
+ createNativeStackScreen,
+} from '@react-navigation/native-stack';
+import {
+ createBottomTabNavigator,
+ createBottomTabScreen,
+} from '@react-navigation/bottom-tabs';
+import { Button } from '@react-navigation/elements';
+
+function SettingsScreen() {
+ const navigation = useNavigation();
+
+ React.useEffect(() => {
+ console.log('SettingsScreen mounted');
+
+ return () => console.log('SettingsScreen unmounted');
+ }, []);
+
+ return (
+
+ Settings Screen
+
+
+ );
+}
+
+function ProfileScreen() {
+ const navigation = useNavigation();
+
+ React.useEffect(() => {
+ console.log('ProfileScreen mounted');
+
+ return () => console.log('ProfileScreen unmounted');
+ }, []);
+
+ return (
+
+ Profile Screen
+
+
+ );
+}
+
+function HomeScreen() {
+ const navigation = useNavigation();
+
+ React.useEffect(() => {
+ console.log('HomeScreen mounted');
+
+ return () => console.log('HomeScreen unmounted');
+ }, []);
+
+ return (
+
+ Home Screen
+
+
+ );
+}
+
+function DetailsScreen() {
+ const navigation = useNavigation();
+
+ React.useEffect(() => {
+ console.log('DetailsScreen mounted');
+
+ return () => console.log('DetailsScreen unmounted');
+ }, []);
+
+ return (
+
+ Details Screen
+
+
+ );
+}
+
+// codeblock-focus-start
+const SettingsStack = createNativeStackNavigator({
+ screens: {
+ Settings: createNativeStackScreen({
+ screen: SettingsScreen,
+ }),
+ Profile: createNativeStackScreen({
+ screen: ProfileScreen,
+ }),
+ },
+});
+
+const HomeStack = createNativeStackNavigator({
+ screens: {
+ Home: createNativeStackScreen({
+ screen: HomeScreen,
+ }),
+ Details: createNativeStackScreen({
+ screen: DetailsScreen,
+ }),
+ },
+});
+
+const MyTabs = createBottomTabNavigator({
+ screenOptions: {
+ headerShown: false,
+ },
+ screens: {
+ First: createBottomTabScreen({
+ screen: SettingsStack,
+ }),
+ Second: createBottomTabScreen({
+ screen: HomeStack,
+ }),
+ },
+});
+// codeblock-focus-end
+
+const Navigation = createStaticNavigation(MyTabs);
+
+export default function App() {
+ return ;
+}
+```
+
+
+
+We start on the `HomeScreen` and navigate to `DetailsScreen`. Then we use the tab bar to switch to the `SettingsScreen` and navigate to `ProfileScreen`. After this sequence of operations is done, all 4 of the screens are mounted! If you use the tab bar to switch back to the `HomeStack`, you'll notice you'll be presented with the `DetailsScreen` - the navigation state of the `HomeStack` has been preserved!
+
+## React Navigation lifecycle events
+
+Now that we understand how React lifecycle methods work in React Navigation, let's answer the question we asked at the beginning: "How do we find out that a user is leaving (blur) it or coming back to it (focus)?"
+
+React Navigation emits events to screen components that subscribe to them. We can listen to `focus` and `blur` events to know when a screen comes into focus or goes out of focus respectively.
+
+Example:
+
+```js name="Focus and blur" snack static2dynamic
+import * as React from 'react';
+import { Text, View } from 'react-native';
+import {
+ createStaticNavigation,
+ useNavigation,
+} from '@react-navigation/native';
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+import { Button } from '@react-navigation/elements';
+
+// codeblock-focus-start
+function ProfileScreen() {
+ const navigation = useNavigation();
+
+ React.useEffect(() => {
+ // highlight-start
+ const unsubscribe = navigation.addListener('focus', () => {
+ console.log('ProfileScreen focused');
+ });
+ // highlight-end
+
+ return unsubscribe;
+ }, [navigation]);
+
+ React.useEffect(() => {
+ // highlight-start
+ const unsubscribe = navigation.addListener('blur', () => {
+ console.log('ProfileScreen blurred');
+ });
+ // highlight-end
+
+ return unsubscribe;
+ }, [navigation]);
+
+ return (
+
+ Profile Screen
+
+ );
+}
+// codeblock-focus-end
+
+function HomeScreen() {
+ const navigation = useNavigation();
+
+ React.useEffect(() => {
+ const unsubscribe = navigation.addListener('focus', () => {
+ console.log('HomeScreen focused');
+ });
+
+ return unsubscribe;
+ }, [navigation]);
+
+ React.useEffect(() => {
+ const unsubscribe = navigation.addListener('blur', () => {
+ console.log('HomeScreen blurred');
+ });
+
+ return unsubscribe;
+ }, [navigation]);
+
+ return (
+
+ Home Screen
+
+
+ );
+}
+
+const RootStack = createNativeStackNavigator({
+ screens: {
+ Home: HomeScreen,
+ Profile: ProfileScreen,
+ },
+});
+
+const Navigation = createStaticNavigation(RootStack);
+
+export default function App() {
+ return ;
+}
+```
+
+See [Navigation events](navigation-events.md) for more details on the available events and the API usage.
+
+For performing side effects, we can use the [`useFocusEffect`](use-focus-effect.md) hook instead of subscribing to events. It's like React's `useEffect` hook, but it ties into the navigation lifecycle.
+
+Example:
+
+```js name="Focus effect" snack static2dynamic
+import * as React from 'react';
+import { Text, View } from 'react-native';
+import {
+ createStaticNavigation,
+ useNavigation,
+} from '@react-navigation/native';
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+import { Button } from '@react-navigation/elements';
+// codeblock-focus-start
+import { useFocusEffect } from '@react-navigation/native';
+
+function ProfileScreen() {
+ // highlight-start
+ useFocusEffect(
+ React.useCallback(() => {
+ // Do something when the screen is focused
+ console.log('ProfileScreen focus effect');
+
+ return () => {
+ // Do something when the screen is unfocused
+ // Useful for cleanup functions
+ console.log('ProfileScreen focus effect cleanup');
+ };
+ }, [])
+ );
+ // highlight-end
+
+ return (
+
+ Profile Screen
+
+ );
+}
+// codeblock-focus-end
+
+function HomeScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ Home Screen
+
+
+ );
+}
+
+const RootStack = createNativeStackNavigator({
+ screens: {
+ Home: HomeScreen,
+ Profile: ProfileScreen,
+ },
+});
+
+const Navigation = createStaticNavigation(RootStack);
+
+export default function App() {
+ return ;
+}
+```
+
+
+
+If you want to render different things based on if the screen is focused or not, you can use the [`useIsFocused`](use-is-focused.md) hook which returns a boolean indicating whether the screen is focused.
+
+If you want to know if the screen is focused or not inside of an event listener, you can use the [`navigation.isFocused()`](navigation-object.md#isfocused) method. Note that using this method doesn't trigger a re-render like the `useIsFocused` hook does, so it is not suitable for rendering different things based on focus state.
+
+## Summary
+
+- React Navigation does not unmount screens when navigating away from them
+- The [`useFocusEffect`](use-focus-effect.md) hook is analogous to React's [`useEffect`](https://react.dev/reference/react/useEffect) but is tied to the navigation lifecycle instead of the component lifecycle.
+- The [`useIsFocused`](use-is-focused.md) hook and [`navigation.isFocused()`](navigation-object.md#isfocused) method can be used to determine if a screen is currently focused.
+- React Navigation emits [`focus`](navigation-events.md#focus) and [`blur`](navigation-events.md#blur) events that can be listened to when a screen comes into focus or goes out of focus.
diff --git a/versioned_docs/version-8.x/navigation-object.md b/versioned_docs/version-8.x/navigation-object.md
new file mode 100755
index 00000000000..54eb9921f0e
--- /dev/null
+++ b/versioned_docs/version-8.x/navigation-object.md
@@ -0,0 +1,1938 @@
+---
+id: navigation-object
+title: Navigation object reference
+sidebar_label: Navigation object
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+The `navigation` object contains various convenience functions that dispatch navigation actions. It looks like this:
+
+- `navigation`
+ - `navigate` - go to the given screen, this will behave differently based on the navigator
+ - `goBack` - go back to the previous screen, this will pop the current screen when used in a stack
+ - `reset` - replace the navigation state of the navigator with the given state
+ - `preload` - preload a screen in the background before navigating to it
+ - `setParams` - merge new params onto the route's params
+ - `replaceParams` - replace the route's params with new params
+ - `pushParams` - update params and push a new entry to history stack
+ - `dispatch` - send an action object to update the [navigation state](navigation-state.md)
+ - `setOptions` - update the screen's options
+ - `isFocused` - check whether the screen is focused
+ - `canGoBack` - check whether it's possible to go back from the current screen
+ - `getState` - get the navigation state of the navigator
+ - `getParent` - get the navigation object of the parent screen, if any
+ - `addListener` - subscribe to events for the screen
+ - `removeListener` - unsubscribe from events for the screen
+
+The `navigation` object can be accessed inside any screen component with the [`useNavigation`](use-navigation.md) hook. It's also passed as a prop only to screens components defined with the dynamic API.
+
+:::warning
+
+`setParams`/`setOptions` etc. should only be called in event listeners or `useEffect`/`useLayoutEffect`/`componentDidMount`/`componentDidUpdate` etc. Not during render or in constructor.
+
+:::
+
+### Navigator-dependent functions
+
+There are several additional functions present on `navigation` object based on the kind of the current navigator.
+
+If the navigator is a stack navigator, several alternatives to `navigate` and `goBack` are provided and you can use whichever you prefer. The functions are:
+
+- `navigation`
+ - `replace` - replace the current screen with a new one
+ - `push` - push a new screen onto the stack
+ - `pop` - go back in the stack
+ - `popTo` - go back to a specific screen in the stack
+ - `popToTop` - go to the top of the stack
+
+See [Stack navigator helpers](stack-navigator.md#helpers) and [Native Stack navigator helpers](native-stack-navigator.md#helpers) for more details on these methods.
+
+If the navigator is a tab navigator, the following are also available:
+
+- `navigation`
+ - `jumpTo` - go to a specific screen in the tab navigator
+
+See [Bottom Tab navigator helpers](bottom-tab-navigator.md#helpers) and [Material Top Tab navigator helpers](material-top-tab-navigator.md#helpers) for more details on these methods.
+
+If the navigator is a drawer navigator, the following are also available:
+
+- `navigation`
+ - `jumpTo` - go to a specific screen in the drawer navigator
+ - `openDrawer` - open the drawer
+ - `closeDrawer` - close the drawer
+ - `toggleDrawer` - toggle the state, ie. switch from closed to open and vice versa
+
+See [Drawer navigator helpers](drawer-navigator.md#helpers) for more details on these methods.
+
+## Common API reference
+
+The vast majority of your interactions with the `navigation` object will involve `navigate`, `goBack`, and `setParams`.
+
+### `navigate`
+
+The `navigate` method lets us navigate to another screen in your app. It takes the following arguments:
+
+`navigation.navigate(name, params)`
+
+- `name` - _string_ - A destination name of the screen in the current or a parent navigator.
+- `params` - _object_ - Params to use for the destination route.
+- `options` - Options object containing the following properties:
+ - `merge` - _boolean_ - Whether params should be merged with the existing route params, or replace them (when navigating to an existing screen). Defaults to `false`.
+ - `pop` - _boolean_ - Whether screens should be popped to navigate to a matching screen in the stack. Defaults to `false`.
+
+
+
+
+```js name="Navigate method" snack
+import * as React from 'react';
+import { View, Text } from 'react-native';
+import { Button } from '@react-navigation/elements';
+import {
+ useNavigation,
+ createStaticNavigation,
+} from '@react-navigation/native';
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+
+// codeblock-focus-start
+function HomeScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ This is the home screen of the app
+
+
+ );
+}
+// codeblock-focus-end
+
+function ProfileScreen({ route }) {
+ const navigation = useNavigation();
+
+ return (
+
+ Profile Screen
+ Friends:
+ {route.params.names[0]}
+ {route.params.names[1]}
+ {route.params.names[2]}
+
+
+ );
+}
+
+const Stack = createNativeStackNavigator({
+ initialRouteName: 'Home',
+ screens: {
+ Home: HomeScreen,
+ Profile: ProfileScreen,
+ },
+});
+
+const Navigation = createStaticNavigation(Stack);
+
+function App() {
+ return ;
+}
+
+export default App;
+```
+
+
+
+
+```js name="Navigate method" snack
+import * as React from 'react';
+import { View, Text } from 'react-native';
+import { Button } from '@react-navigation/elements';
+import { NavigationContainer, useNavigation } from '@react-navigation/native';
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+
+// codeblock-focus-start
+function HomeScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ This is the home screen of the app
+
+
+ );
+}
+// codeblock-focus-end
+
+function ProfileScreen({ route }) {
+ const navigation = useNavigation();
+
+ return (
+
+ Profile Screen
+ Friends:
+ {route.params.names[0]}
+ {route.params.names[1]}
+ {route.params.names[2]}
+
+
+ );
+}
+
+const Stack = createNativeStackNavigator();
+
+function App() {
+ return (
+
+
+
+
+
+
+ );
+}
+
+export default App;
+```
+
+
+
+
+In a stack navigator ([stack](stack-navigator.md) or [native stack](native-stack-navigator.md)), calling `navigate` with a screen name will have the following behavior:
+
+- If you're already on a screen with the same name, it will update its params and not push a new screen.
+- If you're on a different screen, it will push the new screen onto the stack.
+- If the [`getId`](screen.md#id) prop is specified, and another screen in the stack has the same ID, it will bring that screen to focus and update its params instead.
+- If none of the above conditions match, it'll push a new screen to the stack.
+
+In a tab or drawer navigator, calling `navigate` will switch to the relevant screen if it's not focused already and update the params of the screen.
+
+### `navigateDeprecated`
+
+:::warning
+
+This method is deprecated and will be removed in a future release. It only exists for compatibility purposes. Use `navigate` instead.
+
+:::
+
+The `navigateDeprecated` action implements the old behavior of `navigate` from previous versions.
+
+It takes the following arguments:
+
+`navigation.navigateDeprecated(name, params)`
+
+- `name` - _string_ - A destination name of the screen in the current or a parent navigator.
+- `params` - _object_ - Params to use for the destination route.
+
+In a stack navigator ([stack](stack-navigator.md) or [native stack](native-stack-navigator.md)), calling `navigate` with a screen name will have the following behavior:
+
+- If you're already on a screen with the same name, it will update its params and not push a new screen.
+- If you're on a different screen, it will push the new screen onto the stack.
+- If the [`getId`](screen.md#id) prop is specified, and another screen in the stack has the same ID, it will bring that screen to focus and update its params instead.
+
+In a tab or drawer navigator, calling `navigate` will switch to the relevant screen if it's not focused already and update the params of the screen.
+
+### `goBack`
+
+The `goBack` method lets us go back to the previous screen in the navigator.
+
+By default, `goBack` will go back from the screen that it is called from:
+
+
+
+
+```js name="Navigate method" snack
+import * as React from 'react';
+import { View, Text } from 'react-native';
+import { Button } from '@react-navigation/elements';
+import {
+ useNavigation,
+ createStaticNavigation,
+} from '@react-navigation/native';
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+
+function HomeScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ This is the home screen of the app
+
+
+ );
+}
+
+// codeblock-focus-start
+function ProfileScreen({ route }) {
+ const navigation = useNavigation();
+
+ return (
+
+ Profile Screen
+ Friends:
+ {route.params.names[0]}
+ {route.params.names[1]}
+ {route.params.names[2]}
+ // highlight-next-line
+
+
+ );
+}
+// codeblock-focus-end
+
+const Stack = createNativeStackNavigator({
+ initialRouteName: 'Home',
+ screens: {
+ Home: HomeScreen,
+ Profile: ProfileScreen,
+ },
+});
+
+const Navigation = createStaticNavigation(Stack);
+
+function App() {
+ return ;
+}
+
+export default App;
+```
+
+
+
+
+```js name="Navigate method" snack
+import * as React from 'react';
+import { View, Text } from 'react-native';
+import { Button } from '@react-navigation/elements';
+import { NavigationContainer, useNavigation } from '@react-navigation/native';
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+
+function HomeScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ This is the home screen of the app
+
+
+ );
+}
+
+// codeblock-focus-start
+function ProfileScreen({ route }) {
+ const navigation = useNavigation();
+
+ return (
+
+ Profile Screen
+ Friends:
+ {route.params.names[0]}
+ {route.params.names[1]}
+ {route.params.names[2]}
+ // highlight-next-line
+
+
+ );
+}
+// codeblock-focus-end
+
+const Stack = createNativeStackNavigator();
+
+function App() {
+ return (
+
+
+
+
+
+
+ );
+}
+
+export default App;
+```
+
+
+
+
+### `reset`
+
+The `reset` method lets us replace the navigator state with a new state:
+
+
+
+
+```js name="Navigation object replace and reset" snack
+import * as React from 'react';
+import { Button } from '@react-navigation/elements';
+import { View, Text } from 'react-native';
+import {
+ useNavigation,
+ createStaticNavigation,
+} from '@react-navigation/native';
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+
+function HomeScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ This is the home screen of the app
+
+
+ );
+}
+
+function ProfileScreen({ route }) {
+ const navigation = useNavigation();
+
+ return (
+
+ Profile Screen
+ Friends:
+ {route.params.names[0]}
+ {route.params.names[1]}
+ {route.params.names[2]}
+
+
+
+
+
+
+ );
+}
+
+function SettingsScreen({ route }) {
+ const navigation = useNavigation();
+
+ return (
+
+ Settings screen
+ {route.params.someParam}
+
+
+
+ );
+}
+
+const Stack = createNativeStackNavigator({
+ initialRouteName: 'Home',
+ screens: {
+ Home: HomeScreen,
+ Profile: ProfileScreen,
+ Settings: SettingsScreen,
+ },
+});
+
+const Navigation = createStaticNavigation(Stack);
+
+function App() {
+ return ;
+}
+
+export default App;
+```
+
+
+
+
+```js name="Navigation object replace and reset" snack
+import * as React from 'react';
+import { Button } from '@react-navigation/elements';
+import { View, Text } from 'react-native';
+import { NavigationContainer, useNavigation } from '@react-navigation/native';
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+
+function HomeScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ This is the home screen of the app
+
+
+ );
+}
+
+function ProfileScreen({ route }) {
+ const navigation = useNavigation();
+
+ return (
+
+ Profile Screen
+ Friends:
+ {route.params.names[0]}
+ {route.params.names[1]}
+ {route.params.names[2]}
+
+
+
+
+
+
+ );
+}
+
+function SettingsScreen({ route }) {
+ const navigation = useNavigation();
+
+ return (
+
+ Settings screen
+ {route.params.someParam}
+
+
+
+ );
+}
+
+const Stack = createNativeStackNavigator();
+
+function App() {
+ return (
+
+
+
+
+
+
+
+ );
+}
+
+export default App;
+```
+
+
+
+
+The state object specified in `reset` replaces the existing [navigation state](navigation-state.md) with the new one, i.e. removes existing screens and add new ones. If you want to preserve the existing screens when changing the state, you can use [`CommonActions.reset`](navigation-actions.md#reset) with [`dispatch`](#dispatch) instead.
+
+:::warning
+
+Consider the navigator's state object to be internal and subject to change in a minor release. Avoid using properties from the [navigation state](navigation-state.md) state object except `index` and `routes`, unless you really need it. If there is some functionality you cannot achieve without relying on the structure of the state object, please open an issue.
+
+:::
+
+### `preload`
+
+The `preload` method allows preloading a screen in the background before navigating to it. It takes the following arguments:
+
+- `name` - _string_ - A destination name of the screen in the current or a parent navigator.
+- `params` - _object_ - Params to use for the destination route.
+
+
+
+
+```js name="Common actions preload" snack
+import * as React from 'react';
+import { View, Text } from 'react-native';
+import {
+ createStaticNavigation,
+ useNavigation,
+ CommonActions,
+} from '@react-navigation/native';
+import { createStackNavigator } from '@react-navigation/stack';
+import { Button } from '@react-navigation/elements';
+
+// codeblock-focus-start
+function HomeScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ Home!
+
+
+
+ );
+}
+// codeblock-focus-end
+
+function ProfileScreen({ route }) {
+ const navigation = useNavigation();
+ const [startTime] = React.useState(Date.now());
+ const [endTime, setEndTime] = React.useState(null);
+
+ React.useEffect(() => {
+ const unsubscribe = navigation.addListener('focus', () => {
+ setEndTime(Date.now());
+ });
+
+ return () => {
+ unsubscribe();
+ };
+ }, [navigation]);
+
+ return (
+
+ Profile!
+ {route.params.user}'s profile
+ Preloaded for: {endTime ? endTime - startTime : 'N/A'}ms
+
+ );
+}
+
+const Stack = createStackNavigator({
+ screens: {
+ Home: HomeScreen,
+ Profile: ProfileScreen,
+ },
+});
+
+const Navigation = createStaticNavigation(Stack);
+
+export default function App() {
+ return ;
+}
+```
+
+
+
+
+```js name="Common actions preload" snack
+import * as React from 'react';
+import { View, Text } from 'react-native';
+import {
+ NavigationContainer,
+ CommonActions,
+ useNavigation,
+} from '@react-navigation/native';
+import { createStackNavigator } from '@react-navigation/stack';
+import { Button } from '@react-navigation/elements';
+
+// codeblock-focus-start
+function HomeScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ Home!
+
+
+
+ );
+}
+// codeblock-focus-end
+
+function ProfileScreen({ route }) {
+ const navigation = useNavigation();
+ const [startTime] = React.useState(Date.now());
+ const [endTime, setEndTime] = React.useState(null);
+
+ React.useEffect(() => {
+ const unsubscribe = navigation.addListener('focus', () => {
+ setEndTime(Date.now());
+ });
+
+ return () => {
+ unsubscribe();
+ };
+ }, [navigation]);
+
+ return (
+
+ Profile!
+ {route.params.user}'s profile
+ Preloaded for: {endTime ? endTime - startTime : 'N/A'}ms
+
+ );
+}
+
+const Stack = createStackNavigator();
+
+export default function App() {
+ return (
+
+
+
+
+
+
+ );
+}
+```
+
+
+
+
+Preloading a screen means that the screen will be rendered in the background. All the components in the screen will be mounted and the `useEffect` hooks will be called. This can be useful when you want to improve the perceived performance by hiding the delay in mounting heavy components or loading data.
+
+Depending on the navigator, `preload` may work slightly differently:
+
+- In a stack navigator ([stack](stack-navigator.md), [native stack](native-stack-navigator.md)), the screen will be rendered off-screen and animated in when you navigate to it. If [`getId`](screen.md#id) is specified, it'll be used for the navigation to identify the preloaded screen.
+- In a tab or drawer navigator ([bottom tabs](bottom-tab-navigator.md), [material top tabs](material-top-tab-navigator.md), [drawer](drawer-navigator.md), etc.), the existing screen will be rendered as if `lazy` was set to `false`. Calling `preload` on a screen that is already rendered will not have any effect.
+
+When a screen is preloaded in a stack navigator, it will have a few limitations:
+
+- It can't dispatch navigation actions (e.g. `navigate`, `goBack`, etc.).
+- It can't update options with `navigation.setOptions`.
+- It can't listen to events from the navigator (e.g. `focus`, `tabPress`, etc.).
+
+The `navigation` object will be updated once you navigate to the screen. So if you have an event listener in a `useEffect` hook, and have a dependency on `navigation`, it will add any listeners when the screen is navigated to:
+
+```js
+React.useEffect(() => {
+ const unsubscribe = navigation.addListener('tabPress', () => {
+ // do something
+ });
+
+ return () => {
+ unsubscribe();
+ };
+}, [navigation]);
+```
+
+Similarly, for dispatching actions or updating options, you can check if the screen is focused before doing so:
+
+```js
+if (navigation.isFocused()) {
+ navigation.setOptions({ title: 'Updated title' });
+}
+```
+
+### `setParams`
+
+The `setParams` method lets us update the params (`route.params`) of the current screen. `setParams` works like React's `setState` - it shallow merges the provided params object with the current params.
+
+
+
+
+```js name="Navigation object setParams" snack
+import * as React from 'react';
+import { Button } from '@react-navigation/elements';
+import { View, Text } from 'react-native';
+import {
+ useNavigation,
+ createStaticNavigation,
+} from '@react-navigation/native';
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+
+function HomeScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ This is the home screen of the app
+
+
+ );
+}
+
+// codeblock-focus-start
+function ProfileScreen({ route }) {
+ const navigation = useNavigation();
+
+ return (
+
+ Profile Screen
+ Friends:
+ {route.params.friends[0]}
+ {route.params.friends[1]}
+ {route.params.friends[2]}
+
+
+
+ );
+}
+// codeblock-focus-end
+
+const Stack = createNativeStackNavigator({
+ initialRouteName: 'Home',
+ screens: {
+ Home: HomeScreen,
+ Profile: {
+ screen: ProfileScreen,
+ options: ({ route }) => ({ title: route.params.title }),
+ },
+ },
+});
+
+const Navigation = createStaticNavigation(Stack);
+
+function App() {
+ return ;
+}
+
+export default App;
+```
+
+
+
+
+```js name="Navigation object setParams" snack
+import * as React from 'react';
+import { Button } from '@react-navigation/elements';
+import { View, Text } from 'react-native';
+import { NavigationContainer, useNavigation } from '@react-navigation/native';
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+
+function HomeScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ This is the home screen of the app
+
+
+ );
+}
+
+// codeblock-focus-start
+function ProfileScreen({ route }) {
+ const navigation = useNavigation();
+
+ return (
+
+ Profile Screen
+ Friends:
+ {route.params.friends[0]}
+ {route.params.friends[1]}
+ {route.params.friends[2]}
+
+
+
+ );
+}
+// codeblock-focus-end
+
+const Stack = createNativeStackNavigator();
+
+function App() {
+ return (
+
+
+
+ ({ title: route.params.title })}
+ />
+
+
+ );
+}
+
+export default App;
+```
+
+
+
+
+### `replaceParams`
+
+The `replaceParams` method lets us replace the params (`route.params`) of the current screen with a new params object.
+
+
+
+
+```js name="Navigation object replaceParams" snack
+import * as React from 'react';
+import { Button } from '@react-navigation/elements';
+import { View, Text } from 'react-native';
+import {
+ useNavigation,
+ createStaticNavigation,
+} from '@react-navigation/native';
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+
+function HomeScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ This is the home screen of the app
+
+
+ );
+}
+
+// codeblock-focus-start
+function ProfileScreen({ route }) {
+ const navigation = useNavigation();
+
+ return (
+
+ Profile Screen
+ Friends:
+ {route.params.friends[0]}
+ {route.params.friends[1]}
+ {route.params.friends[2]}
+
+
+
+ );
+}
+// codeblock-focus-end
+
+const Stack = createNativeStackNavigator({
+ initialRouteName: 'Home',
+ screens: {
+ Home: HomeScreen,
+ Profile: {
+ screen: ProfileScreen,
+ options: ({ route }) => ({ title: route.params.title }),
+ },
+ },
+});
+
+const Navigation = createStaticNavigation(Stack);
+
+function App() {
+ return ;
+}
+
+export default App;
+```
+
+
+
+
+```js name="Navigation object replaceParams" snack
+import * as React from 'react';
+import { Button } from '@react-navigation/elements';
+import { View, Text } from 'react-native';
+import { NavigationContainer, useNavigation } from '@react-navigation/native';
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+
+function HomeScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ This is the home screen of the app
+
+
+ );
+}
+
+// codeblock-focus-start
+function ProfileScreen({ route }) {
+ const navigation = useNavigation();
+
+ return (
+
+ Profile Screen
+ Friends:
+ {route.params.friends[0]}
+ {route.params.friends[1]}
+ {route.params.friends[2]}
+
+
+
+ );
+}
+// codeblock-focus-end
+
+const Stack = createNativeStackNavigator();
+
+function App() {
+ return (
+
+
+
+ ({ title: route.params.title })}
+ />
+
+
+ );
+}
+
+export default App;
+```
+
+
+
+
+### `pushParams`
+
+The `pushParams` method lets us update the params (`route.params`) of the current screen and push a new entry to the history stack. Unlike `setParams` which merges the new params with the existing ones, `pushParams` uses the new params object as-is.
+
+`navigation.pushParams(params)`
+
+- `params` - _object_ - New params to use for the route.
+
+This is useful in scenarios like:
+
+- A product listing page with filters, where changing filters should create a new history entry so users can go back to previous filter states.
+- A screen with a custom modal component, where the modal is not a separate screen but its state should be reflected in the URL and history.
+
+```js
+navigation.pushParams({ filter: 'new' });
+```
+
+The action works in all navigators, including stack, tab, and drawer navigators.
+
+### `setOptions`
+
+The `setOptions` method lets us set screen options from within the component. This is useful if we need to use the component's props, state or context to configure our screen.
+
+
+
+
+```js name="Navigation object setOptions" snack
+import * as React from 'react';
+import { View, Text, TextInput } from 'react-native';
+import { Button } from '@react-navigation/elements';
+import {
+ useNavigation,
+ createStaticNavigation,
+} from '@react-navigation/native';
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+
+function HomeScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ This is the home screen of the app
+
+
+ );
+}
+
+// codeblock-focus-start
+function ProfileScreen({ route }) {
+ const navigation = useNavigation();
+ const [value, onChangeText] = React.useState(route.params.title);
+
+ React.useEffect(() => {
+ // highlight-start
+ navigation.setOptions({
+ title: value === '' ? 'No title' : value,
+ });
+ // highlight-end
+ }, [navigation, value]);
+
+ return (
+
+
+
+
+ );
+}
+// codeblock-focus-end
+
+const Stack = createNativeStackNavigator({
+ initialRouteName: 'Home',
+ screens: {
+ Home: HomeScreen,
+ Profile: {
+ screen: ProfileScreen,
+ options: ({ route }) => ({ title: route.params.title }),
+ },
+ },
+});
+
+const Navigation = createStaticNavigation(Stack);
+
+function App() {
+ return ;
+}
+
+export default App;
+```
+
+
+
+
+```js name="Navigation object setOptions" snack
+import * as React from 'react';
+import { View, Text, TextInput } from 'react-native';
+import { Button } from '@react-navigation/elements';
+import { NavigationContainer, useNavigation } from '@react-navigation/native';
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+
+function HomeScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ This is the home screen of the app
+
+
+ );
+}
+
+// codeblock-focus-start
+function ProfileScreen({ route }) {
+ const navigation = useNavigation();
+ const [value, onChangeText] = React.useState(route.params.title);
+
+ React.useEffect(() => {
+ // highlight-start
+ navigation.setOptions({
+ title: value === '' ? 'No title' : value,
+ });
+ // highlight-end
+ }, [navigation, value]);
+
+ return (
+
+
+
+
+ );
+}
+// codeblock-focus-end
+
+const Stack = createNativeStackNavigator();
+
+function App() {
+ return (
+
+
+
+ ({ title: route.params.title })}
+ />
+
+
+ );
+}
+
+export default App;
+```
+
+
+
+
+Any options specified here are shallow merged with the options specified when defining the screen.
+
+When using `navigation.setOptions`, we recommend specifying a placeholder in the screen's `options` prop and update it using `navigation.setOptions`. This makes sure that the delay for updating the options isn't noticeable to the user. It also makes it work with lazy-loaded screens.
+
+You can also use `React.useLayoutEffect` to reduce the delay in updating the options. But we recommend against doing it if you support web and do server side rendering.
+
+:::note
+
+`navigation.setOptions` is intended to provide the ability to update existing options when necessary. It's not a replacement for the `options` prop on the screen. Make sure to use `navigation.setOptions` sparingly only when absolutely necessary.
+
+:::
+
+## Navigation events
+
+Screens can add listeners on the `navigation` object with the `addListener` method. For example, to listen to the `focus` event:
+
+
+
+
+```js name="Navigation events" snack
+import * as React from 'react';
+import { View, Text } from 'react-native';
+import { Button } from '@react-navigation/elements';
+import {
+ useNavigation,
+ createStaticNavigation,
+} from '@react-navigation/native';
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+
+function SettingsScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ Settings Screen
+
+
+ );
+}
+
+// codeblock-focus-start
+function ProfileScreen() {
+ const navigation = useNavigation();
+
+ React.useEffect(
+ () => navigation.addListener('focus', () => alert('Screen was focused')),
+ [navigation]
+ );
+
+ React.useEffect(
+ () => navigation.addListener('blur', () => alert('Screen was unfocused')),
+ [navigation]
+ );
+
+ return (
+
+ Profile Screen
+
+
+ );
+}
+// codeblock-focus-end
+
+const SettingsStack = createNativeStackNavigator({
+ screens: {
+ Settings: SettingsScreen,
+ Profile: ProfileScreen,
+ },
+});
+
+const Navigation = createStaticNavigation(SettingsStack);
+
+export default function App() {
+ return ;
+}
+```
+
+
+
+
+```js name="Navigation events" snack
+import * as React from 'react';
+import { View, Text } from 'react-native';
+import { Button } from '@react-navigation/elements';
+import { NavigationContainer, useNavigation } from '@react-navigation/native';
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+
+function SettingsScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ Settings Screen
+
+
+ );
+}
+
+// codeblock-focus-start
+function ProfileScreen() {
+ const navigation = useNavigation();
+
+ React.useEffect(
+ () => navigation.addListener('focus', () => alert('Screen was focused')),
+ [navigation]
+ );
+
+ React.useEffect(
+ () => navigation.addListener('blur', () => alert('Screen was unfocused')),
+ [navigation]
+ );
+
+ return (
+
+ Profile Screen
+
+
+ );
+}
+// codeblock-focus-end
+
+const SettingsStack = createNativeStackNavigator();
+
+export default function App() {
+ return (
+
+
+
+
+
+
+ );
+}
+```
+
+
+
+
+See [Navigation events](navigation-events.md) for more details on the available events and the API usage.
+
+### `isFocused`
+
+This method lets us check whether the screen is currently focused. Returns `true` if the screen is focused and `false` otherwise.
+
+```js
+const isFocused = navigation.isFocused();
+```
+
+This method doesn't re-render the screen when the value changes and mainly useful in callbacks. You probably want to use [useIsFocused](use-is-focused.md) instead of using this directly, it will return a boolean a prop to indicating if the screen is focused.
+
+## Advanced API Reference
+
+The `dispatch` function is much less commonly used, but a good escape hatch if you can't do what you need with the available methods such as `navigate`, `goBack` etc. We recommend to avoid using the `dispatch` method often unless absolutely necessary.
+
+### `dispatch`
+
+The `dispatch` method lets us send a navigation action object which determines how the [navigation state](navigation-state.md) will be updated. All of the navigation functions like `navigate` use `dispatch` behind the scenes.
+
+Note that if you want to dispatch actions you should use the action creators provided in this library instead of writing the action object directly.
+
+See [Navigation Actions Docs](navigation-actions.md) for a full list of available actions.
+
+```js
+import { CommonActions } from '@react-navigation/native';
+
+navigation.dispatch(
+ CommonActions.navigate({
+ name: 'Profile',
+ params: {},
+ })
+);
+```
+
+When dispatching action objects, you can also specify few additional properties:
+
+- `source` - The key of the route which should be considered as the source of the action. For example, the `replace` action will replace the route with the given key. By default, it'll use the key of the route that dispatched the action. You can explicitly pass `undefined` to override this behavior.
+- `target` - The key of the [navigation state](navigation-state.md) the action should be applied on. By default, actions bubble to other navigators if not handled by a navigator. If `target` is specified, the action won't bubble if the navigator with the same key didn't handle it.
+
+Example:
+
+```js
+import { CommonActions } from '@react-navigation/native';
+
+navigation.dispatch({
+ ...CommonActions.navigate('Profile'),
+ source: 'someRoutekey',
+ target: 'someStatekey',
+});
+```
+
+#### Custom action creators
+
+It's also possible to pass a action creator function to `dispatch`. The function will receive the current state and needs to return a navigation action object to use:
+
+```js
+import { CommonActions } from '@react-navigation/native';
+
+navigation.dispatch((state) => {
+ // Add the home route to the start of the stack
+ const routes = [{ name: 'Home' }, ...state.routes];
+
+ return CommonActions.reset({
+ ...state,
+ routes,
+ index: routes.length - 1,
+ });
+});
+```
+
+You can use this functionality to build your own helpers that you can utilize in your app. Here is an example which implements inserting a screen just before the last one:
+
+```js
+import { CommonActions } from '@react-navigation/native';
+
+const insertBeforeLast = (routeName, params) => (state) => {
+ const routes = [
+ ...state.routes.slice(0, -1),
+ { name: routeName, params },
+ state.routes[state.routes.length - 1],
+ ];
+
+ return CommonActions.reset({
+ ...state,
+ routes,
+ index: routes.length - 1,
+ });
+};
+```
+
+Then use it like:
+
+```js
+navigation.dispatch(insertBeforeLast('Home'));
+```
+
+### `canGoBack`
+
+This method returns a boolean indicating whether there's any navigation history available in the current navigator, or in any parent navigators. You can use this to check if you can call `navigation.goBack()`:
+
+```js
+if (navigation.canGoBack()) {
+ navigation.goBack();
+}
+```
+
+Don't use this method for rendering content as this will not trigger a re-render. This is only intended for use inside callbacks, event listeners etc.
+
+### `getParent`
+
+This method returns the navigation object from the parent navigator that the current navigator is nested in. For example, if you have a stack navigator and a tab navigator nested inside the stack, then you can use `getParent` inside a screen of the tab navigator to get the navigation object passed from the stack navigator.
+
+It accepts an optional screen name parameter to refer to a specific parent screen. For example, if your screen is nested with multiple levels of nesting somewhere under a drawer navigator, you can directly refer to it by the name of the screen in the drawer navigator instead of calling `getParent` multiple times.
+
+For example, consider the following structure:
+
+```js static2dynamic
+const LeftDrawer = createDrawerNavigator({
+ screens: {
+ Feed: {
+ screen: FeedScreen,
+ },
+ Messages: {
+ screen: MessagesScreen,
+ },
+ },
+});
+
+const RootDrawer = createDrawerNavigator({
+ screens: {
+ Home: {
+ screen: HomeScreen,
+ },
+ Dashboard: {
+ screen: LeftDrawer,
+ },
+ },
+});
+```
+
+Then when using `getParent` inside of `FeedScree`, instead of:
+
+```js
+// Avoid this
+const drawerNavigation = navigation.getParent();
+
+// ...
+
+drawerNavigation?.openDrawer();
+```
+
+You can do:
+
+```js
+// Do this
+const drawerNavigation = navigation.getParent('Dashboard');
+
+// ...
+
+drawerNavigation?.openDrawer();
+```
+
+In this case, `'Dashboard'` refers to the name of a parent screen of `Feed` that's used in the parent drawer navigator.
+
+This approach allows components to not have to know the nesting structure of the navigators. So it's highly recommended to use a screen name when using `getParent`.
+
+This method will return `undefined` if there is no matching parent navigator.
+
+### `getState`
+
+:::warning
+
+Consider the navigator's state object to be internal and subject to change in a minor release. Avoid using properties from the [navigation state](navigation-state.md) state object except `index` and `routes`, unless you really need it. If there is some functionality you cannot achieve without relying on the structure of the state object, please open an issue.
+
+:::
+
+This method returns the state object of the navigator which contains the screen. Getting the navigator state could be useful in very rare situations. You most likely don't need to use this method. If you do, make sure you have a good reason.
+
+If you need the state for rendering content, you should use [`useNavigationState`](use-navigation-state.md) instead of this method.
diff --git a/versioned_docs/version-8.x/navigation-state.md b/versioned_docs/version-8.x/navigation-state.md
new file mode 100644
index 00000000000..43d2a59a7b5
--- /dev/null
+++ b/versioned_docs/version-8.x/navigation-state.md
@@ -0,0 +1,127 @@
+---
+id: navigation-state
+title: Navigation state reference
+sidebar_label: Navigation state
+---
+
+The navigation state is the state where React Navigation stores the navigation structure and history of the app. It's useful to know about the structure of the navigation state if you need to do advanced operations such as [resetting the state](navigation-actions.md#reset), [providing a custom initial state](navigation-container.md#initialstate) etc.
+
+It's a JavaScript object which looks like this:
+
+```js
+const state = {
+ type: 'stack',
+ key: 'stack-1',
+ routeNames: ['Home', 'Profile', 'Settings'],
+ routes: [
+ { key: 'home-1', name: 'Home', params: { sortBy: 'latest' } },
+ { key: 'settings-1', name: 'Settings' },
+ ],
+ index: 1,
+ stale: false,
+};
+```
+
+There are few properties present in every navigation state object:
+
+- `type` - Type of the navigator that the state belongs to, e.g. `stack`, `tab`, `drawer`.
+- `key` - Unique key to identify the navigator.
+- `routeNames` - Name of the screens defined in the navigator. This is an unique array containing strings for each screen.
+- `routes` - List of route objects (screens) which are rendered in the navigator. It also represents the history in a stack navigator. There should be at least one item present in this array.
+- `index` - Index of the focused route object in the `routes` array.
+- `history` - A list of visited items. This is an optional property and not present in all navigators. For example, it's only present in tab and drawer navigators in the core. The shape of the items in the `history` array can vary depending on the navigator. There should be at least one item present in this array.
+- `stale` - A navigation state is assumed to be stale unless the `stale` property is explicitly set to `false`. This means that the state object needs to be ["rehydrated"](#stale-state-objects).
+
+Each route object in a `routes` array may contain the following properties:
+
+- `key` - Unique key of the screen. Created automatically or added while navigating to this screen.
+- `name` - Name of the screen. Defined in navigator component hierarchy.
+- `params` - An optional object containing params which is defined while navigating e.g. `navigate('Home', { sortBy: 'latest' })`.
+- `state` - An optional object containing the [stale navigation state](#stale-state-objects) of a child navigator nested inside this screen.
+
+For example, a stack navigator containing a tab navigator nested inside it's home screen may have a navigation state object like this:
+
+```js
+const state = {
+ type: 'stack',
+ key: 'stack-1',
+ routeNames: ['Home', 'Profile', 'Settings'],
+ routes: [
+ {
+ key: 'home-1',
+ name: 'Home',
+ state: {
+ key: 'tab-1',
+ routeNames: ['Feed', 'Library', 'Favorites'],
+ routes: [
+ { key: 'feed-1', name: 'Feed', params: { sortBy: 'latest' } },
+ { key: 'library-1', name: 'Library' },
+ { key: 'favorites-1', name: 'Favorites' },
+ ],
+ index: 0,
+ },
+ },
+ { key: 'settings-1', name: 'Settings' },
+ ],
+ index: 1,
+};
+```
+
+It's important to note that even if there's a nested navigator, the `state` property on the `route` object is not added until a navigation happens, hence it's not guaranteed to exist.
+
+## Stale state objects
+
+Earlier there was a mention of `stale` property in the navigation state. If the `stale` property is set to `true` or is missing, the state is assumed to be stale. A stale navigation state means that the state object may be partial, such as missing keys or routes, contain invalid routes, or may not be up-to-date. A stale state can be a result of [deep linking](deep-linking.md), r[estoring from a persisted state](state-persistence.md) etc.
+
+If you're accessing the navigation state of a navigator using the built-in APIs such as [`useNavigationState()`](use-navigation-state.md), [`navigation.getState()`](navigation-object.md#getstate) etc., the state object is guaranteed to be complete and not stale. However, if you try to access a child navigator's state with the `state` property on the `route` object, it maybe a stale or partial state object. So it's not recommended to use this property directly.
+
+Using the [`ref.getRootState()`](navigation-container.md#getrootstate) API will always return a complete and up-to-date state object for the current navigation tree, including any nested child navigators.
+
+When React Navigation encounters stale or partial state, it will automatically fix it up before using it. This includes adding missing keys, removing any invalid routes, ensuring the `index` is correct etc. This process of fixing stale state is called **rehydration**. If you're writing a [custom router](custom-routers.md), the `getRehydratedState` method lets you write custom rehydration logic to fix up state objects.
+
+For example, `index` should be the last route in a stack, and if a different value was specified, React Navigation fixes it. For example, if you wanted to reset your app's navigation state to have it display the `Profile` route, and have the `Home` route displayed upon going back, and dispatched the following action:
+
+```js
+navigation.reset({
+ index: 0,
+ routes: [{ name: 'Home' }, { name: 'Profile' }],
+});
+```
+
+React Navigation would correct `index` to `1` before the routes are displayed.
+
+This feature comes handy when doing operations such as [reset](navigation-actions.md#reset), [providing a initial state](navigation-container.md#initialstate) etc., as you can safely omit many properties from the navigation state object and relying on React Navigation to add those properties for you, making your code simpler. For example, you can only provide a `routes` array without any keys and React Navigation will automatically add everything that's needed to make it work:
+
+```js
+const state = {
+ routes: [{ name: 'Home' }, { name: 'Profile' }],
+};
+```
+
+After rehydration, it'll look something like this:
+
+```js
+const state = {
+ type: 'stack',
+ key: 'stack-1',
+ routeNames: ['Home', 'Profile', 'Settings'],
+ routes: [
+ { key: 'home-1', name: 'Home' },
+ { key: 'settings-1', name: 'Settings' },
+ ],
+ index: 1,
+ stale: false,
+};
+```
+
+Here, React Navigation filled in the missing bits such as keys, route names, index etc.
+
+It's also possible to provide invalid data such as non-existent screens and it'll be fixed automatically. While it's not recommended to write code with invalid state objects, it can be super useful if you do things like [state persistence](state-persistence.md), where the configured screens might have changed after an update, which could cause problems if React Navigation didn't fix the state object automatically.
+
+:::tip
+
+If you want React Navigation to fix invalid state, you need to make sure that you don't have `stale: false` in the state object. State objects with `stale: false` are assumed to be valid state objects and React Navigation won't attempt to fix them.
+
+:::
+
+When you're providing a state object in [`initialState`](navigation-container.md#initialstate), React Navigation will always assume that it's a stale state object, since navigation configuration may have changed since the last time. This makes sure that things like [state persistence](state-persistence.md) work smoothly without extra manipulation of the state object.
diff --git a/versioned_docs/version-8.x/navigator.md b/versioned_docs/version-8.x/navigator.md
new file mode 100644
index 00000000000..00ba96a354f
--- /dev/null
+++ b/versioned_docs/version-8.x/navigator.md
@@ -0,0 +1,417 @@
+---
+id: navigator
+title: Navigator
+sidebar_label: Navigator
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+A navigator is responsible for managing and rendering a set of screens. It can be created using the `createXNavigator` functions, e.g. [`createStackNavigator`](stack-navigator.md), [`createNativeStackNavigator`](native-stack-navigator.md), [`createBottomTabNavigator`](bottom-tab-navigator.md), [`createMaterialTopTabNavigator`](material-top-tab-navigator.md), [`createDrawerNavigator`](drawer-navigator.md) etc.:
+
+
+
+
+```js
+const MyStack = createNativeStackNavigator({
+ screens: {
+ Home: HomeScreen,
+ Profile: ProfileScreen,
+ },
+});
+```
+
+
+
+
+```js
+const Stack = createNativeStackNavigator();
+
+function MyStack() {
+ return (
+
+
+
+
+ );
+}
+```
+
+
+
+
+In addition to the built-in navigators, it's also possible to build your custom navigators or use third-party navigators. See [custom navigators](custom-navigators.md) for more details.
+
+## Configuration
+
+Different navigators accept different configuration options. You can find the list of options for each navigator in their respective documentation.
+
+There is a set of common configurations that are shared across all navigators:
+
+### Initial route name
+
+The name of the route to render on the first load of the navigator.
+
+
+
+
+```js
+const MyStack = createNativeStackNavigator({
+ // highlight-next-line
+ initialRouteName: 'Home',
+ screens: {
+ Home: HomeScreen,
+ Profile: ProfileScreen,
+ },
+});
+```
+
+
+
+
+```js
+const Stack = createNativeStackNavigator();
+
+function MyStack() {
+ return (
+
+
+
+
+ );
+}
+```
+
+
+
+
+### Layout
+
+A layout is a wrapper around the navigator. It can be useful for augmenting the navigators with additional UI with a wrapper.
+
+The difference from adding a wrapper around the navigator manually is that the code in a layout callback has access to the navigator's state, options etc.
+
+It takes a function that returns a React element:
+
+
+
+
+```js
+const MyStack = createNativeStackNavigator({
+ // highlight-start
+ layout: ({ children, state, descriptors, navigation }) => (
+
+
+ {children}
+
+ ),
+ // highlight-end
+ screens: {
+ Home: HomeScreen,
+ Profile: ProfileScreen,
+ },
+});
+```
+
+
+
+
+```js
+const Stack = createNativeStackNavigator();
+
+function MyStack() {
+ return (
+ (
+
+
+ {children}
+
+ )}
+ // highlight-end
+ >
+
+
+
+ );
+}
+```
+
+
+
+
+### Screen options
+
+Default options to use for all the screens in the navigator. It accepts either an object or a function returning an object:
+
+
+
+
+```js
+const MyStack = createNativeStackNavigator({
+ // highlight-start
+ screenOptions: {
+ headerShown: false,
+ },
+ // highlight-end
+ screens: {
+ Home: HomeScreen,
+ Profile: ProfileScreen,
+ },
+});
+```
+
+
+
+
+```js
+const Stack = createNativeStackNavigator();
+
+function MyStack() {
+ return (
+
+
+
+
+ );
+}
+```
+
+
+
+
+See [Options for screens](screen-options.md) for more details and examples.
+
+### Screen listeners
+
+Event listeners can be used to subscribe to various events emitted for the screen. See [`screenListeners` prop on the navigator](navigation-events.md#screenlisteners-prop-on-the-navigator) for more details.
+
+### Screen layout
+
+A screen layout is a wrapper around each screen in the navigator. It makes it easier to provide things such as an error boundary and suspense fallback for all screens in the navigator, or wrap each screen with additional UI.
+
+It takes a function that returns a React element:
+
+
+
+
+```js
+const MyStack = createNativeStackNavigator({
+ // highlight-start
+ screenLayout: ({ children }) => (
+
+
+ Loading…
+
+ }
+ >
+ {children}
+
+
+ ),
+ // highlight-end
+ screens: {
+ Home: HomeScreen,
+ Profile: ProfileScreen,
+ },
+});
+```
+
+
+
+
+```js
+const Stack = createNativeStackNavigator();
+
+function MyStack() {
+ return (
+ (
+
+
+ Loading…
+
+ }
+ >
+ {children}
+
+
+ )}
+ // highlight-end
+ >
+
+
+
+ );
+}
+```
+
+
+
+
+### Router
+
+Routers can be customized with the `router` prop on navigator to override how navigation actions are handled.
+
+It takes a function that receives the original router and returns an object with overrides:
+
+
+
+
+```js
+const MyStack = createNativeStackNavigator({
+ // highlight-start
+ router: (original) => ({
+ getStateForAction(state, action) {
+ if (action.type === 'SOME_ACTION') {
+ // Custom logic
+ }
+
+ // Fallback to original behavior
+ return original.getStateForAction(state, action);
+ },
+ }),
+ // highlight-end
+ screens: {
+ Home: HomeScreen,
+ Profile: ProfileScreen,
+ },
+});
+```
+
+
+
+
+```js
+const Stack = createNativeStackNavigator();
+
+function MyStack() {
+ return (
+ ({
+ getStateForAction(state, action) {
+ if (action.type === 'SOME_ACTION') {
+ // Custom logic
+ }
+
+ // Fallback to original behavior
+ return original.getStateForAction(state, action);
+ },
+ })}
+ // highlight-end
+ >
+
+
+
+ );
+}
+```
+
+
+
+
+The function passed to `router` **must be a pure function and cannot reference outside dynamic variables**.
+
+The overrides object is shallow merged with the original router. So you don't need to specify all properties of the router, only the ones you want to override.
+
+See [custom routers](custom-routers.md) for more details on routers.
+
+### Route names change behavior
+
+:::warning
+
+This API is experimental and may change in a minor release.
+
+:::
+
+When the list of available routes in a navigator changes dynamically, e.g. based on conditional rendering, looping over data from an API etc., the navigator needs to update the [navigation state](navigation-state.md) according to the new list of routes.
+
+By default, it works as follows:
+
+- Any routes not present in the new available list of routes are removed from the navigation state
+- If the currently focused route is still present in the new available list of routes, it remains focused.
+- If the currently focused route has been removed, but the navigation state has other routes that are present in the new available list, the first route in from the list of rendered routes becomes focused.
+- If none of the routes in the navigation state are present in the new available list of routes, one of the following things can happen based on the `UNSTABLE_routeNamesChangeBehavior` prop:
+ - `'firstMatch'` - The first route defined in the new list of routes becomes focused. This is the default behavior based on [`getStateForRouteNamesChange`](custom-routers.md) in the router.
+ - `'lastUnhandled'` - The last state that was unhandled due to conditional rendering is restored.
+
+Example cases where state might have been unhandled:
+
+- Opened a deep link to a screen, but a login screen was shown.
+- Navigated to a screen containing a navigator, but a different screen was shown.
+- Reset the navigator to a state with different routes not matching the available list of routes.
+
+In these cases, specifying `'lastUnhandled'` will reuse the unhandled state if present. If there's no unhandled state, it will fallback to `'firstMatch'` behavior.
+
+Caveats:
+
+- Direct navigation is only handled for `NAVIGATE` actions.
+- Unhandled state is restored only if the current state becomes invalid, i.e. it doesn't contain any currently defined screens.
+
+Example usage:
+
+
+
+
+```js
+const RootStack = createNativeStackNavigator({
+ // highlight-next-line
+ UNSTABLE_routeNamesChangeBehavior: 'lastUnhandled',
+ screens: {
+ Home: {
+ if: useIsSignedIn,
+ screen: HomeScreen,
+ },
+ SignIn: {
+ if: useIsSignedOut,
+ screen: SignInScreen,
+ options: {
+ title: 'Sign in',
+ },
+ },
+ },
+});
+```
+
+
+
+
+```js
+
+ {isSignedIn ? (
+
+ ) : (
+
+ )}
+
+```
+
+
+
+
+The most common use case for this is to [show the correct screen based on authentication based on deep link](auth-flow.md#handling-deep-links-after-auth).
diff --git a/versioned_docs/version-8.x/nesting-navigators.md b/versioned_docs/version-8.x/nesting-navigators.md
new file mode 100755
index 00000000000..7e2eb31b036
--- /dev/null
+++ b/versioned_docs/version-8.x/nesting-navigators.md
@@ -0,0 +1,1046 @@
+---
+id: nesting-navigators
+title: Nesting navigators
+sidebar_label: Nesting navigators
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+Nesting navigators means rendering a navigator inside a screen of another navigator, for example:
+
+```js name="Nested navigators" snack static2dynamic
+import * as React from 'react';
+import { Text, View } from 'react-native';
+import {
+ createStaticNavigation,
+ useNavigation,
+} from '@react-navigation/native';
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
+import { Button } from '@react-navigation/elements';
+
+function ProfileScreen() {
+ return (
+
+ Profile Screen
+
+ );
+}
+
+function FeedScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ Feed Screen
+
+
+ );
+}
+
+function MessagesScreen() {
+ return (
+
+ Messages Screen
+
+ );
+}
+
+// codeblock-focus-start
+const HomeTabs = createBottomTabNavigator({
+ screens: {
+ Feed: createBottomTabScreen({
+ screen: FeedScreen,
+ }),
+ Messages: createBottomTabScreen({
+ screen: MessagesScreen,
+ }),
+ },
+});
+
+const RootStack = createNativeStackNavigator({
+ screens: {
+ Home: createNativeStackScreen({
+ // highlight-next-line
+ screen: HomeTabs,
+ options: {
+ headerShown: false,
+ },
+ }),
+ Profile: createNativeStackScreen({
+ screen: ProfileScreen,
+ }),
+ },
+});
+// codeblock-focus-end
+
+const Navigation = createStaticNavigation(RootStack);
+
+export default function App() {
+ return ;
+}
+```
+
+In the above example, `HomeTabs` contains a tab navigator. It is also used for the `Home` screen in your stack navigator in `RootStack`. So here, a tab navigator is nested inside a stack navigator:
+
+- `RootStack` (Stack navigator)
+ - `HomeTabs` (Tab navigator)
+ - `Feed` (screen)
+ - `Messages` (screen)
+ - `Profile` (screen)
+
+Nesting navigators work very much like nesting regular components. To achieve the behavior you want, it's often necessary to nest multiple navigators.
+
+## How nesting navigators affects the behaviour
+
+When nesting navigators, there are some things to keep in mind:
+
+### Each navigator keeps its own navigation history
+
+For example, when you press the back button when inside a screen in a nested stack navigator, it'll go back to the previous screen inside the nested stack even if there's another navigator as the parent.
+
+### Each navigator has its own options
+
+For example, specifying a `title` option in a screen nested in a child navigator won't affect the title shown in a parent navigator.
+
+If you want to achieve this behavior, see the guide for [screen options with nested navigators](screen-options-resolution.md#setting-parent-screen-options-based-on-child-navigators-state). this could be useful if you are rendering a tab navigator inside a stack navigator and want to show the title of the active screen inside the tab navigator in the header of the stack navigator.
+
+### Each screen in a navigator has its own params
+
+For example, any `params` passed to a screen in a nested navigator are in the `route` object of that screen and aren't accessible from a screen in a parent or child navigator.
+
+If you need to access params of the parent screen from a child screen, you can use [React Context](https://react.dev/reference/react/useContext) to expose params to children.
+
+### Navigation actions are handled by current navigator and bubble up if couldn't be handled
+
+For example, if you're calling `navigation.goBack()` in a nested screen, it'll only go back in the parent navigator if you're already on the first screen of the navigator. Other actions such as `navigate` work similarly, i.e. navigation will happen in the nested navigator and if the nested navigator couldn't handle it, then the parent navigator will try to handle it. In the above example, when calling `navigate('Messages')`, inside `Feed` screen, the nested tab navigator will handle it, but if you call `navigate('Settings')`, the parent stack navigator will handle it.
+
+### Navigator specific methods are available in the navigators nested inside
+
+For example, if you have a stack inside a drawer navigator, the drawer's `openDrawer`, `closeDrawer`, `toggleDrawer` methods etc. will also be available on the `navigation` object in the screens inside the stack navigator. But say you have a stack navigator as the parent of the drawer, then the screens inside the stack navigator won't have access to these methods, because they aren't nested inside the drawer.
+
+Similarly, if you have a tab navigator inside stack navigator, the screens in the tab navigator will get the `push` and `replace` methods for stack in their `navigation` object.
+
+If you need to dispatch actions to the nested child navigators from a parent, you can use [`navigation.dispatch`](navigation-object.md#dispatch):
+
+```js
+navigation.dispatch(DrawerActions.toggleDrawer());
+```
+
+### Nested navigators don't receive parent's events
+
+For example, if you have a stack navigator nested inside a tab navigator, the screens in the stack navigator won't receive the events emitted by the parent tab navigator such as (`tabPress`) when using `navigation.addListener`.
+
+To receive events from the parent navigator, you can explicitly listen to parent's events with [`navigation.getParent`](navigation-object.md#getparent):
+
+
+
+
+```js name="Events from parent" snack
+import * as React from 'react';
+import { Text, View } from 'react-native';
+import {
+ createStaticNavigation,
+ useNavigation,
+} from '@react-navigation/native';
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
+import { Button } from '@react-navigation/elements';
+
+function ProfileScreen() {
+ return (
+
+ Profile Screen
+
+ );
+}
+
+function FeedScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ Feed Screen
+
+
+ );
+}
+
+function MessagesScreen() {
+ const navigation = useNavigation();
+
+ React.useEffect(() => {
+ // codeblock-focus-start
+ const unsubscribe = navigation
+ .getParent('Home')
+ .addListener('tabPress', (e) => {
+ // Do something
+ alert('Tab pressed!');
+ });
+ // codeblock-focus-end
+
+ return unsubscribe;
+ }, [navigation]);
+
+ return (
+
+ Messages Screen
+
+ );
+}
+
+const HomeStack = createNativeStackNavigator({
+ screens: {
+ Feed: FeedScreen,
+ Messages: MessagesScreen,
+ },
+});
+
+const RootTabs = createBottomTabNavigator({
+ screens: {
+ Home: {
+ screen: HomeStack,
+ options: {
+ headerShown: false,
+ },
+ },
+ Profile: ProfileScreen,
+ },
+});
+
+const Navigation = createStaticNavigation(RootTabs);
+
+export default function App() {
+ return ;
+}
+```
+
+
+
+
+```js name="Events from parent" snack
+import * as React from 'react';
+import { Text, View } from 'react-native';
+import { NavigationContainer, useNavigation } from '@react-navigation/native';
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
+import { Button } from '@react-navigation/elements';
+
+function ProfileScreen() {
+ return (
+
+ Profile Screen
+
+ );
+}
+
+function FeedScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ Feed Screen
+
+
+ );
+}
+
+function MessagesScreen() {
+ const navigation = useNavigation();
+
+ React.useEffect(() => {
+ // codeblock-focus-start
+ const unsubscribe = navigation
+ .getParent('Home')
+ .addListener('tabPress', (e) => {
+ // Do something
+ alert('Tab pressed!');
+ });
+ // codeblock-focus-end
+
+ return unsubscribe;
+ }, [navigation]);
+
+ return (
+
+ Messages Screen
+
+ );
+}
+
+const Tab = createBottomTabNavigator();
+const Stack = createNativeStackNavigator();
+
+function HomeStack() {
+ return (
+
+
+
+
+ );
+}
+
+function RootTabs() {
+ return (
+
+
+
+
+ );
+}
+
+export default function App() {
+ return (
+
+
+
+ );
+}
+```
+
+
+
+
+Here `'Home'` refers to the name of the parent screen that contains the tab navigator whose event you want to listen to.
+
+### Parent navigator's UI is rendered on top of child navigator
+
+For example, when you nest a stack navigator inside a drawer navigator, you'll see that the drawer appears above the stack navigator's header. However, if you nest the drawer navigator inside a stack, the drawer will appear below the header of the stack. This is an important point to consider when deciding how to nest your navigators.
+
+In your app, you will probably use these patterns depending on the behavior you want:
+
+- Tab navigator nested inside the initial screen of stack navigator - New screens cover the tab bar when you push them.
+- Drawer navigator nested inside the initial screen of stack navigator with the initial screen's stack header hidden - The drawer can only be opened from the first screen of the stack.
+- Stack navigators nested inside each screen of drawer navigator - The drawer appears over the header from the stack.
+- Stack navigators nested inside each screen of tab navigator - The tab bar is always visible. Usually pressing the tab again also pops the stack to top.
+
+## Navigating to a screen in a nested navigator
+
+Consider the following example:
+
+
+
+
+```js name="Navigating to nested screen" snack
+import * as React from 'react';
+import { Text, View } from 'react-native';
+import {
+ createStaticNavigation,
+ useNavigation,
+} from '@react-navigation/native';
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
+import { Button } from '@react-navigation/elements';
+
+function HomeScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ Home Screen
+
+
+
+ );
+}
+
+function FeedScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ Feed Screen
+
+
+ );
+}
+
+function MessagesScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ Messages Screen
+
+
+ );
+}
+
+// codeblock-focus-start
+const MoreTabs = createBottomTabNavigator({
+ screens: {
+ Feed: FeedScreen,
+ Messages: MessagesScreen,
+ },
+});
+
+const RootStack = createNativeStackNavigator({
+ screens: {
+ Home: HomeScreen,
+ More: {
+ screen: MoreTabs,
+ options: {
+ headerShown: false,
+ },
+ },
+ },
+});
+// codeblock-focus-end
+
+const Navigation = createStaticNavigation(RootStack);
+
+export default function App() {
+ return ;
+}
+```
+
+
+
+
+```js name="Navigating to nested screen" snack
+import * as React from 'react';
+import { Text, View } from 'react-native';
+import { NavigationContainer, useNavigation } from '@react-navigation/native';
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
+import { Button } from '@react-navigation/elements';
+
+function HomeScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ Home Screen
+
+
+
+ );
+}
+
+function FeedScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ Feed Screen
+
+
+ );
+}
+
+function MessagesScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ Messages Screen
+
+
+ );
+}
+
+const Tab = createBottomTabNavigator();
+const Stack = createNativeStackNavigator();
+
+// codeblock-focus-start
+function MoreTabs() {
+ return (
+
+
+
+
+ );
+}
+
+function RootStack() {
+ return (
+
+
+
+
+ );
+}
+// codeblock-focus-end
+
+export default function App() {
+ return (
+
+
+
+ );
+}
+```
+
+
+
+
+Here, you might want to navigate to the `More` screen (which contains `MoreTabs`) from your `HomeScreen` component:
+
+```js
+navigation.navigate('More');
+```
+
+It works, and the initial screen inside the `MoreTabs` component is shown, which is `Feed`. But sometimes you may want to control the screen that should be shown upon navigation. To achieve it, you can pass the name of the screen in params:
+
+```js
+navigation.navigate('More', { screen: 'Messages' });
+```
+
+Now, the `Messages` screen will be rendered instead of `Feed` upon navigation.
+
+### Passing params to a screen in a nested navigator
+
+You can also pass params by specifying a `params` key:
+
+
+
+
+```js name="Navigating to nested screen" snack
+import * as React from 'react';
+import { Text, View } from 'react-native';
+import {
+ createStaticNavigation,
+ useNavigation,
+} from '@react-navigation/native';
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
+import { Button } from '@react-navigation/elements';
+
+function HomeScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ Home Screen
+
+
+ );
+}
+
+function FeedScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ Feed Screen
+
+
+ );
+}
+
+function MessagesScreen({ route }) {
+ const navigation = useNavigation();
+
+ return (
+
+ Messages Screen
+ User: {route.params.user}
+
+
+ );
+}
+
+const MoreTabs = createBottomTabNavigator({
+ screens: {
+ Feed: FeedScreen,
+ Messages: MessagesScreen,
+ },
+});
+
+const RootStack = createNativeStackNavigator({
+ screens: {
+ Home: HomeScreen,
+ More: {
+ screen: MoreTabs,
+ options: {
+ headerShown: false,
+ },
+ },
+ },
+});
+
+const Navigation = createStaticNavigation(RootStack);
+
+export default function App() {
+ return ;
+}
+```
+
+
+
+
+```js name="Navigating to nested screen" snack
+import * as React from 'react';
+import { Text, View } from 'react-native';
+import { NavigationContainer, useNavigation } from '@react-navigation/native';
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
+import { Button } from '@react-navigation/elements';
+
+function HomeScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ Home Screen
+
+
+ );
+}
+
+function FeedScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ Feed Screen
+
+
+ );
+}
+
+function MessagesScreen({ route }) {
+ const navigation = useNavigation();
+
+ return (
+
+ Messages Screen
+ User: {route.params.user}
+
+
+ );
+}
+
+const Tab = createBottomTabNavigator();
+const Stack = createNativeStackNavigator();
+
+function MoreTabs() {
+ return (
+
+
+
+
+ );
+}
+
+function RootStack() {
+ return (
+
+
+
+
+ );
+}
+
+export default function App() {
+ return (
+
+
+
+ );
+}
+```
+
+
+
+
+If the navigator was already rendered, navigating to another screen will push a new screen in case of stack navigator.
+
+You can follow a similar approach for deeply nested screens. Note that the second argument to `navigate` here is just `params`, so you can do something like:
+
+```js
+navigation.navigate('Home', {
+ screen: 'Settings',
+ params: {
+ screen: 'Sound',
+ params: {
+ screen: 'Media',
+ },
+ },
+});
+```
+
+In the above case, you're navigating to the `Media` screen, which is in a navigator nested inside the `Sound` screen, which is in a navigator nested inside the `Settings` screen.
+
+:::warning
+
+The `screen` and related params are reserved for internal use and are managed by React Navigation. While you can access `route.params.screen` etc. in the parent screens, relying on them may lead to unexpected behavior.
+
+:::
+
+### Rendering initial route defined in the navigator
+
+By default, when you navigate a screen in the nested navigator, the specified screen is used as the initial screen and the `initialRouteName` prop on the navigator is ignored.
+
+If you need to render the initial route specified in the navigator, you can disable the behaviour of using the specified screen as the initial screen by setting `initial: false`:
+
+```js
+navigation.navigate('Root', {
+ screen: 'Settings',
+ initial: false,
+});
+```
+
+This affects what happens when pressing the back button. When there's an initial screen, the back button will take the user there.
+
+## Avoiding multiple headers when nesting
+
+When nesting multiple stack, drawer or bottom tab navigators, headers from both child and parent navigators would be shown. However, usually it's more desirable to show the header in the child navigator and hide the header in the screen of the parent navigator.
+
+To achieve this, you can hide the header in the screen containing the navigator using the `headerShown: false` option.
+
+For example:
+
+
+
+
+```js name="Nested navigators" snack
+import * as React from 'react';
+import { Text, View } from 'react-native';
+import {
+ createStaticNavigation,
+ useNavigation,
+} from '@react-navigation/native';
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
+import { Button } from '@react-navigation/elements';
+
+function ProfileScreen() {
+ return (
+
+ Profile Screen
+
+ );
+}
+
+function FeedScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ Feed Screen
+
+
+ );
+}
+
+function MessagesScreen() {
+ return (
+
+ Messages Screen
+
+ );
+}
+
+// codeblock-focus-start
+const HomeTabs = createBottomTabNavigator({
+ screens: {
+ Feed: FeedScreen,
+ Messages: MessagesScreen,
+ },
+});
+
+const RootStack = createNativeStackNavigator({
+ screens: {
+ Home: {
+ screen: HomeTabs,
+ options: {
+ // highlight-next-line
+ headerShown: false,
+ },
+ },
+ Profile: ProfileScreen,
+ },
+});
+// codeblock-focus-end
+
+const Navigation = createStaticNavigation(RootStack);
+
+export default function App() {
+ return ;
+}
+```
+
+
+
+
+```js name="Nested navigators" snack
+import * as React from 'react';
+import { Text, View } from 'react-native';
+import { NavigationContainer, useNavigation } from '@react-navigation/native';
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
+import { Button } from '@react-navigation/elements';
+
+function ProfileScreen() {
+ return (
+
+ Profile Screen
+
+ );
+}
+
+function FeedScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ Feed Screen
+
+
+ );
+}
+
+function MessagesScreen() {
+ return (
+
+ Messages Screen
+
+ );
+}
+
+const Tab = createBottomTabNavigator();
+const Stack = createNativeStackNavigator();
+
+// codeblock-focus-start
+function HomeTabs() {
+ return (
+
+
+
+
+ );
+}
+
+function RootStack() {
+ return (
+
+
+
+
+ );
+}
+// codeblock-focus-end
+
+export default function App() {
+ return (
+
+
+
+ );
+}
+```
+
+
+
+
+In these examples, we have used a bottom tab navigator directly nested inside another stack navigator, but the same principle applies when there are other navigators in the middle, for example: stack navigator inside a tab navigator which is inside another stack navigator, stack navigator inside drawer navigator etc.
+
+If you don't want headers in any of the navigators, you can specify `headerShown: false` in all of the navigators:
+
+
+
+
+```js
+const HomeTabs = createBottomTabNavigator({
+ // highlight-start
+ screenOptions: {
+ headerShown: false,
+ },
+ // highlight-end
+ screens: {
+ Feed: FeedScreen,
+ Messages: MessagesScreen,
+ },
+});
+
+const RootStack = createStackNavigator({
+ // highlight-start
+ screenOptions: {
+ headerShown: false,
+ },
+ // highlight-end
+ screens: {
+ Home: HomeTabs,
+ Profile: ProfileScreen,
+ },
+});
+```
+
+
+
+
+```js
+function HomeTabs() {
+ return (
+
+
+
+
+ );
+}
+
+function RootStack() {
+ return (
+
+
+
+
+ );
+}
+```
+
+
+
+
+## Best practices when nesting
+
+We recommend to reduce nesting navigators to minimal. Try to achieve the behavior you want with as little nesting as possible. Nesting has many downsides:
+
+- It results in deeply nested view hierarchy which can cause memory and performance issues in lower end devices
+- Nesting same type of navigators (e.g. tabs inside tabs, drawer inside drawer etc.) might lead to a confusing UX
+- With excessive nesting, code becomes difficult to follow when navigating to nested screens, configuring deep link etc.
+
+Think of nesting navigators as a way to achieve the UI you want rather than a way to organize your code. If you want to create separate group of screens for organization, instead of using separate navigators, you can use the [`Group`](group.md) component for dynamic configuration or [`groups` property](static-configuration.md#groups) for static configuration.
+
+
+
+
+```js
+const MyStack = createStackNavigator({
+ screens: {
+ // Common screens
+ },
+ groups: {
+ // Common modal screens
+ Modal: {
+ screenOptions: {
+ presentation: 'modal',
+ },
+ screens: {
+ Help,
+ Invite,
+ },
+ },
+ // Screens for logged in users
+ User: {
+ if: useIsLoggedIn,
+ screens: {
+ Home,
+ Profile,
+ },
+ },
+ // Auth screens
+ Guest: {
+ if: useIsGuest,
+ screens: {
+ SignIn,
+ SignUp,
+ },
+ },
+ },
+});
+```
+
+
+
+
+```js
+
+ {isLoggedIn ? (
+ // Screens for logged in users
+
+
+
+
+ ) : (
+ // Auth screens
+
+
+
+
+ )}
+ {/* Common modal screens */}
+
+
+
+
+
+```
+
+
+
diff --git a/versioned_docs/version-8.x/next-steps.md b/versioned_docs/version-8.x/next-steps.md
new file mode 100755
index 00000000000..6efc2fb1717
--- /dev/null
+++ b/versioned_docs/version-8.x/next-steps.md
@@ -0,0 +1,24 @@
+---
+id: next-steps
+title: Next steps
+sidebar_label: Next steps
+---
+
+You are now familiar with how to create a stack navigator, configure it on your screen components, navigate between routes, and display [modals](modal.md). Stack navigators and their related APIs will be the most frequently used tools in your React Navigation toolbelt, but there are problems that they don't solve. For example, you can't build tab-based navigation using a stack navigator — for that, you need to use a [Bottom Tabs Navigator](bottom-tab-navigator.md).
+
+You can check out the **"Navigators"** section in the sidebar to learn more about the different navigators available in React Navigation.
+
+The rest of the documentation is organized around specific use cases, so you can jump between the sections under **"Guides"** as the need arises (but it also wouldn't hurt you to familiarize yourself with them pre-emptively!).
+
+Some of the guides you may want to check out are:
+
+- [Authentication flows](auth-flow.md): How to handle authentication flows in your app.
+- [Supporting safe areas](handling-safe-area.md): How to handle safe areas such as statusbar in your app.
+- [Deep linking](deep-linking.md): How to handle deep linking and universal links in your app.
+- [Themes](themes.md): How to customize the look and feel of various UI elements.
+- [Testing with Jest](testing.md): How to test your navigation components.
+- [Configuring TypeScript](typescript.md): How to configure TypeScript for React Navigation.
+
+While most users won't need to do this, if you are curious and want to learn more about how React Navigation works, it's recommended to work through the [Navigation State reference](navigation-state.md) and [Build your own Navigator](custom-navigators.md) sections.
+
+Good luck!
diff --git a/versioned_docs/version-8.x/params.md b/versioned_docs/version-8.x/params.md
new file mode 100755
index 00000000000..db1ccfeb7dc
--- /dev/null
+++ b/versioned_docs/version-8.x/params.md
@@ -0,0 +1,482 @@
+---
+id: params
+title: Passing parameters to routes
+sidebar_label: Passing parameters to routes
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+Remember when I said "more on that later when we talk about `params`!"? Well, the time has come.
+
+Now that we know how to create a stack navigator with some routes and [navigate between those routes](navigating.md), let's look at how we can pass data to routes when we navigate to them.
+
+There are two pieces to this:
+
+1. Pass params to a route by putting them in an object as a second parameter to the `navigation.navigate` function: `navigation.navigate('RouteName', { /* params go here */ })`
+2. Read the params in your screen component: `route.params`.
+
+:::note
+
+We recommend that the params you pass are JSON-serializable. That way, you'll be able to use [state persistence](state-persistence.md) and your screen components will have the right contract for implementing [deep linking](deep-linking.md).
+
+:::
+
+```js name="Passing params" snack static2dynamic
+import * as React from 'react';
+import { View, Text } from 'react-native';
+import {
+ createStaticNavigation,
+ useNavigation,
+} from '@react-navigation/native';
+import {
+ createNativeStackNavigator,
+ createNativeStackScreen,
+} from '@react-navigation/native-stack';
+import { Button } from '@react-navigation/elements';
+
+// codeblock-focus-start
+function HomeScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ Home Screen
+
+
+ );
+}
+
+function DetailsScreen({ route }) {
+ const navigation = useNavigation();
+
+ /* 2. Get the param */
+ // highlight-next-line
+ const { itemId, otherParam } = route.params;
+
+ return (
+
+ Details Screen
+ itemId: {JSON.stringify(itemId)}
+ otherParam: {JSON.stringify(otherParam)}
+
+
+
+
+ );
+}
+// codeblock-focus-end
+
+const RootStack = createNativeStackNavigator({
+ screens: {
+ Home: createNativeStackScreen({
+ screen: HomeScreen,
+ }),
+ Details: createNativeStackScreen({
+ screen: DetailsScreen,
+ }),
+ },
+});
+
+const Navigation = createStaticNavigation(RootStack);
+
+export default function App() {
+ return ;
+}
+```
+
+
+
+## Initial params
+
+You can also pass some initial params to a screen. If you didn't specify any params when navigating to this screen, the initial params will be used. They are also shallow merged with any params that you pass. Initial params can be specified in `initialParams`:
+
+
+
+
+```js
+createNativeStackScreen({
+ Details: {
+ screen: DetailsScreen,
+ // highlight-next-line
+ initialParams: { itemId: 42 },
+ },
+});
+```
+
+
+
+
+```js
+
+```
+
+
+
+
+## Updating params
+
+Screens can also update their params, like they can update their state. The `navigation.setParams` method lets you update the params of a screen. Refer to the [API reference for `setParams`](navigation-object.md#setparams) for more details.
+
+Basic usage:
+
+```js name="Updating params" snack static2dynamic
+import * as React from 'react';
+import { Text, View } from 'react-native';
+import {
+ createStaticNavigation,
+ useNavigation,
+} from '@react-navigation/native';
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+import { Button } from '@react-navigation/elements';
+
+function HomeScreen({ route }) {
+ const navigation = useNavigation();
+ const { itemId } = route.params;
+
+ return (
+
+ Home Screen
+ itemId: {JSON.stringify(itemId)}
+
+
+ );
+}
+
+const RootStack = createNativeStackNavigator({
+ screens: {
+ Home: createNativeStackScreen({
+ screen: HomeScreen,
+ initialParams: { itemId: 42 },
+ }),
+ },
+});
+
+const Navigation = createStaticNavigation(RootStack);
+
+export default function App() {
+ return ;
+}
+```
+
+The `setParams` method merges the new params with the existing ones. To replace the existing params, you can use [`replaceParams`](navigation-object.md#replaceparams) instead.
+
+To update params and push a new entry to the history stack, you can use [`pushParams`](navigation-object.md#pushparams). This is useful for scenarios like:
+
+- A product listing page with filters, where changing filters should create a new history entry so users can go back to previous filter states
+- A screen with a custom modal component whose state should be reflected in the URL and history
+
+```js
+navigation.pushParams({ filter: 'new' });
+```
+
+:::note
+
+Avoid using `setParams`, `replaceParams`, or `pushParams` to update screen options such as `title` etc. If you need to update options, use [`setOptions`](navigation-object.md#setoptions) instead.
+
+:::
+
+## Passing params to a previous screen
+
+Params aren't only useful for passing some data to a new screen, but they can also be useful to pass data to a previous screen as well. For example, let's say you have a screen with a "Create post" button, and the button opens a new screen to create a post. After creating the post, you want to pass the data for the post back to the previous screen.
+
+To achieve this, you can use the `popTo` method to go back to the previous screen as well as pass params to it:
+
+```js name="Passing params back" snack static2dynamic
+import * as React from 'react';
+import { Text, View, TextInput } from 'react-native';
+import {
+ createStaticNavigation,
+ useNavigation,
+} from '@react-navigation/native';
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+import { Button } from '@react-navigation/elements';
+
+// codeblock-focus-start
+function HomeScreen({ route }) {
+ const navigation = useNavigation();
+
+ // Use an effect to monitor the update to params
+ // highlight-start
+ React.useEffect(() => {
+ if (route.params?.post) {
+ // Post updated, do something with `route.params.post`
+ // For example, send the post to the server
+ alert('New post: ' + route.params?.post);
+ }
+ }, [route.params?.post]);
+ // highlight-end
+
+ return (
+
+
+ Post: {route.params?.post}
+
+ );
+}
+
+function CreatePostScreen({ route }) {
+ const navigation = useNavigation();
+ const [postText, setPostText] = React.useState('');
+
+ return (
+ <>
+
+
+ >
+ );
+}
+// codeblock-focus-end
+
+const RootStack = createNativeStackNavigator({
+ screens: {
+ Home: createNativeStackScreen({
+ screen: HomeScreen,
+ }),
+ CreatePost: createNativeStackScreen({
+ screen: CreatePostScreen,
+ }),
+ },
+});
+
+const Navigation = createStaticNavigation(RootStack);
+
+export default function App() {
+ return ;
+}
+```
+
+
+
+Here, after you press "Done", the home screen's `route.params` will be updated to reflect the post text that you passed in `navigate`.
+
+## Passing params to a nested screen
+
+If you have nested navigators, you need to pass params a bit differently. For example, say you have a navigator inside the `More` screen and want to pass params to the `Settings` screen inside that navigator. Then you can pass params as the following:
+
+```js name="Passing params to nested screen" snack static2dynamic
+import * as React from 'react';
+import { Text, View, TextInput } from 'react-native';
+import {
+ createStaticNavigation,
+ useNavigation,
+} from '@react-navigation/native';
+import {
+ createNativeStackNavigator,
+ createNativeStackScreen,
+} from '@react-navigation/native-stack';
+import {
+ createBottomTabNavigator,
+ createBottomTabScreen,
+} from '@react-navigation/bottom-tabs';
+import { Button } from '@react-navigation/elements';
+
+function SettingsScreen({ route }) {
+ const navigation = useNavigation();
+ const { userId } = route.params;
+
+ return (
+
+ Settings Screen
+ User ID: {JSON.stringify(userId)}
+
+
+ );
+}
+
+function ProfileScreen() {
+ return (
+
+ Profile Screen
+
+ );
+}
+
+function HomeScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ Home Screen
+
+
+ );
+}
+
+const MoreStack = createNativeStackNavigator({
+ screens: {
+ Settings: createNativeStackScreen({
+ screen: SettingsScreen,
+ }),
+ Profile: createNativeStackScreen({
+ screen: ProfileScreen,
+ }),
+ },
+});
+
+const RootTabs = createBottomTabNavigator({
+ screens: {
+ Home: createBottomTabScreen({
+ screen: HomeScreen,
+ }),
+ More: createBottomTabScreen({
+ screen: MoreStack,
+ }),
+ },
+});
+
+const Navigation = createStaticNavigation(RootTabs);
+
+export default function App() {
+ return ;
+}
+```
+
+See [Nesting navigators](nesting-navigators.md) for more details on nesting.
+
+## Reserved param names
+
+Some param names are reserved by React Navigation as part of the API for nested navigators. The list of the reserved param names are as follows:
+
+- `screen`
+- `params`
+- `initial`
+- `state`
+
+You should avoid using these param names in your code unless navigating to a screen containing a nested navigator. Otherwise it will result in unexpected behavior, such as the screen not being able to access the params you passed. If you need to pass data to a nested screen, use a different names for the param.
+
+## What should be in params
+
+Params serve two main purposes:
+
+- Information required to identify and display data on a screen (e.g. id of an object to be displayed on the screen)
+- State specific to a screen (e.g. sort order, filters, page numbers etc. that can also be changed on the screen)
+
+Params should contain the minimal information required to show a screen, nothing more. The actual data (e.g. user objects) should be in a global store or global cache.
+
+You can think of the route object as a URL. The same principles apply to params. Think of visiting a shopping website; when you see product listings, the URL usually contains category name, type of sort, any filters etc., not the actual list of products displayed on the screen.
+
+For example, say if you have a `Profile` screen. When navigating to it, you might be tempted to pass the user object in the params:
+
+```js
+// Don't do this
+navigation.navigate('Profile', {
+ user: {
+ id: 'jane',
+ firstName: 'Jane',
+ lastName: 'Done',
+ age: 25,
+ },
+});
+```
+
+This looks convenient and lets you access the user objects with `route.params.user` without any extra work.
+
+However, this is an anti-pattern. There are many reasons why this is a bad idea:
+
+- The same data is duplicated in multiple places. This can lead to bugs such as the profile screen showing outdated data even if the user object has changed after navigation.
+- Each screen that navigates to the `Profile` screen now needs to know how to fetch the user object - which increases the complexity of the code.
+- URLs to the screen (browser URL on the web, or deep links on native) will contain the user object. This is problematic:
+ 1. Since the user object is in the URL, it's possible to pass a random user object representing a user that doesn't exist or has incorrect data in the profile.
+ 2. If the user object isn't passed or improperly formatted, this could result in crashes as the screen won't know how to handle it.
+ 3. The URL can become very long and unreadable.
+
+A better way is to pass only the ID of the user in params:
+
+```js
+navigation.navigate('Profile', { userId: 'jane' });
+```
+
+Now, you can use the passed `userId` to grab the user from your global cache or fetch it from the API. Using a library such as [React Query](https://tanstack.com/query/) can simplify this process since it makes it easy to fetch and cache your data. This approach helps to avoid the problems mentioned above.
+
+Some examples of what should be in params are:
+
+- IDs such as user id, item id etc., e.g. `navigation.navigate('Profile', { userId: 'Jane' })`
+- State for sorting, filtering data etc. when you have a list of items, e.g. `navigation.navigate('Feeds', { sortBy: 'latest' })`
+- Timestamps, page numbers or cursors for pagination, e.g. `navigation.navigate('Chat', { beforeTime: 1603897152675 })`
+- Data to fill inputs on a screen to compose something, e.g. `navigation.navigate('ComposeTweet', { title: 'Hello world!' })`
+
+## Summary
+
+- [`navigate`](navigation-actions.md#navigate) and [`push`](stack-actions.md#push) accept an optional second argument to let you pass parameters to the route you are navigating to. For example: `navigation.navigate('RouteName', { paramName: 'value' })`.
+- You can read the params through [`route.params`](route-object.md) inside a screen
+- You can update the screen's params with [`navigation.setParams`](navigation-object.md#setparams) or [`navigation.replaceParams`](navigation-object.md#replaceparams)
+- Initial params can be passed via the [`initialParams`](screen.md#initial-params) prop on `Screen` or in the navigator config
+- State such as sort order, filters etc. should be kept in params so that the state is reflected in the URL and can be shared/bookmarked.
+- Params should contain the least amount of data required to identify a screen; for most cases, this means passing the ID of an object instead of passing a full object.
+- Don't keep application specific data or cached data in params; instead, use a global store or cache.
+- Some [param names are reserved](#reserved-param-names) by React Navigation and should be avoided
diff --git a/versioned_docs/version-8.x/pitch.md b/versioned_docs/version-8.x/pitch.md
new file mode 100755
index 00000000000..c126beb56f8
--- /dev/null
+++ b/versioned_docs/version-8.x/pitch.md
@@ -0,0 +1,23 @@
+---
+id: pitch
+title: Pitch & anti-pitch
+sidebar_label: Pitch & anti-pitch
+---
+
+It's useful when considering whether or not to use a project to understand the tradeoffs that the developers of the project made when building it. What problems does it explicitly try to solve for you, and which ones does it ignore? What are the current limitations of the project and common problems that people encounter? These are the kinds of questions that we believe you should have answers to when making an important technology decision for your project, and so we have documented answers to these questions as best we can here, in the form of a "pitch" (why you should use it) and "anti-pitch" (why you should not use it). Please [submit a pull request](https://github.com/react-navigation/react-navigation.github.io) if you believe we have omitted important information!
+
+## Pitch
+
+- React Navigation doesn't include any native code in the library itself, but we use many native libraries such as [Screens](https://github.com/software-mansion/react-native-screens), [Reanimated](https://software-mansion.github.io/react-native-reanimated/), [Gesture Handler](https://software-mansion.github.io/react-native-gesture-handler/) etc. to implement performant animations and gestures. Depending on the navigator, many UI components are written in JavaScript on top of React Native primitives. This has a lot of benefits:
+ - Easy OTA updates
+ - Debuggable
+ - Customizable
+- Most apps heavily customize navigation, to do this with an API that wraps native navigation you will need to write a lot of native code. In React Navigation, we provide navigators written fully with JavaScript (e.g. [Stack Navigator](stack-navigator.md)) and navigators implemented on top of platform navigation primitives (e.g. [Native Stack Navigator](native-stack-navigator.md)). This lets you pick the navigators suitable for your use case, depending on whether you want native platform behavior or full customizability.
+- It's possible to write your own navigators that integrate cleanly with standard navigators, or to fork the standard navigators and create your own version of them with the exact look and feel you want in your app.
+
+## Anti-pitch
+
+- Improvements may require breaking changes. We are working to make ["easy things easy and hard things possible"](https://www.quora.com/What-is-the-origin-of-the-phrase-make-the-easy-things-easy-and-the-hard-things-possible) and this may require us to change the API at times.
+- Some navigators don't directly use the native navigation APIs on iOS and Android; rather, they use the lowest level pieces and then re-creates some subset of the APIs on top. This is a conscious choice in order to make it possible for users to customize any part of the navigation experience (because it's implemented in JavaScript) and to be able to debug issues that they encounter without needing to learn Objective C / Swift / Java / Kotlin.
+ - If you need the exact platform behavior, you can choose to use the navigators that use native platform primitives (e.g. [Native Stack Navigator](native-stack-navigator.md)), or use a different navigation library which provides fully native navigation APIs (e.g. [React Native Navigation](https://github.com/wix/react-native-navigation)).
+- There are other limitations which you may want to consider, see [Limitations](limitations.md) for more details.
diff --git a/versioned_docs/version-8.x/preventing-going-back.md b/versioned_docs/version-8.x/preventing-going-back.md
new file mode 100644
index 00000000000..0d727fdc033
--- /dev/null
+++ b/versioned_docs/version-8.x/preventing-going-back.md
@@ -0,0 +1,96 @@
+---
+id: preventing-going-back
+title: Preventing going back
+sidebar_label: Preventing going back
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+Sometimes you may want to prevent the user from leaving a screen to avoid losing unsaved changes. There are a couple of things you may want to do in this case:
+
+## Prevent the user from leaving the screen
+
+The `usePreventRemove` hook allows you to prevent the user from leaving a screen. See the [`usePreventRemove`](use-prevent-remove.md) docs for more details.
+
+
+Previous approach
+
+Previously, the way to do this was to:
+
+- Override the back button in the header
+- Disable back swipe gesture
+- Override system back button/gesture on Android
+
+However, using the hook has many important differences in addition to being less code:
+
+- It's not coupled to any specific buttons, going back from custom buttons will trigger it as well
+- It's not coupled to any specific actions, any action that removes the route from the state will trigger it
+- It works across nested navigators, e.g. if the screen is being removed due to an action in the parent navigator
+- The user can still swipe back in the stack navigator, however, the swipe will be canceled if the event is prevented
+- It's possible to continue the same action that triggered the event
+
+
+
+## Prevent the user from leaving the app
+
+To be able to prompt the user before they leave the app on Android, you can use the `BackHandler` API from React Native:
+
+```js
+import { Alert, BackHandler } from 'react-native';
+
+// ...
+
+React.useEffect(() => {
+ const onBackPress = () => {
+ Alert.alert(
+ 'Exit App',
+ 'Do you want to exit?',
+ [
+ {
+ text: 'Cancel',
+ onPress: () => {
+ // Do nothing
+ },
+ style: 'cancel',
+ },
+ { text: 'YES', onPress: () => BackHandler.exitApp() },
+ ],
+ { cancelable: false }
+ );
+
+ return true;
+ };
+
+ const backHandler = BackHandler.addEventListener(
+ 'hardwareBackPress',
+ onBackPress
+ );
+
+ return () => backHandler.remove();
+}, []);
+```
+
+On the Web, you can use the `beforeunload` event to prompt the user before they leave the browser tab:
+
+```js
+React.useEffect(() => {
+ const onBeforeUnload = (event) => {
+ // Prevent the user from leaving the page
+ event.preventDefault();
+ event.returnValue = true;
+ };
+
+ window.addEventListener('beforeunload', onBeforeUnload);
+
+ return () => {
+ window.removeEventListener('beforeunload', onBeforeUnload);
+ };
+}, []);
+```
+
+:::warning
+
+The user can still close the app by swiping it away from the app switcher or closing the browser tab. Or the app can be closed by the system due to low memory or other reasons. It's also not possible to prevent leaving the app on iOS. We recommend persisting the data and restoring it when the app is opened again instead of prompting the user before they leave the app.
+
+:::
diff --git a/versioned_docs/version-8.x/route-object.md b/versioned_docs/version-8.x/route-object.md
new file mode 100755
index 00000000000..346af1b4ed1
--- /dev/null
+++ b/versioned_docs/version-8.x/route-object.md
@@ -0,0 +1,85 @@
+---
+id: route-object
+title: Route object reference
+sidebar_label: Route object
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+Each `screen` component in your app is provided with the `route` object as a prop automatically. The prop contains various information regarding current route (place in navigation hierarchy component lives).
+
+- `route`
+ - `key` - Unique key of the screen. Created automatically or added while navigating to this screen.
+ - `name` - Name of the screen. Defined in navigator component hierarchy.
+ - `path` - An optional string containing the path that opened the screen, exists when the screen was opened via a deep link.
+ - `params` - An optional object containing params which is defined while navigating e.g. `navigate('Twitter', { user: 'Dan Abramov' })`.
+
+
+
+
+```js name="Route prop" snack
+import * as React from 'react';
+import { View, Text } from 'react-native';
+import { createStaticNavigation } from '@react-navigation/native';
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+
+const Stack = createNativeStackNavigator({
+ screens: {
+ Profile: ProfileScreen,
+ },
+});
+
+// codeblock-focus-start
+function ProfileScreen({ route }) {
+ return (
+
+ This is the profile screen of the app
+ {route.name}
+
+ );
+}
+// codeblock-focus-end
+
+const Navigation = createStaticNavigation(Stack);
+
+export default function App() {
+ return ;
+}
+```
+
+
+
+
+```js name="Route prop" snack
+import * as React from 'react';
+import { View, Text } from 'react-native';
+import { NavigationContainer } from '@react-navigation/native';
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+
+const Stack = createNativeStackNavigator();
+
+// codeblock-focus-start
+function ProfileScreen({ route }) {
+ return (
+
+ This is the profile screen of the app
+ {route.name}
+
+ );
+}
+// codeblock-focus-end
+
+export default function App() {
+ return (
+
+
+
+
+
+ );
+}
+```
+
+
+
diff --git a/versioned_docs/version-8.x/screen-options-resolution.md b/versioned_docs/version-8.x/screen-options-resolution.md
new file mode 100755
index 00000000000..1a97af4543a
--- /dev/null
+++ b/versioned_docs/version-8.x/screen-options-resolution.md
@@ -0,0 +1,763 @@
+---
+id: screen-options-resolution
+title: Screen options with nested navigators
+sidebar_label: Options with nested navigators
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+In this document we'll explain how [screen options](screen-options.md) work when there are multiple navigators. It's important to understand this so that you put your `options` in the correct place and can properly configure your navigators. If you put them in the wrong place, at best nothing will happen and at worst something confusing and unexpected will happen.
+
+**You can only modify navigation options for a navigator from one of its screen components. This applies equally to navigators that are nested as screens.**
+
+Let's take for example a tab navigator that contains a native stack in each tab. What happens if we set the `options` on a screen inside of the stack?
+
+
+
+
+```js name="Tabs with native stack" snack
+import * as React from 'react';
+import { View } from 'react-native';
+import { createStaticNavigation } from '@react-navigation/native';
+import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+
+function A() {
+ return ;
+}
+
+function B() {
+ return ;
+}
+
+// codeblock-focus-start
+const HomeStackScreen = createNativeStackNavigator({
+ screens: {
+ A: {
+ screen: A,
+ options: {
+ tabBarLabel: 'Home',
+ },
+ },
+ },
+});
+
+const SettingsStackScreen = createNativeStackNavigator({
+ screens: {
+ B: {
+ screen: B,
+ options: {
+ tabBarLabel: 'Settings!',
+ },
+ },
+ },
+});
+
+const Tab = createBottomTabNavigator({
+ screens: {
+ Home: HomeStackScreen,
+ Settings: SettingsStackScreen,
+ },
+});
+// codeblock-focus-end
+
+const Navigation = createStaticNavigation(Tab);
+
+export default function App() {
+ return ;
+}
+```
+
+
+
+
+```js name="Tabs with native stack" snack
+import * as React from 'react';
+import { View } from 'react-native';
+import { NavigationContainer } from '@react-navigation/native';
+import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+
+const Tab = createBottomTabNavigator();
+const HomeStack = createNativeStackNavigator();
+const SettingsStack = createNativeStackNavigator();
+
+function A() {
+ return ;
+}
+
+function B() {
+ return ;
+}
+// codeblock-focus-start
+function HomeStackScreen() {
+ return (
+
+
+
+ );
+}
+
+function SettingsStackScreen() {
+ return (
+
+
+
+ );
+}
+
+export default function App() {
+ return (
+
+
+
+
+
+
+ );
+}
+// codeblock-focus-end
+```
+
+
+
+
+As we mentioned earlier, you can only modify navigation options for a navigator from one of its screen components. `A` and `B` above are screen components in `HomeStack` and `SettingsStack` respectively, not in the tab navigator. So the result will be that the `tabBarLabel` property is not applied to the tab navigator. We can fix this though!
+
+
+
+
+```js name="Tabs with native stack" snack
+import * as React from 'react';
+import { View } from 'react-native';
+import { createStaticNavigation } from '@react-navigation/native';
+import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+
+function A() {
+ return ;
+}
+
+function B() {
+ return ;
+}
+
+const HomeStackScreen = createNativeStackNavigator({
+ screens: {
+ A: A,
+ },
+});
+
+const SettingsStackScreen = createNativeStackNavigator({
+ screens: {
+ B: B,
+ },
+});
+
+// codeblock-focus-start
+const Tab = createBottomTabNavigator({
+ screens: {
+ Home: {
+ screen: HomeStackScreen,
+ options: {
+ tabBarLabel: 'Home!',
+ },
+ },
+ Settings: {
+ screen: SettingsStackScreen,
+ options: {
+ tabBarLabel: 'Settings!',
+ },
+ },
+ },
+});
+// codeblock-focus-start
+
+const Navigation = createStaticNavigation(Tab);
+
+export default function App() {
+ return ;
+}
+```
+
+
+
+
+```js name="Tabs with native stack" snack
+import * as React from 'react';
+import { View } from 'react-native';
+import { NavigationContainer } from '@react-navigation/native';
+import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+
+const Tab = createBottomTabNavigator();
+const HomeStack = createNativeStackNavigator();
+const SettingsStack = createNativeStackNavigator();
+
+function A() {
+ return ;
+}
+
+function B() {
+ return ;
+}
+
+function HomeStackScreen() {
+ return (
+
+
+
+ );
+}
+
+function SettingsStackScreen() {
+ return (
+
+
+
+ );
+}
+
+// codeblock-focus-start
+export default function App() {
+ return (
+
+
+
+
+
+
+ );
+}
+// codeblock-focus-end
+```
+
+
+
+
+When we set the `options` directly on `Screen` components containing the `HomeStack` and `SettingsStack` component, it allows us to control the options for its parent navigator when its used as a screen component. In this case, the options on our stack components configure the label in the tab navigator that renders the stacks.
+
+## Setting parent screen options based on child navigator's state
+
+Imagine the following configuration:
+
+
+
+
+```js name="Parent options from a child" snack
+import * as React from 'react';
+import { View } from 'react-native';
+import {
+ createStaticNavigation,
+ useNavigation,
+} from '@react-navigation/native';
+import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+import { Button } from '@react-navigation/elements';
+
+function FeedScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+
+
+ );
+}
+
+function ProfileScreen() {
+ return ;
+}
+
+function AccountScreen() {
+ return ;
+}
+
+function SettingsScreen() {
+ return ;
+}
+
+// codeblock-focus-start
+const HomeTabs = createBottomTabNavigator({
+ screens: {
+ Feed: FeedScreen,
+ Profile: ProfileScreen,
+ Account: AccountScreen,
+ },
+});
+
+const RootStack = createNativeStackNavigator({
+ screens: {
+ Home: HomeTabs,
+ Settings: SettingsScreen,
+ },
+});
+
+const Navigation = createStaticNavigation(RootStack);
+
+export default function App() {
+ return ;
+}
+// codeblock-focus-end
+```
+
+
+
+
+```js name="Parent options from a child" snack
+import * as React from 'react';
+import { View } from 'react-native';
+import { NavigationContainer, useNavigation } from '@react-navigation/native';
+import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+import { Button } from '@react-navigation/elements';
+
+function FeedScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+
+
+ );
+}
+
+function ProfileScreen() {
+ return ;
+}
+
+function AccountScreen() {
+ return ;
+}
+
+function SettingsScreen() {
+ return ;
+}
+
+// codeblock-focus-start
+const Tab = createBottomTabNavigator();
+
+function HomeTabs() {
+ return (
+
+
+
+
+
+ );
+}
+
+const Stack = createNativeStackNavigator();
+
+export default function App() {
+ return (
+
+
+
+
+
+
+ );
+}
+
+// codeblock-focus-end
+```
+
+
+
+
+If we were to set the `headerTitle` with `options` for the `FeedScreen`, this would not work. This is because `App` stack will only look at its immediate children for configuration: `HomeTabs` and `SettingsScreen`.
+
+But we can determine the `headerTitle` option based on the [navigation state](navigation-state.md) of our tab navigator using the `getFocusedRouteNameFromRoute` helper. Let's create a function to get the title first:
+
+```js
+import { getFocusedRouteNameFromRoute } from '@react-navigation/native';
+
+function getHeaderTitle(route) {
+ // If the focused route is not found, we need to assume it's the initial screen
+ // This can happen during if there hasn't been any navigation inside the screen
+ // In our case, it's "Feed" as that's the first screen inside the navigator
+ const routeName = getFocusedRouteNameFromRoute(route) ?? 'Feed';
+
+ switch (routeName) {
+ case 'Feed':
+ return 'News feed';
+ case 'Profile':
+ return 'My profile';
+ case 'Account':
+ return 'My account';
+ }
+}
+```
+
+Then we can use this function with the `options` prop on `Screen`:
+
+
+
+
+```js name="Parent options from a child" snack
+import * as React from 'react';
+import { View } from 'react-native';
+import { getFocusedRouteNameFromRoute } from '@react-navigation/native';
+import {
+ createStaticNavigation,
+ useNavigation,
+} from '@react-navigation/native';
+import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+import { Button } from '@react-navigation/elements';
+
+function getHeaderTitle(route) {
+ // If the focused route is not found, we need to assume it's the initial screen
+ // This can happen during if there hasn't been any navigation inside the screen
+ // In our case, it's "Feed" as that's the first screen inside the navigator
+ const routeName = getFocusedRouteNameFromRoute(route) ?? 'Feed';
+
+ switch (routeName) {
+ case 'Feed':
+ return 'News feed';
+ case 'Profile':
+ return 'My profile';
+ case 'Account':
+ return 'My account';
+ }
+}
+
+function FeedScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+
+
+ );
+}
+
+function ProfileScreen() {
+ return ;
+}
+
+function AccountScreen() {
+ return ;
+}
+
+function SettingsScreen() {
+ return ;
+}
+const HomeTabs = createBottomTabNavigator({
+ screenOptions: {
+ headerShown: false,
+ },
+ screens: {
+ Feed: FeedScreen,
+ Profile: ProfileScreen,
+ Account: AccountScreen,
+ },
+});
+
+// codeblock-focus-start
+const RootStack = createNativeStackNavigator({
+ screens: {
+ Home: {
+ screen: HomeTabs,
+ options: ({ route }) => ({
+ headerTitle: getHeaderTitle(route),
+ }),
+ },
+ Settings: SettingsScreen,
+ },
+});
+// codeblock-focus-end
+
+const Navigation = createStaticNavigation(RootStack);
+
+export default function App() {
+ return ;
+}
+```
+
+
+
+
+
+```js name="Parent options from a child" snack
+import * as React from 'react';
+import { View } from 'react-native';
+import {
+ NavigationContainer,
+ useNavigation,
+ getFocusedRouteNameFromRoute,
+} from '@react-navigation/native';
+import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+import { Button } from '@react-navigation/elements';
+
+function getHeaderTitle(route) {
+ // If the focused route is not found, we need to assume it's the initial screen
+ // This can happen during if there hasn't been any navigation inside the screen
+ // In our case, it's "Feed" as that's the first screen inside the navigator
+ const routeName = getFocusedRouteNameFromRoute(route) ?? 'Feed';
+
+ switch (routeName) {
+ case 'Feed':
+ return 'News feed';
+ case 'Profile':
+ return 'My profile';
+ case 'Account':
+ return 'My account';
+ }
+}
+
+function FeedScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+
+
+ );
+}
+
+function ProfileScreen() {
+ return ;
+}
+
+function AccountScreen() {
+ return ;
+}
+
+function SettingsScreen() {
+ return ;
+}
+
+const Tab = createBottomTabNavigator();
+
+function HomeTabs() {
+ return (
+
+
+
+
+
+ );
+}
+
+const Stack = createNativeStackNavigator();
+
+export default function App() {
+ return (
+
+
+ // codeblock-focus-start
+ ({
+ headerTitle: getHeaderTitle(route),
+ })}
+ />
+ // codeblock-focus-end
+
+
+
+ );
+}
+```
+
+
+
+
+So what's happening here? With the `getFocusedRouteNameFromRoute` helper, we can get the currently active route name from this child navigator (in this case it's the tab navigator since that's what we're rendering) and setting an appropriate title for the header.
+
+This approach can be used anytime you want to set options for a parent navigator based on a child navigator's state. Common use cases are:
+
+1. Show tab title in stack header: a stack contains a tab navigator and you want to set the title on the stack header (above example)
+2. Show screens without tab bar: a tab navigator contains a stack and you want to hide the tab bar on specific screens (not recommended, see [Hiding tab bar in specific screens](hiding-tabbar-in-screens.md) instead)
+3. Lock drawer on certain screens: a drawer has a stack inside of it and you want to lock the drawer on certain screens
+
+In many cases, similar behavior can be achieved by reorganizing our navigators. We usually recommend this option if it fits your use case.
+
+For example, for the above use case, instead of adding a tab navigator inside a stack navigator, we can add a stack navigator inside each of the tabs.
+
+
+
+
+```js name="Reorganized navigators" snack
+import * as React from 'react';
+import { View } from 'react-native';
+import {
+ createStaticNavigation,
+ useNavigation,
+} from '@react-navigation/native';
+import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+import { Button } from '@react-navigation/elements';
+
+function FeedScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+
+
+ );
+}
+
+function ProfileScreen() {
+ return ;
+}
+
+function SettingsScreen() {
+ return ;
+}
+
+// codeblock-focus-start
+const FeedStackScreen = createNativeStackNavigator({
+ screens: {
+ Feed: FeedScreen,
+ /* other screens */
+ },
+});
+
+const ProfileStackScreen = createNativeStackNavigator({
+ screens: {
+ Profile: ProfileScreen,
+ /* other screens */
+ },
+});
+
+const HomeTabs = createBottomTabNavigator({
+ screens: {
+ Feed: FeedStackScreen,
+ Profile: ProfileStackScreen,
+ },
+});
+
+const RootStack = createNativeStackNavigator({
+ screens: {
+ Home: HomeTabs,
+ Settings: SettingsScreen,
+ },
+});
+
+const Navigation = createStaticNavigation(RootStack);
+
+export default function App() {
+ return ;
+}
+// codeblock-focus-end
+```
+
+
+
+
+```js name="Reorganized navigators" snack
+import * as React from 'react';
+import { View } from 'react-native';
+import { NavigationContainer, useNavigation } from '@react-navigation/native';
+import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+import { Button } from '@react-navigation/elements';
+
+function FeedScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+
+
+ );
+}
+
+function ProfileScreen() {
+ return ;
+}
+
+function SettingsScreen() {
+ return ;
+}
+
+const FeedStack = createNativeStackNavigator();
+
+// codeblock-focus-start
+function FeedStackScreen() {
+ return (
+
+
+ {/* other screens */}
+
+ );
+}
+
+const ProfileStack = createNativeStackNavigator();
+
+function ProfileStackScreen() {
+ return (
+
+
+ {/* other screens */}
+
+ );
+}
+
+const Tab = createBottomTabNavigator();
+
+function HomeTabs() {
+ return (
+
+
+
+
+ );
+}
+
+const RootStack = createNativeStackNavigator();
+
+export default function App() {
+ return (
+
+
+
+
+
+
+ );
+}
+// codeblock-focus-end
+```
+
+
+
+
+Additionally, this lets you push new screens to the feed and profile stacks without hiding the tab bar by adding more routes to those stacks.
+
+If you want to push screens on top of the tab bar (i.e. that don't show the tab bar), then you can add them to the `App` stack instead of adding them into the screens inside the tab navigator.
diff --git a/versioned_docs/version-8.x/screen-options.md b/versioned_docs/version-8.x/screen-options.md
new file mode 100644
index 00000000000..0a9a89af8e2
--- /dev/null
+++ b/versioned_docs/version-8.x/screen-options.md
@@ -0,0 +1,371 @@
+---
+id: screen-options
+title: Options for screens
+sidebar_label: Options for screens
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+Each screen can configure various aspects about how it gets presented in the navigator that renders it by specifying certain options, for example, the header title in stack navigator, tab bar icon in bottom tab navigator etc. Different navigators support different set of options.
+
+In the [configuring the header bar](headers.md) section of the fundamentals documentation we explain the basics of how this works. Also see the [screen options resolution guide](screen-options-resolution.md) to get an idea of how they work when there are multiple navigators.
+
+See [our docs](typescript.md#annotating-options-and-screenoptions) to learn more about how to use TypeScript with `screenOptions` and `options`.
+
+There are 3 ways of specifying options for screens:
+
+### `options` prop on `Screen`
+
+You can pass a prop named `options` to the `Screen` component to configure a screen, where you can specify an object with different options for that screen:
+
+```js name="Screen title option" snack static2dynamic
+import * as React from 'react';
+import { View, Text } from 'react-native';
+import { Button } from '@react-navigation/elements';
+import {
+ createStaticNavigation,
+ useNavigation,
+} from '@react-navigation/native';
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+
+function HomeScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ Home Screen
+
+
+ );
+}
+
+function ProfileScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ Profile Screen
+
+
+ );
+}
+
+// codeblock-focus-start
+const RootStack = createNativeStackNavigator({
+ screens: {
+ Home: createNativeStackScreen({
+ screen: HomeScreen,
+ options: {
+ title: 'Awesome app',
+ },
+ }),
+ Profile: createNativeStackScreen({
+ screen: ProfileScreen,
+ options: {
+ title: 'My profile',
+ },
+ }),
+ },
+});
+
+const Navigation = createStaticNavigation(RootStack);
+
+export default function App() {
+ return ;
+}
+// codeblock-focus-end
+```
+
+You can also pass a function to `options`. The function will receive the [`navigation` object](navigation-object.md) and the [`route` object](route-object.md) for that screen, as well as the [`theme` object](themes.md). This can be useful if you want to perform navigation in your options:
+
+```js static2dynamic
+const RootStack = createNativeStackNavigator({
+ screens: {
+ Home: createNativeStackScreen({
+ screen: HomeScreen,
+ options: ({ navigation }) => ({
+ title: 'Awesome app',
+ headerLeft: () => {
+ navigation.toggleDrawer()} />;
+ },
+ }),
+ }),
+ },
+});
+```
+
+### `screenOptions` prop on `Group`
+
+You can pass a prop named `screenOptions` to the `Group` component to configure screens inside the group, where you can specify an object with different options. The options specified in `screenOptions` apply to all of the screens in the group.
+
+Example:
+
+
+
+
+```js name="Screen options for group" snack
+import * as React from 'react';
+import { View, Text } from 'react-native';
+import { Button } from '@react-navigation/elements';
+import {
+ useNavigation,
+ createStaticNavigation,
+} from '@react-navigation/native';
+import {
+ createNativeStackNavigator,
+ createNativeStackScreen,
+} from '@react-navigation/native-stack';
+
+// codeblock-focus-start
+const RootStack = createNativeStackNavigator({
+ groups: {
+ App: {
+ screenOptions: {
+ headerStyle: {
+ backgroundColor: '#FFB6C1',
+ },
+ },
+ screens: {
+ Home: ScreenWithButton('Home', 'Profile'),
+ Profile: ScreenWithButton('Profile', 'Settings'),
+ },
+ },
+ Modal: {
+ screenOptions: {
+ presentation: 'modal',
+ },
+ screens: {
+ Settings: ScreenWithButton('Settings', 'Share'),
+ Share: ScreenWithButton('Share'),
+ },
+ },
+ },
+});
+// codeblock-focus-end
+
+function ScreenWithButton(screenName, navigateTo) {
+ return function () {
+ const navigation = useNavigation();
+ return (
+
+ {screenName} Screen
+ {navigateTo && (
+
+ )}
+
+ );
+ };
+}
+
+const Navigation = createStaticNavigation(RootStack);
+
+export default function App() {
+ return ;
+}
+```
+
+
+
+
+```js name="Screen options for group" snack
+import * as React from 'react';
+import { View, Text } from 'react-native';
+import { Button } from '@react-navigation/elements';
+import { NavigationContainer, useNavigation } from '@react-navigation/native';
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+
+const Stack = createNativeStackNavigator();
+
+function ScreenWithButton(screenName, navigateTo) {
+ return function () {
+ const navigation = useNavigation();
+
+ return (
+
+ {screenName} Screen
+ {navigateTo && (
+
+ )}
+
+ );
+ };
+}
+
+const HomeScreen = ScreenWithButton('Home', 'Profile');
+const ProfileScreen = ScreenWithButton('Profile', 'Settings');
+const SettingsScreen = ScreenWithButton('Settings', 'Share');
+const ShareScreen = ScreenWithButton('Share');
+
+export default function App() {
+ return (
+
+ // codeblock-focus-start
+
+
+
+
+
+
+
+
+
+
+ // codeblock-focus-end
+
+ );
+}
+```
+
+
+
+
+Similar to `options`, you can also pass a function to `screenOptions`. The function will receive the [`navigation` object](navigation-object.md) and the [`route` object](route-object.md) for each screen. This can be useful if you want to configure options for all the screens in one place based on the route:
+
+
+
+
+```js
+const Stack = createNativeStackNavigator({
+ screens: {
+ Home: createNativeStackScreen({
+ screen: HomeScreen,
+ }),
+ Profile: createNativeStackScreen({
+ screen: ProfileScreen,
+ }),
+ },
+ groups: {
+ Modal: {
+ screenOptions: {
+ presentation: 'modal',
+ headerLeft: () => ,
+ },
+ screens: {
+ Settings: createNativeStackScreen({
+ screen: Settings,
+ }),
+ Share: createNativeStackScreen({
+ screen: Share,
+ }),
+ },
+ },
+ },
+});
+```
+
+
+
+
+```js
+
+
+
+ ({
+ presentation: 'modal',
+ headerLeft: () => ,
+ })}
+ >
+
+
+
+
+```
+
+
+
+
+### `screenOptions` prop on the navigator
+
+You can pass a prop named `screenOptions` to the navigator component, where you can specify an object with different options. The options specified in `screenOptions` apply to all of the screens in the navigator. So this is a good place to specify options that you want to configure for the whole navigator.
+
+Example:
+
+```js static2dynamic
+const RootStack = createNativeStackNavigator({
+ screenOptions: {
+ headerStyle: {
+ backgroundColor: 'papayawhip',
+ },
+ },
+ screens: {
+ Home: createNativeStackScreen({
+ screen: HomeScreen,
+ }),
+ Profile: createNativeStackScreen({
+ screen: ProfileScreen,
+ }),
+ },
+});
+```
+
+Similar to `options`, you can also pass a function to `screenOptions`. The function will receive the [`navigation` object](navigation-object.md) and the [`route` object](route-object.md) for each screen. This can be useful if you want to configure options for all the screens in one place based on the route:
+
+```js name="Screen options for tab navigator" snack dependencies=@expo/vector-icons static2dynamic
+import * as React from 'react';
+import { View } from 'react-native';
+import { createStaticNavigation } from '@react-navigation/native';
+import {
+ createBottomTabNavigator,
+ createBottomTabScreen,
+} from '@react-navigation/bottom-tabs';
+import { MaterialCommunityIcons } from '@expo/vector-icons';
+
+// codeblock-focus-start
+const MyTabs = createBottomTabNavigator({
+ screenOptions: ({ route }) => ({
+ tabBarIcon: ({ color, size }) => {
+ const icons = {
+ Home: 'home',
+ Profile: 'account',
+ };
+
+ return (
+
+ );
+ },
+ }),
+ screens: {
+ Home: createBottomTabScreen({
+ screen: EmptyScreen,
+ }),
+ Profile: createBottomTabScreen({
+ screen: EmptyScreen,
+ }),
+ },
+});
+// codeblock-focus-end
+
+function EmptyScreen() {
+ return ;
+}
+
+const Navigation = createStaticNavigation(MyTabs);
+
+export default function App() {
+ return ;
+}
+```
+
+### `navigation.setOptions` method
+
+The `navigation` object has a `setOptions` method that lets you update the options for a screen from within a component. See [navigation object's docs](navigation-object.md#setoptions) for more details.
+
+```js
+
+```
diff --git a/versioned_docs/version-8.x/screen-tracking.md b/versioned_docs/version-8.x/screen-tracking.md
new file mode 100644
index 00000000000..87786b0bfe4
--- /dev/null
+++ b/versioned_docs/version-8.x/screen-tracking.md
@@ -0,0 +1,189 @@
+---
+id: screen-tracking
+title: Screen tracking for analytics
+sidebar_label: Screen tracking
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+To track the currently active screen, we need to:
+
+1. Add a callback to get notified of state changes
+2. Get the root navigator state and find the active route name
+
+To get notified of state changes, we can use the `onStateChange` prop on `NavigationContainer`. To get the root navigator state, we can use the `getRootState` method on the container's ref. Please note that `onStateChange` is not called on initial render so you have to set your initial screen separately.
+
+## Example
+
+This example shows how the approach can be adapted to any mobile analytics SDK.
+
+
+
+
+```js name="Screen tracking for analytics" snack
+import * as React from 'react';
+import { View } from 'react-native';
+// codeblock-focus-start
+import {
+ createStaticNavigation,
+ useNavigationContainerRef,
+ useNavigation,
+} from '@react-navigation/native';
+// codeblock-focus-end
+import { Button } from '@react-navigation/elements';
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+
+function Home() {
+ const navigation = useNavigation();
+
+ return (
+
+
+
+ );
+}
+
+function Settings() {
+ const navigation = useNavigation();
+
+ return (
+
+
+
+ );
+}
+
+const RootStack = createNativeStackNavigator({
+ screens: {
+ Home: Home,
+ Settings: Settings,
+ },
+});
+
+const Navigation = createStaticNavigation(RootStack);
+
+// codeblock-focus-start
+
+export default function App() {
+ const navigationRef = useNavigationContainerRef();
+ const routeNameRef = React.useRef();
+
+ return (
+ {
+ routeNameRef.current = navigationRef.current.getCurrentRoute().name;
+ }}
+ onStateChange={async () => {
+ const previousRouteName = routeNameRef.current;
+ const currentRouteName = navigationRef.current.getCurrentRoute().name;
+ const trackScreenView = () => {
+ // Your implementation of analytics goes here!
+ };
+
+ if (previousRouteName !== currentRouteName) {
+ // Replace the line below to add the tracker from a mobile analytics SDK
+ await trackScreenView(currentRouteName);
+ }
+
+ // Save the current route name for later comparison
+ routeNameRef.current = currentRouteName;
+ }}
+ />
+ );
+}
+// codeblock-focus-end
+```
+
+
+
+
+```js name="Screen tracking for anylytics" snack
+import * as React from 'react';
+import { View } from 'react-native';
+// codeblock-focus-start
+import {
+ NavigationContainer,
+ useNavigation,
+ useNavigationContainerRef,
+} from '@react-navigation/native';
+// codeblock-focus-end
+import { Button } from '@react-navigation/elements';
+import { createStackNavigator } from '@react-navigation/stack';
+
+function Home() {
+ const navigation = useNavigation();
+
+ return (
+
+
+
+ );
+}
+
+function Settings() {
+ const navigation = useNavigation();
+
+ return (
+
+
+
+ );
+}
+
+const Stack = createStackNavigator();
+
+// codeblock-focus-start
+
+export default function App() {
+ const navigationRef = useNavigationContainerRef();
+ const routeNameRef = React.useRef();
+
+ return (
+ {
+ routeNameRef.current = navigationRef.current.getCurrentRoute().name;
+ }}
+ onStateChange={async () => {
+ const previousRouteName = routeNameRef.current;
+ const currentRouteName = navigationRef.current.getCurrentRoute().name;
+ const trackScreenView = () => {
+ // Your implementation of analytics goes here!
+ };
+
+ if (previousRouteName !== currentRouteName) {
+ // Replace the line below to add the tracker from a mobile analytics SDK
+ await trackScreenView(currentRouteName);
+ }
+
+ // Save the current route name for later comparison
+ routeNameRef.current = currentRouteName;
+ }}
+ >
+ {/* ... */}
+ // codeblock-focus-end
+
+
+
+
+ // codeblock-focus-start
+
+ );
+}
+// codeblock-focus-end
+```
+
+
+
+
+:::note
+
+If you are building a library that wants to provide screen tracking integration with React Navigation, you can accept a [`ref`](navigation-container.md#ref) to the navigation container and use the [`ready`](navigation-container.md#ready) and [`state`](navigation-container.md#state) events instead of `onReady` and `onStateChange` props to keep your logic self-contained.
+
+:::
diff --git a/versioned_docs/version-8.x/screen.md b/versioned_docs/version-8.x/screen.md
new file mode 100644
index 00000000000..354b1a00ba4
--- /dev/null
+++ b/versioned_docs/version-8.x/screen.md
@@ -0,0 +1,439 @@
+---
+id: screen
+title: Screen
+sidebar_label: Screen
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+A screen represents routes in a navigator. A screen's configuration contains the component for the route, options, event listeners, etc.
+
+
+
+
+Screens can be defined under the `screens` key in the navigator configuration:
+
+```js
+const MyStack = createNativeStackNavigator({
+ screens: {
+ Home: HomeScreen,
+ Profile: ProfileScreen,
+ },
+});
+```
+
+
+
+
+A `Screen` component is returned from a `createXNavigator` function. After creating the navigator, it can be used as children of the `Navigator` component:
+
+```js
+const Stack = createNativeStackNavigator();
+
+function MyStack() {
+ return (
+
+
+
+
+ );
+}
+```
+
+You need to provide at least a name and a component to render for each screen.
+
+
+
+
+## Configuration
+
+### Name
+
+The name to use for the screen.
+
+
+
+
+The key in the `screens` object is used as the name:
+
+```js
+const Stack = createNativeStackNavigator({
+ screens: {
+ // highlight-next-line
+ Profile: {
+ screen: ProfileScreen,
+ },
+ },
+});
+```
+
+
+
+
+It can be passed in the `name` prop to the `Screen` component:
+
+```jsx
+
+```
+
+
+
+
+This name is used to navigate to the screen:
+
+```js
+navigation.navigate('Profile');
+```
+
+It is also used for the `name` property in the [`route`](route-object.md).
+
+While it is supported, we recommend avoiding spaces or special characters in screen names and keeping them simple.
+
+### Options
+
+Options are used to configure how the screen gets presented in the navigator. It accepts either an object or a function returning an object:
+
+
+
+
+```js
+const Stack = createNativeStackNavigator({
+ screens: {
+ Profile: {
+ screen: ProfileScreen,
+ // highlight-start
+ options: {
+ title: 'Awesome app',
+ },
+ // highlight-end
+ },
+ },
+});
+```
+
+
+
+
+```jsx
+
+```
+
+
+
+
+When you pass a function, it'll receive the [`route`](route-object.md), [`navigation`](navigation-object.md) and [`theme`](themes.md) as arguments:
+
+
+
+
+```js
+const Stack = createNativeStackNavigator({
+ screens: {
+ Profile: {
+ screen: ProfileScreen,
+ // highlight-start
+ options: ({ route, navigation, theme }) => ({
+ title: route.params.userId,
+ }),
+ // highlight-end
+ },
+ },
+});
+```
+
+
+
+
+```jsx
+ ({
+ title: route.params.userId,
+ })}
+ // highlight-end
+/>
+```
+
+
+
+
+See [Options for screens](screen-options.md) for more details and examples.
+
+### Initial params
+
+Initial params are used as the default params for the screen. If a screen is used as `initialRouteName`, it'll contain the params from `initialParams`. If you navigate to a new screen, the params passed are shallow merged with the initial params.
+
+
+
+
+```js
+const Stack = createNativeStackNavigator({
+ screens: {
+ Details: {
+ screen: DetailsScreen,
+ // highlight-next-line
+ initialParams: { itemId: 42 },
+ },
+ },
+});
+```
+
+
+
+
+```jsx
+
+```
+
+
+
+
+### ID
+
+A screen can have an ID to identify it uniquely. This is useful when you want to ensure that the screen with the same ID doesn't appear multiple times in the stack.
+
+This can be done by specifying the `getId` callback. It receives an object with the route params:
+
+
+
+
+```js
+const Stack = createStackNavigator({
+ screens: {
+ Profile: {
+ screen: ProfileScreen,
+ // highlight-next-line
+ getId: ({ params }) => params.userId,
+ },
+ },
+});
+```
+
+
+
+
+```jsx
+ params.userId}
+/>
+```
+
+
+
+
+In the above example, `params.userId` is used as an ID for the `Profile` screen with `getId`. This changes how the navigation works to ensure that the screen with the same ID appears only once in the stack.
+
+Let's say you have a stack with the history `Home > Profile (userId: bob) > Settings`, consider following scenarios:
+
+- You call `navigate(Profile, { userId: 'bob' })`:
+ The resulting screens will be `Home > Settings > Profile (userId: bob)` since the existing `Profile` screen matches the ID.
+- You call `navigate(Profile, { userId: 'alice' })`:
+ The resulting screens will be `Home > Profile (userId: bob) > Settings > Profile (userId: alice)` since it'll add a new `Profile` screen as no matching screen was found.
+
+If `getId` is specified in a tab or drawer navigator, the screen will remount if the ID changes.
+
+:::warning
+
+If you're using [`@react-navigation/native-stack`](native-stack-navigator.md), it doesn't work correctly with the `getId` callback. So it's recommended to avoid using it in that case.
+
+:::
+
+### Component
+
+Each screen must specify a component to render for that route.
+
+
+
+
+It can be passed under the `screen` property in the screen configuration:
+
+```js
+const Stack = createNativeStackNavigator({
+ screens: {
+ Profile: {
+ // highlight-next-line
+ screen: ProfileScreen,
+ },
+ },
+});
+```
+
+
+
+
+#### `component`
+
+It can be passed in the `component` prop to the `Screen` component:
+
+```jsx
+
+```
+
+#### `getComponent`
+
+It's also possible to pass a function in the `getComponent` prop to lazily evaluate the component:
+
+```jsx
+ require('./ProfileScreen').default}
+/>
+```
+
+You can use this approach instead of the `component` prop if you want the `ProfileScreen` module to be lazily evaluated when needed. This is especially useful when using [ram bundles](https://reactnative.dev/docs/ram-bundles-inline-requires) to improve initial load.
+
+#### `children`
+
+Another way is to pass a render callback to return React Element to use for the screen:
+
+```jsx
+
+ // highlight-next-line
+ {(props) => }
+
+```
+
+You can use this approach instead of the `component` prop if you need to pass additional props. Though we recommend using [React context](https://react.dev/reference/react/useContext) for passing data instead.
+
+:::warning
+
+By default, React Navigation applies optimizations to screen components to prevent unnecessary renders. Using a render callback removes those optimizations. So if you use a render callback, you'll need to ensure that you use [`React.memo`](https://react.dev/reference/react/memo) or [`React.PureComponent`](https://react.dev/reference/react/PureComponent) for your screen components to avoid performance issues.
+
+:::
+
+
+
+
+### Layout
+
+A layout is a wrapper around the screen. It makes it easier to provide things such as an error boundary and suspense fallback for a screen, or wrap the screen with additional UI.
+
+It takes a function that returns a React element:
+
+
+
+
+```js
+const Stack = createNativeStackNavigator({
+ screens: {
+ Profile: {
+ screen: ProfileScreen,
+ // highlight-start
+ layout: ({ children }) => (
+
+
+ Loading…
+
+ }
+ >
+ {children}
+
+
+ ),
+ // highlight-end
+ },
+ },
+});
+```
+
+
+
+
+```jsx
+ (
+
+
+ Loading…
+
+ }
+ >
+ {children}
+
+
+ )}
+ // highlight-end
+/>
+```
+
+To specify a layout for all multiple screens, you can use `screenLayout` in a [group](group.md#screen-layout) or [navigator](navigator.md#screen-layout).
+
+
+
+
+### Navigation key
+
+A navigation key is an optional key for this screen. This doesn't need to be unique. If the key changes, existing screens with this name will be removed (if used in a stack navigator) or reset (if used in a tab or drawer navigator).
+
+This can be useful when we have some screens that we want to be removed or reset when the condition changes:
+
+
+
+
+```js
+const Stack = createNativeStackNavigator({
+ screens: {
+ Profile: {
+ screen: ProfileScreen,
+ // highlight-next-line
+ navigationKey: 'user',
+ },
+ },
+});
+```
+
+For the static API, we recommend using the [`groups`](group.md#navigation-key) instead of the `navigationKey` for each screen as you can dynamically add or remove groups with the [`if`](static-configuration.md#if) property.
+
+
+
+
+```jsx
+
+```
+
+
+
+
+### Event listeners
+
+Event listeners can be used to subscribe to various events emitted for the screen. See [`listeners` prop on `Screen`](navigation-events.md#listeners-prop-on-screen) for more details.
diff --git a/versioned_docs/version-8.x/server-container.md b/versioned_docs/version-8.x/server-container.md
new file mode 100644
index 00000000000..655b158feac
--- /dev/null
+++ b/versioned_docs/version-8.x/server-container.md
@@ -0,0 +1,76 @@
+---
+id: server-container
+title: ServerContainer
+sidebar_label: ServerContainer
+---
+
+The `ServerContainer` component provides utilities to render your app on server with the correct [navigation state](navigation-state.md).
+
+Example:
+
+```js
+// Ref which will be populated with the screen options
+const ref = React.createRef();
+
+// Location object containing the `pathname` and `search` fields of the current URL
+const location = { pathname: '/profile', search: '?user=jane' };
+
+// Get rendered HTML
+const html = ReactDOMServer.renderToString(
+
+
+
+);
+
+// Then you can access the options for the current screen in the ref
+const options = ref.current.getCurrentOptions(); // { title: 'My Profile' }
+```
+
+The `ServerContainer` component should wrap your entire app during server rendering. Note that you still need a `NavigationContainer` in your app, `ServerContainer` doesn't replace it.'
+
+See the [`server rendering guide`](server-rendering.md) for a detailed guide and examples.
+
+## Ref
+
+If you attach a `ref` to the container, you can get the options for the current screen after rendering the app. The `ref` will contain a method called `getCurrentOptions` which will return an object with options for the focused screen in the navigation tree:
+
+```js
+const options = ref.current.getCurrentOptions();
+```
+
+Then you can access the options for the screen from this object and put it in the HTML:
+
+```jsx
+{options.title}
+
+```
+
+Note that the `options` object can be undefined if you are not rendering a navigator on the initial render.
+
+## Props
+
+### `location`
+
+Location object containing the location to use for server rendered output. You can pass the `pathname` and `search` properties matching the `location` object in the browsers:
+
+```js
+
+
+
+```
+
+Normally, you'd construct this object based on the incoming request.
+
+Basic example with Koa (don't use as is in production):
+
+```js
+app.use(async (ctx) => {
+ const html = ReactDOMServer.renderToString(
+
+
+
+ );
+
+ ctx.body = html;
+});
+```
diff --git a/versioned_docs/version-8.x/server-rendering.md b/versioned_docs/version-8.x/server-rendering.md
new file mode 100644
index 00000000000..4dddb2019b0
--- /dev/null
+++ b/versioned_docs/version-8.x/server-rendering.md
@@ -0,0 +1,256 @@
+---
+id: server-rendering
+title: Server rendering
+sidebar_label: Server rendering
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+This guide will cover how to server render your React Native app using React Native for Web and React Navigation. We'll cover the following cases:
+
+1. Rendering the correct layout depending on the request URL
+2. Setting appropriate page metadata based on the focused screen
+
+:::warning
+
+Server rendering support is currently limited. It's not possible to provide a seamless SSR experience due to a lack of APIs such as media queries. In addition, many third-party libraries often don't work well with server rendering.
+
+:::
+
+## Pre-requisites
+
+Before you follow the guide, make sure that your app already renders fine on server. To do that, you will need to ensure the following:
+
+- All of the dependencies that you use are [compiled before publishing](https://github.com/react-native-community/bob) to npm, so that you don't get syntax errors on Node.
+- Node is configured to be able to `require` asset files such as images and fonts. You can try [webpack-isomorphic-tools](https://github.com/catamphetamine/webpack-isomorphic-tools) to do that.
+- `react-native` is aliased to `react-native-web`. You can do it with [babel-plugin-module-resolver](https://github.com/tleunen/babel-plugin-module-resolver).
+
+## Rendering the app
+
+First, let's take a look at an example of how you'd do [server rendering with React Native Web](http://necolas.github.io/react-native-web/docs/?path=/docs/guides-server-side--page) without involving React Navigation:
+
+```js
+import { AppRegistry } from 'react-native-web';
+import ReactDOMServer from 'react-dom/server';
+import App from './src/App';
+
+const { element, getStyleElement } = AppRegistry.getApplication('App');
+
+const html = ReactDOMServer.renderToString(element);
+const css = ReactDOMServer.renderToStaticMarkup(getStyleElement());
+
+const document = `
+
+
+
+
+
+ ${css}
+
+
+ ${html}
+
+`;
+```
+
+Here, `./src/App` is the file where you have `AppRegistry.registerComponent('App', () => App)`.
+
+If you're using React Navigation in your app, this will render the screens rendered by your home page. However, if you have [configured links](configuring-links.md) in your app, you'd want to render the correct screens for the request URL on server so that it matches what'll be rendered on the client.
+
+We can use the [`ServerContainer`](server-container.md) to do that by passing this info in the `location` prop. For example, with Koa, you can use the `path` and `search` properties from the context argument:
+
+```js
+app.use(async (ctx) => {
+ const location = new URL(ctx.url, 'https://example.org/');
+
+ const { element, getStyleElement } = AppRegistry.getApplication('App');
+
+ const html = ReactDOMServer.renderToString(
+ {element}
+ );
+
+ const css = ReactDOMServer.renderToStaticMarkup(getStyleElement());
+
+ const document = `
+
+
+
+
+
+ ${css}
+
+
+ ${html}
+
+`;
+
+ ctx.body = document;
+});
+```
+
+You may also want to set the correct document title and descriptions for search engines, open graph etc. To do that, you can pass a `ref` to the container which will give you the current screen's options.
+
+```js
+app.use(async (ctx) => {
+ const location = new URL(ctx.url, 'https://example.org/');
+
+ const { element, getStyleElement } = AppRegistry.getApplication('App');
+
+ const ref = React.createRef();
+
+ const html = ReactDOMServer.renderToString(
+
+ {element}
+
+ );
+
+ const css = ReactDOMServer.renderToStaticMarkup(getStyleElement());
+
+ const options = ref.current?.getCurrentOptions();
+
+ const document = `
+
+
+
+
+
+ ${css}
+ ${options.title}
+
+
+ ${html}
+
+`;
+
+ ctx.body = document;
+});
+```
+
+Make sure that you have specified a `title` option for your screens:
+
+
+
+
+```js
+const Stack = createNativeStackNavigator({
+ screens: {
+ Home: {
+ screen: HomeScreen,
+ options: {
+ // highlight-next-line
+ title: 'My App',
+ },
+ },
+ Profile: {
+ screen: ProfileScreen,
+ options: ({ route }) => ({
+ // highlight-next-line
+ title: `${route.params.name}'s Profile`,
+ }),
+ },
+ },
+});
+```
+
+
+
+
+```js
+
+
+ ({
+ // highlight-next-line
+ title: `${route.params.name}'s Profile`,
+ })}
+ />
+
+```
+
+
+
+
+## Handling 404 or other status codes
+
+When [rendering a screen for an invalid URL](configuring-links.md#handling-unmatched-routes-or-404), we should also return a `404` status code from the server.
+
+First, we need to create a context where we'll attach the status code. To do this, place the following code in a separate file that we will be importing on both the server and client:
+
+```js
+import * as React from 'react';
+
+const StatusCodeContext = React.createContext();
+
+export default StatusCodeContext;
+```
+
+Then, we need to use the context in our `NotFound` screen. Here, we add a `code` property with the value of `404` to signal that the screen was not found:
+
+```js
+function NotFound() {
+ const status = React.useContext(StatusCodeContext);
+
+ if (status) {
+ staus.code = 404;
+ }
+
+ return (
+
+ Oops! This URL doesn't exist.
+
+ );
+}
+```
+
+You could also attach additional information in this object if you need to.
+
+Next, we need to create a status object to pass in the context on our server. By default, we'll set the `code` to `200`. Then pass the object in `StatusCodeContext.Provider` which should wrap the element with `ServerContainer`:
+
+```js
+// Create a status object
+const status = { code: 200 };
+
+const html = ReactDOMServer.renderToString(
+ // Pass the status object via context
+
+
+ {element}
+
+
+);
+
+// After rendering, get the status code and use it for server's response
+ctx.status = status.code;
+```
+
+After we render the app with `ReactDOMServer.renderToString`, the `code` property of the `status` object will be updated to be `404` if the `NotFound` screen was rendered.
+
+You can follow a similar approach for other status codes too, for example, `401` for unauthorized etc.
+
+## Summary
+
+- Use the `location` prop on `ServerContainer` to render correct screens based on the incoming request.
+- Attach a `ref` to the `ServerContainer` get options for the current screen.
+- Use context to attach more information such as status code.
diff --git a/versioned_docs/version-8.x/shared-element-transitions.md b/versioned_docs/version-8.x/shared-element-transitions.md
new file mode 100644
index 00000000000..18d5fd917f0
--- /dev/null
+++ b/versioned_docs/version-8.x/shared-element-transitions.md
@@ -0,0 +1,229 @@
+---
+id: shared-element-transitions
+title: Animating elements between screens
+sidebar_label: Shared element transitions
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+This guide covers how to animate elements between screens. This feature is known as a [Shared Element Transition](https://docs.swmansion.com/react-native-reanimated/docs/shared-element-transitions/overview/) and it's implemented in the [`@react-navigation/native-stack`](native-stack-navigator.md) with [React Native Reanimated](https://docs.swmansion.com/react-native-reanimated/).
+
+:::warning
+
+As of writing this guide, Shared Element Transitions are considered an experimental feature not recommended for production use.
+
+Shared Element Transitions are currently only supported on **old React Native architecture** (Paper).
+
+:::
+
+
+
+## Pre-requisites
+
+Before continuing this guide make sure your app meets these criteria:
+
+- You are using [`@react-navigation/native-stack`](native-stack-navigator.md). The Shared Element Transitions feature isn't supported in JS-based [`@react-navigation/stack`](stack-navigator.md).
+- You have [`react-native-reanimated`](https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/getting-started) **v3.0.0 or higher** installed and configured.
+
+## Minimal example
+
+To create a shared transition:
+
+1. Use `Animated` components imported from `react-native-reanimated`.
+2. Assign the same `sharedTransitionTag` to elements on different screens.
+3. Navigate between screens. The transition will start automatically.
+
+
+
+
+```js name="Shared transition"
+import * as React from 'react';
+import { View, StyleSheet } from 'react-native';
+import {
+ useNavigation,
+ createStaticNavigation,
+} from '@react-navigation/native';
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+import { Button } from '@react-navigation/elements';
+
+import Animated from 'react-native-reanimated';
+
+function HomeScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+
+
+
+ );
+}
+
+function DetailsScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+
+
+
+ );
+}
+
+// highlight-start
+const RootStack = createNativeStackNavigator({
+ screens: {
+ Home: HomeScreen,
+ Details: DetailsScreen,
+ },
+});
+// highlight-end
+
+const Navigation = createStaticNavigation(RootStack);
+
+export default function App() {
+ return ;
+}
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ alignItems: 'center',
+ },
+});
+```
+
+
+
+
+```js name="Shared transition"
+import * as React from 'react';
+import { View, StyleSheet } from 'react-native';
+import { NavigationContainer, useNavigation } from '@react-navigation/native';
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+import { Button } from '@react-navigation/elements';
+
+import Animated from 'react-native-reanimated';
+
+// highlight-next-line
+const Stack = createNativeStackNavigator();
+
+function HomeScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+
+
+
+ );
+}
+
+function DetailsScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+
+
+
+ );
+}
+
+export default function App() {
+ return (
+
+
+
+
+
+
+ );
+}
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ alignItems: 'center',
+ },
+});
+```
+
+
+
+
+`sharedTransitionTag` is a string that has to be unique in the context of a single screen, but has to match elements between screens. This prop allows Reanimated to identify and animate the elements, similarly to the [`key`](https://react.dev/learn/rendering-lists#keeping-list-items-in-order-with-key) property, which tells React which element in the list is which.
+
+## Customizing the transition
+
+By default, the transition animates the `width`, `height`, `originX`, `originY` and `transform` properties using `withTiming` with a 500 ms duration. You can easily customize `width`, `height`, `originX`, and `originY` props. Customizing `transform` is also possible but it's far beyond the scope of this guide.
+
+:::warning
+
+Custom SharedTransition API is not finalized and might change in a future release.
+
+:::
+
+To customize the transition you need to pass all the properties besides `transform`.
+
+```jsx
+import { SharedTransition } from 'react-native-reanimated';
+
+const customTransition = SharedTransition.custom((values) => {
+ 'worklet';
+ return {
+ height: withSpring(values.targetHeight),
+ width: withSpring(values.targetWidth),
+ originX: withSpring(values.targetOriginX),
+ originY: withSpring(values.targetOriginY),
+ };
+});
+
+function HomeScreen() {
+ return (
+
+ );
+}
+```
+
+## Reference
+
+You can find a full Shared Element Transitions reference in the [React Native Reanimated documentation](https://docs.swmansion.com/react-native-reanimated/docs/shared-element-transitions/overview/).
+
+## Alternatives
+
+Alternatively, you can use [`react-native-shared-element`](https://github.com/IjzerenHein/react-native-shared-element) library with a [React Navigation binding](https://github.com/IjzerenHein/react-navigation-shared-element) which implements Shared Element Transitions in a JS-based `@react-navigation/stack` navigator. This solution, however, isn't actively maintained.
+
+The [`react-native-navigation`](https://github.com/wix/react-native-navigation) also comes with support for Shared Element Transitions. You can read more about it [here](https://wix.github.io/react-native-navigation/docs/style-animations#shared-element-transitions).
diff --git a/versioned_docs/version-8.x/stack-actions.md b/versioned_docs/version-8.x/stack-actions.md
new file mode 100755
index 00000000000..6e1d34e500a
--- /dev/null
+++ b/versioned_docs/version-8.x/stack-actions.md
@@ -0,0 +1,469 @@
+---
+id: stack-actions
+title: StackActions reference
+sidebar_label: StackActions
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+`StackActions` is an object containing methods for generating actions specific to stack-based navigators. Its methods expand upon the actions available in [`CommonActions`](navigation-actions.md).
+
+The following actions are supported:
+
+### replace
+
+The `replace` action allows to replace a route in the [navigation state](navigation-state.md). It takes the following arguments:
+
+- `name` - _string_ - A destination name of the route that has been registered somewhere.
+- `params` - _object_ - Params to pass to the destination route.
+
+```js name="Stack actions replace" snack static2dynamic
+import * as React from 'react';
+import { View, Text } from 'react-native';
+import { Button } from '@react-navigation/elements';
+import {
+ createStaticNavigation,
+ useNavigation,
+ StackActions,
+} from '@react-navigation/native';
+import { createStackNavigator } from '@react-navigation/stack';
+
+function HomeScreen() {
+ const navigation = useNavigation();
+ return (
+
+ Home!
+
+
+
+ );
+}
+
+function ProfileScreen({ route }) {
+ const navigation = useNavigation();
+ return (
+
+ Profile!
+ {route.params.user}'s profile
+
+
+
+
+ );
+}
+
+const RootStack = createStackNavigator({
+ screens: {
+ Home: HomeScreen,
+ Profile: ProfileScreen,
+ },
+});
+
+const Navigation = createStaticNavigation(RootStack);
+
+export default function App() {
+ return ;
+}
+```
+
+If you want to replace a particular route, you can add a `source` property referring to the route key and `target` property referring to the navigation state key:
+
+```js
+import { StackActions } from '@react-navigation/native';
+
+navigation.dispatch({
+ ...StackActions.replace('Profile', {
+ user: 'jane',
+ }),
+ source: route.key,
+ target: navigation.getState().key,
+});
+```
+
+If the `source` property is explicitly set to `undefined`, it'll replace the focused route.
+
+### push
+
+The `push` action adds a route on top of the stack and navigates forward to it. This differs from `navigate` in that `navigate` will pop back to earlier in the stack if a route of the given name is already present there. `push` will always add on top, so a route can be present multiple times.
+
+- `name` - _string_ - Name of the route to push onto the stack.
+- `params` - _object_ - Screen params to pass to the destination route.
+
+```js name="Stack actions push" snack static2dynamic
+import * as React from 'react';
+import { View, Text } from 'react-native';
+import { Button } from '@react-navigation/elements';
+import {
+ createStaticNavigation,
+ useNavigation,
+ StackActions,
+} from '@react-navigation/native';
+import { createStackNavigator } from '@react-navigation/stack';
+
+function HomeScreen() {
+ const navigation = useNavigation();
+ return (
+
+ Home!
+
+
+
+ );
+}
+
+function ProfileScreen({ route }) {
+ const navigation = useNavigation();
+ return (
+
+ Profile!
+ {route.params.user}'s profile
+
+
+
+
+ );
+}
+
+const RootStack = createStackNavigator({
+ screens: {
+ Home: HomeScreen,
+ Profile: ProfileScreen,
+ },
+});
+
+const Navigation = createStaticNavigation(RootStack);
+
+export default function App() {
+ return ;
+}
+```
+
+### pop
+
+The `pop` action takes you back to a previous screen in the stack. It takes one optional argument (`count`), which allows you to specify how many screens to pop back by.
+
+```js name="Stack actions pop" snack static2dynamic
+import * as React from 'react';
+import { Button } from '@react-navigation/elements';
+import { View, Text } from 'react-native';
+import {
+ createStaticNavigation,
+ useNavigation,
+ StackActions,
+} from '@react-navigation/native';
+import { createStackNavigator } from '@react-navigation/stack';
+
+function HomeScreen() {
+ const navigation = useNavigation();
+ return (
+
+ Home!
+
+
+
+ );
+}
+
+function ProfileScreen({ route }) {
+ const navigation = useNavigation();
+ return (
+
+ Profile!
+ {route.params.user}'s profile
+
+
+
+
+ );
+}
+
+const RootStack = createStackNavigator({
+ screens: {
+ Home: HomeScreen,
+ Profile: ProfileScreen,
+ },
+});
+
+const Navigation = createStaticNavigation(RootStack);
+
+export default function App() {
+ return ;
+}
+```
+
+### popTo
+
+The `popTo` action takes you back to a previous screen in the stack by the name. It also allows you to pass params to the route.
+
+If a matching screen is not found in the stack, this will pop the current screen and add a new screen with the specified name and params - essentially behaving like a [`replace`](#replace). This ensures that the app doesn't break if a previous screen with the name did not exist - which can happen when the screen was opened from a deep link or push notification, or when used on the web etc.
+
+The method accepts the following arguments:
+
+- `name` - _string_ - Name of the route to navigate to.
+- `params` - _object_ - Screen params to pass to the destination route.
+- `options` - Options object containing the following properties:
+ - `merge` - _boolean_ - Whether params should be merged with the existing route params, or replace them (when navigating to an existing screen). Defaults to `false`.
+
+```js name="Stack actions popTo" snack static2dynamic
+import * as React from 'react';
+import { View, Text } from 'react-native';
+import { Button } from '@react-navigation/elements';
+import {
+ createStaticNavigation,
+ useNavigation,
+ StackActions,
+} from '@react-navigation/native';
+import { createStackNavigator } from '@react-navigation/stack';
+
+function HomeScreen() {
+ const navigation = useNavigation();
+ return (
+
+ Home!
+
+
+
+ );
+}
+
+function ProfileScreen({ route }) {
+ const navigation = useNavigation();
+ return (
+
+ Profile!
+ {route?.params?.user || 'Guest'}'s profile
+
+
+
+ );
+}
+
+function SettingsScreen() {
+ const navigation = useNavigation();
+ return (
+
+ Settings!
+
+
+
+ );
+}
+
+const RootStack = createStackNavigator({
+ screens: {
+ Home: HomeScreen,
+ Profile: ProfileScreen,
+ Settings: SettingsScreen,
+ },
+});
+
+const Navigation = createStaticNavigation(RootStack);
+
+export default function App() {
+ return ;
+}
+```
+
+### popToTop
+
+The `popToTop` action takes you back to the first screen in the stack, dismissing all the others. It's functionally identical to `StackActions.pop({n: currentIndex})`.
+
+```js name="Stack actions popToTop" snack static2dynamic
+import * as React from 'react';
+import { View, Text } from 'react-native';
+import { Button } from '@react-navigation/elements';
+import {
+ createStaticNavigation,
+ useNavigation,
+ StackActions,
+} from '@react-navigation/native';
+import { createStackNavigator } from '@react-navigation/stack';
+
+function HomeScreen() {
+ const navigation = useNavigation();
+ return (
+
+ Home!
+
+
+
+ );
+}
+
+function ProfileScreen({ route }) {
+ const navigation = useNavigation();
+ return (
+
+ Profile!
+ {route.params.user}'s profile
+
+
+
+
+ );
+}
+
+const RootStack = createStackNavigator({
+ screens: {
+ Home: HomeScreen,
+ Profile: ProfileScreen,
+ },
+});
+
+const Navigation = createStaticNavigation(RootStack);
+
+export default function App() {
+ return ;
+}
+```
diff --git a/versioned_docs/version-8.x/stack-navigator.md b/versioned_docs/version-8.x/stack-navigator.md
new file mode 100755
index 00000000000..6a1f3fed9e1
--- /dev/null
+++ b/versioned_docs/version-8.x/stack-navigator.md
@@ -0,0 +1,1350 @@
+---
+id: stack-navigator
+title: Stack Navigator
+sidebar_label: Stack
+---
+
+Stack Navigator provides a way for your app to transition between screens where each new screen is placed on top of a stack.
+
+By default the stack navigator is configured to have the familiar iOS and Android look & feel: new screens slide in from the right on iOS, use OS default animation on Android. But the [animations can be customized](#animation-related-options) to match your needs.
+
+
+
+
+
+One thing to keep in mind is that while `@react-navigation/stack` is extremely customizable, it's implemented in JavaScript. While it runs animations and gestures using natively, the performance may not be as fast as a native implementation. This may not be an issue for a lot of apps, but if you're experiencing performance issues during navigation, consider using [`@react-navigation/native-stack`](native-stack-navigator.md) instead - which uses native navigation primitives.
+
+## Installation
+
+To use this navigator, ensure that you have [`@react-navigation/native` and its dependencies (follow this guide)](getting-started.md), then install [`@react-navigation/stack`](https://github.com/react-navigation/react-navigation/tree/main/packages/stack):
+
+```bash npm2yarn
+npm install @react-navigation/stack
+```
+
+The navigator depends on [`react-native-gesture-handler`](https://docs.swmansion.com/react-native-gesture-handler/) for gestures and optionally [`@react-native-masked-view/masked-view`](https://github.com/react-native-masked-view/masked-view) for [UIKit style animations for the header](#headerstyleinterpolator).
+
+
+
+
+If you have a Expo managed project, in your project directory, run:
+
+```bash
+npx expo install react-native-gesture-handler @react-native-masked-view/masked-view
+```
+
+
+
+
+If you have a bare React Native project, in your project directory, run:
+
+```bash npm2yarn
+npm install react-native-gesture-handler @react-native-masked-view/masked-view
+```
+
+
+
+
+If you're on a Mac and developing for iOS, you also need to install [pods](https://cocoapods.org/) to complete the linking.
+
+```bash
+npx pod-install ios
+```
+
+## Usage
+
+To use this navigator, import it from `@react-navigation/stack`:
+
+```js name="Stack Navigator" snack static2dynamic
+import * as React from 'react';
+import { Text, View } from 'react-native';
+import {
+ createStaticNavigation,
+ useNavigation,
+} from '@react-navigation/native';
+import { Button } from '@react-navigation/elements';
+// codeblock-focus-start
+import { createStackNavigator } from '@react-navigation/stack';
+
+// codeblock-focus-end
+function HomeScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ Home Screen
+
+
+ );
+}
+
+function ProfileScreen() {
+ return (
+
+ Profile Screen
+
+ );
+}
+
+// codeblock-focus-start
+const MyStack = createStackNavigator({
+ screens: {
+ Home: HomeScreen,
+ Profile: ProfileScreen,
+ },
+});
+// codeblock-focus-end
+
+const Navigation = createStaticNavigation(MyStack);
+
+export default function App() {
+ return ;
+}
+```
+
+## API Definition
+
+### Props
+
+In addition to the [common props](navigator.md#configuration) shared by all navigators, the stack navigator accepts the following additional props:
+
+#### `detachInactiveScreens`
+
+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`.
+
+If you need to disable this optimization for specific screens (e.g. you want to screen to stay in view even when unfocused) [`detachPreviousScreen`](#detachpreviousscreen) option.
+
+### Options
+
+The following [options](screen-options.md) can be used to configure the screens in the navigator. These can be specified under `screenOptions` prop of `Stack.Navigator` or `options` prop of `Stack.Screen`.
+
+#### `title`
+
+String that can be used as a fallback for `headerTitle`.
+
+#### `cardShadowEnabled`
+
+Use this prop to have visible shadows during transitions. Defaults to `true`.
+
+#### `cardOverlayEnabled`
+
+Use this prop to have a semi-transparent dark overlay visible under the card during transitions. Defaults to `true` on Android and `false` on iOS.
+
+#### `cardOverlay`
+
+Function which returns a React Element to display as the overlay for the card. Make sure to set `cardOverlayEnabled` to `true` when using this.
+
+#### `cardStyle`
+
+Style object for the card in stack. You can provide a custom background color to use instead of the default background here.
+
+You can also specify `{ backgroundColor: 'transparent' }` to make the previous screen visible underneath (for transparent modals). This is useful to implement things like modal dialogs. You should also specify `presentation: 'modal'` in the options when using a transparent background so previous screens aren't detached and stay visible underneath.
+
+On Web, the height of the screen isn't limited to the height of the viewport. This is by design to allow the browser's address bar to hide when scrolling. If this isn't desirable behavior, you can set `cardStyle` to `{ flex: 1 }` to force the screen to fill the viewport.
+
+#### `presentation`
+
+This is shortcut option which configures several options to configure the style for rendering and transitions:
+
+- `card`: Use the default OS animations for iOS and Android screen transitions.
+- `modal`: Use Modal animations. This changes a few things:
+ - Sets `headerMode` to `screen` for the screen unless specified otherwise.
+ - Changes the screen animation to match the platform behavior for modals.
+- `transparentModal`: Similar to `modal`. This changes following things:
+ - Sets `headerMode` to `screen` for the screen unless specified otherwise.
+ - Sets background color of the screen to transparent, so previous screen is visible
+ - Adjusts the `detachPreviousScreen` option so that the previous screen stays rendered.
+ - Prevents the previous screen from animating from its last position.
+ - Changes the screen animation to a vertical slide animation.
+
+See [Transparent modals](#transparent-modals) for more details on how to customize `transparentModal`.
+
+#### `animationTypeForReplace`
+
+The type of animation to use when this screen replaces another screen. It takes the following values:
+
+- `push` - The animation of a new screen being pushed will be used
+- `pop` - The animation of a screen being popped will be used
+
+Defaults to `push`.
+
+When `pop` is used, the `pop` animation is applied to the screen being replaced.
+
+#### `gestureEnabled`
+
+Whether you can use gestures to dismiss this screen. Defaults to `true` on iOS, `false` on Android.
+
+Gestures are not supported on Web.
+
+#### `gestureResponseDistance`
+
+Number to override the distance of touch start from the edge of the screen to recognize gestures.
+
+It'll configure either the horizontal or vertical distance based on the [`gestureDirection`](#gesturedirection) value.
+
+The default values are:
+
+- `50` - when `gestureDirection` is `horizontal` or `horizontal-inverted`
+- `135` - when `gestureDirection` is `vertical` or `vertical-inverted`
+
+This is not supported on Web.
+
+#### `gestureVelocityImpact`
+
+Number which determines the relevance of velocity for the gesture. Defaults to 0.3.
+
+This is not supported on Web.
+
+#### `gestureDirection`
+
+Direction of the gestures. Refer the [Animations section](#animations) for details.
+
+This is not supported on Web.
+
+#### `transitionSpec`
+
+Configuration object for the screen transition. Refer the [Animations section](#animations) for details.
+
+#### `cardStyleInterpolator`
+
+Interpolated styles for various parts of the card. Refer the [Animations section](#animations) for details.
+
+#### `headerStyleInterpolator`
+
+Interpolated styles for various parts of the header. Refer the [Animations section](#animations) for details.
+
+#### `keyboardHandlingEnabled`
+
+If `false`, the keyboard will NOT automatically dismiss when navigating to a new screen from this screen. Defaults to `true`.
+
+#### `detachPreviousScreen`
+
+Boolean used to indicate whether to detach the previous screen from the view hierarchy to save memory. Set it to `false` if you need the previous screen to be seen through the active screen. Only applicable if `detachInactiveScreens` isn't set to `false`.
+
+This is automatically adjusted when using [`presentation`](#presentation) as `transparentModal` or `modal` to keep the required screens visible. Defaults to `true` in other cases.
+
+#### `freezeOnBlur`
+
+Boolean indicating whether to prevent inactive screens from re-rendering. Defaults to `false`.
+Defaults to `true` when `enableFreeze()` from `react-native-screens` package is run at the top of the application.
+
+Only supported on iOS and Android.
+
+### 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 `Stack.Navigator` or `options` prop of `Stack.Screen`. You don't have to be using `@react-navigation/elements` directly to use these options, they are just documented in that page.
+
+In addition to those, the following options are also supported in stack:
+
+#### `header`
+
+Custom header to use instead of the default header.
+
+This accepts a function that returns a React Element to display as a header. The function receives an object containing the following properties as the argument:
+
+- `navigation` - The navigation object for the current screen.
+- `route` - The route object for the current screen.
+- `options` - The options for the current screen
+- `layout` - Dimensions of the screen, contains `height` and `width` properties.
+- `progress` Animated nodes representing the progress of the animation.
+- `back` - Options for the back button, contains an object with a `title` property to use for back button label.
+- `styleInterpolator` - Function which returns interpolated styles for various elements in the header.
+
+Make sure to set `headerMode` to `screen` as well when using a custom header (see below for more details).
+
+Example:
+
+```js
+import { getHeaderTitle } from '@react-navigation/elements';
+
+// ..
+
+header: ({ navigation, route, options, back }) => {
+ const title = getHeaderTitle(options, route.name);
+
+ return (
+ : undefined
+ }
+ style={options.headerStyle}
+ />
+ );
+};
+```
+
+To set a custom header for all the screens in the navigator, you can specify this option in the `screenOptions` prop of the navigator.
+
+When using a custom header, there are 2 things to keep in mind:
+
+##### Specify a `height` in `headerStyle` to avoid glitches
+
+If your header's height differs from the default header height, then you might notice glitches due to measurement being async. Explicitly specifying the height will avoid such glitches.
+
+Example:
+
+```js
+headerStyle: {
+ height: 80, // Specify the height of your custom header
+};
+```
+
+Note that this style is not applied to the header by default since you control the styling of your custom header. If you also want to apply this style to your header, use `headerStyle` from the props.
+
+##### Set `headerMode` to `float` for custom header animations
+
+By default, there is one floating header which renders headers for multiple screens on iOS for non-modals. These headers include animations to smoothly switch to one another.
+
+If you specify a custom header, React Navigation will change it to `screen` automatically so that the header animated along with the screen instead. This means that you don't have to implement animations to animate it separately.
+
+But you might want to keep the floating header to have a different transition animation between headers. To do that, you'll need to specify `headerMode: 'float'` in the options, and then interpolate on the `progress.current` and `progress.next` props in your custom header. For example, following will cross-fade the header:
+
+```js
+const opacity = Animated.add(progress.current, progress.next || 0).interpolate({
+ inputRange: [0, 1, 2],
+ outputRange: [0, 1, 0],
+});
+
+return (
+ {/* Header content */}
+);
+```
+
+#### `headerMode`
+
+Specifies how the header should be rendered:
+
+- `float` - The header is rendered above the screen and animates independently of the screen. This is default on iOS for non-modals.
+- `screen` - The header is rendered as part of the screen and animates together with the screen. This is default on other platforms.
+
+#### `headerShown`
+
+Whether to show or hide the header for the screen. The header is shown by default. Setting this to `false` hides the header.
+
+#### `headerBackAllowFontScaling`
+
+Whether back button title font should scale to respect Text Size accessibility settings. Defaults to false.
+
+#### `headerBackAccessibilityLabel`
+
+Accessibility label for the header back button.
+
+#### `headerBackImage`
+
+Function which returns a React Element to display custom image in header's back button. When a function is used, it receives the `tintColor` in it's argument object. Defaults to Image component with back image source, which is the default back icon image for the platform (a chevron on iOS and an arrow on Android).
+
+#### `headerBackTitle`
+
+Title string used by the back button on iOS. Defaults to the previous scene's title. Use `headerBackButtonDisplayMode` to customize the behavior.
+
+#### `headerTruncatedBackTitle`
+
+Title string used by the back button when `headerBackTitle` doesn't fit on the screen. `"Back"` by default.
+
+#### `headerBackButtonDisplayMode`
+
+How the back button displays icon and title.
+
+Supported values:
+
+- `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).
+- `minimal`: Always displays only the icon without a title.
+
+Defaults to `default` on iOS, and `minimal` on Android.
+
+#### `headerBackTitleStyle`
+
+Style object for the back title.
+
+### Events
+
+The navigator can [emit events](navigation-events.md) on certain actions. Supported events are:
+
+#### `transitionStart`
+
+This event is fired when the transition animation starts for the current screen.
+
+Event data:
+
+- `e.data.closing` - Boolean indicating whether the screen is being opened or closed.
+
+Example:
+
+```js
+React.useEffect(() => {
+ const unsubscribe = navigation.addListener('transitionStart', (e) => {
+ // Do something
+ });
+
+ return unsubscribe;
+}, [navigation]);
+```
+
+#### `transitionEnd`
+
+This event is fired when the transition animation ends for the current screen.
+
+Event data:
+
+- `e.data.closing` - Boolean indicating whether the screen was opened or closed.
+
+Example:
+
+```js
+React.useEffect(() => {
+ const unsubscribe = navigation.addListener('transitionEnd', (e) => {
+ // Do something
+ });
+
+ return unsubscribe;
+}, [navigation]);
+```
+
+#### `gestureStart`
+
+This event is fired when the swipe gesture starts for the current screen.
+
+Example:
+
+```js
+React.useEffect(() => {
+ const unsubscribe = navigation.addListener('gestureStart', (e) => {
+ // Do something
+ });
+
+ return unsubscribe;
+}, [navigation]);
+```
+
+#### `gestureEnd`
+
+This event is fired when the swipe gesture ends for the current screen. e.g. a screen was successfully dismissed.
+
+Example:
+
+```js
+React.useEffect(() => {
+ const unsubscribe = navigation.addListener('gestureEnd', (e) => {
+ // Do something
+ });
+
+ return unsubscribe;
+}, [navigation]);
+```
+
+#### `gestureCancel`
+
+This event is fired when the swipe gesture is cancelled for the current screen. e.g. a screen wasn't dismissed by the gesture.
+
+Example:
+
+```js
+React.useEffect(() => {
+ const unsubscribe = navigation.addListener('gestureCancel', (e) => {
+ // Do something
+ });
+
+ return unsubscribe;
+}, [navigation]);
+```
+
+### Helpers
+
+The stack navigator adds the following methods to the navigation object:
+
+#### `replace`
+
+Replaces the current screen with a new screen in the stack. The method accepts the following arguments:
+
+- `name` - _string_ - Name of the route to push onto the stack.
+- `params` - _object_ - Screen params to pass to the destination route.
+
+```js
+navigation.replace('Profile', { owner: 'Michaś' });
+```
+
+#### `push`
+
+Pushes a new screen to the top of the stack and navigate to it. The method accepts the following arguments:
+
+- `name` - _string_ - Name of the route to push onto the stack.
+- `params` - _object_ - Screen params to pass to the destination route.
+
+```js
+navigation.push('Profile', { owner: 'Michaś' });
+```
+
+#### `pop`
+
+Pops the current screen from the stack and navigates back to the previous screen. It takes one optional argument (`count`), which allows you to specify how many screens to pop back by.
+
+```js
+navigation.pop();
+```
+
+#### `popTo`
+
+Navigates back to a previous screen in the stack by popping screens after it. The method accepts the following arguments:
+
+- `name` - _string_ - Name of the route to navigate to.
+- `params` - _object_ - Screen params to pass to the destination route.
+- `options` - Options object containing the following properties:
+ - `merge` - _boolean_ - Whether params should be merged with the existing route params, or replace them (when navigating to an existing screen). Defaults to `false`.
+
+If a matching screen is not found in the stack, this will pop the current screen and add a new screen with the specified name and params.
+
+```js
+navigation.popTo('Profile', { owner: 'Michaś' });
+```
+
+#### `popToTop`
+
+Pops all of the screens in the stack except the first one and navigates to it.
+
+```js
+navigation.popToTop();
+```
+
+### Hooks
+
+The stack navigator exports the following hooks:
+
+#### `useCardAnimation`
+
+This hook returns values related to the screen's animation. It contains the following properties:
+
+- `current` - Values for the current screen:
+ - `progress` - Animated node representing the progress value of the current screen.
+- `next` - Values for the screen after this one in the stack. This can be `undefined` in case the screen animating is the last one.
+ - `progress` - Animated node representing the progress value of the next screen.
+- `closing` - Animated node representing whether the card is closing. `1` when closing, `0` if not.
+- `swiping` - Animated node representing whether the card is being swiped. `1` when swiping, `0` if not.
+- `inverted` - Animated node representing whether the card is inverted. `-1` when inverted, `1` if not.
+- `index` - The index of the card in the stack.
+- `layouts` - Layout measurements for various items we use for animation.
+ - `screen` - Layout of the whole screen. Contains `height` and `width` properties.
+- `insets` - Layout of the safe area insets. Contains `top`, `right`, `bottom` and `left` properties.
+
+See [Transparent modals](#transparent-modals) for an example of how to use this hook.
+
+## Animations
+
+You can specify the `animation` option to customize the transition animation for screens being pushed or popped.
+
+Supported values for `animation` are:
+
+- `default` - Default animation based on the platform and OS version.
+- `fade` - Simple fade animation for dialogs.
+- `fade_from_bottom` - Standard Android-style fade-in from the bottom for Android Oreo.
+- `fade_from_right` - Standard Android-style fade-in from the right for Android 14.
+- `reveal_from_bottom` - Standard Android-style reveal from the bottom for Android Pie.
+- `scale_from_center` - Scale animation from the center.
+- `slide_from_right` - Standard iOS-style slide in from the right.
+- `slide_from_left` - Similar to `slide_from_right`, but the screen will slide in from the left.
+- `slide_from_bottom` - Slide animation from the bottom for modals and bottom sheets.
+- `none` - The screens are pushed or popped immediately without any animation.
+
+By default, Android and iOS use the `default` animation and other platforms use `none`.
+
+If you need more control over the animation, you can customize individual parts of the animation using the various animation-related options:
+
+### Animation related options
+
+Stack Navigator exposes various options to configure the transition animation when a screen is added or removed. These transition animations can be customized on a per-screen basis by specifying the options in the `options` prop for each screen.
+
+- `gestureDirection` - The direction of swipe gestures:
+ - `horizontal` - The gesture to close the screen will start from the left, and from the right in RTL. For animations, screen will slide from the right with `SlideFromRightIOS`, and from the left in RTL.
+ - `horizontal-inverted` - The gesture to close the screen will start from the right, and from the left in RTL. For animations, screen will slide from the left with `SlideFromRightIOS`, and from the right in RTL as the direction is inverted.
+ - `vertical` - The gesture to close the screen will start from the top. For animations, screen will slide from the bottom.
+ - `vertical-inverted` - The gesture to close the screen will start from the bottom. For animations, screen will slide from the top.
+
+ You may want to specify a matching horizontal/vertical animation along with `gestureDirection` as well. For the animations included in the library, if you set `gestureDirection` to one of the inverted ones, it'll also flip the animation direction.
+
+- `transitionSpec` - An object which specifies the animation type (`timing` or `spring`) and their options (such as `duration` for `timing`). It takes 2 properties:
+ - `open` - Configuration for the transition when adding a screen
+ - `close` - Configuration for the transition when removing a screen.
+
+ Each of the object should specify 2 properties:
+ - `animation` - The animation function to use for the animation. Supported values are `timing` and `spring`.
+ - `config` - The configuration object for the timing function. For `timing`, it can be `duration` and `easing`. For `spring`, it can be `stiffness`, `damping`, `mass`, `overshootClamping`, `restDisplacementThreshold` and `restSpeedThreshold`.
+
+ A config which uses spring animation looks like this:
+
+ ```js
+ const config = {
+ animation: 'spring',
+ config: {
+ stiffness: 1000,
+ damping: 500,
+ mass: 3,
+ overshootClamping: true,
+ restDisplacementThreshold: 0.01,
+ restSpeedThreshold: 0.01,
+ },
+ };
+ ```
+
+ We can pass this config in the `transitionSpec` option:
+
+ ```js name="Custom Transition Config" snack static2dynamic
+ import * as React from 'react';
+ import { Text, View } from 'react-native';
+ import {
+ createStaticNavigation,
+ useNavigation,
+ } from '@react-navigation/native';
+ import { Button } from '@react-navigation/elements';
+ import { createStackNavigator } from '@react-navigation/stack';
+
+ function HomeScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ Home Screen
+
+
+ );
+ }
+
+ function ProfileScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ Profile Screen
+
+
+ );
+ }
+
+ // codeblock-focus-start
+ const config = {
+ animation: 'spring',
+ config: {
+ stiffness: 1000,
+ damping: 500,
+ mass: 3,
+ overshootClamping: true,
+ restDisplacementThreshold: 0.01,
+ restSpeedThreshold: 0.01,
+ },
+ };
+
+ const MyStack = createStackNavigator({
+ screens: {
+ Home: HomeScreen,
+ Profile: {
+ screen: ProfileScreen,
+ options: {
+ transitionSpec: {
+ open: config,
+ close: config,
+ },
+ },
+ },
+ },
+ });
+ // codeblock-focus-end
+
+ const Navigation = createStaticNavigation(MyStack);
+
+ export default function App() {
+ return ;
+ }
+ ```
+
+- `cardStyleInterpolator` - This is a function which specifies interpolated styles for various parts of the card. This allows you to customize the transitions when navigating from screen to screen. It is expected to return at least empty object, possibly containing interpolated styles for container, the card itself, overlay and shadow. Supported properties are:
+ - `containerStyle` - Style for the container view wrapping the card.
+ - `cardStyle` - Style for the view representing the card.
+ - `overlayStyle` - Style for the view representing the semi-transparent overlay below
+ - `shadowStyle` - Style for the view representing the card shadow.
+
+ The function receives the following properties in its argument:
+ - `current` - Values for the current screen:
+ - `progress` - Animated node representing the progress value of the current screen.
+ - `next` - Values for the screen after this one in the stack. This can be `undefined` in case the screen animating is the last one.
+ - `progress` - Animated node representing the progress value of the next screen.
+ - `index` - The index of the card in the stack.
+ - `closing` - Animated node representing whether the card is closing. `1` when closing, `0` if not.
+ - `layouts` - Layout measurements for various items we use for animation.
+ - `screen` - Layout of the whole screen. Contains `height` and `width` properties.
+
+ > **Note that when a screen is not the last, it will use the next screen's transition config.** This is because many transitions involve an animation of the previous screen, and so these two transitions need to be kept together to prevent running two different kinds of transitions on the two screens (for example a slide and a modal). You can check the `next` parameter to find out if you want to animate out the previous screen. For more information about this parameter, see [Animation](stack-navigator.md#animations) section.
+
+ A config which just fades the screen looks like this:
+
+ ```js
+ const forFade = ({ current }) => ({
+ cardStyle: {
+ opacity: current.progress,
+ },
+ });
+ ```
+
+ We can pass this function in `cardStyleInterpolator` option:
+
+ ```js name="Custom Card Style Interpolator" snack static2dynamic
+ import * as React from 'react';
+ import { Text, View } from 'react-native';
+ import {
+ createStaticNavigation,
+ useNavigation,
+ } from '@react-navigation/native';
+ import { Button } from '@react-navigation/elements';
+ import { createStackNavigator } from '@react-navigation/stack';
+
+ function HomeScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ Home Screen
+
+
+ );
+ }
+
+ function ProfileScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ Profile Screen
+
+
+ );
+ }
+
+ // codeblock-focus-start
+ const forFade = ({ current }) => ({
+ cardStyle: {
+ opacity: current.progress,
+ },
+ });
+
+ const MyStack = createStackNavigator({
+ screens: {
+ Home: HomeScreen,
+ Profile: {
+ screen: ProfileScreen,
+ options: {
+ cardStyleInterpolator: forFade,
+ },
+ },
+ },
+ });
+ // codeblock-focus-end
+
+ const Navigation = createStaticNavigation(MyStack);
+
+ export default function App() {
+ return ;
+ }
+ ```
+
+The interpolator will be called for each screen. For example, say you have a 2 screens in the stack, A & B. B is the new screen coming into focus and A is the previous screen. The interpolator will be called for each screen:
+
+- The interpolator is called for `B`: Here, the `current.progress` value represents the progress of the transition, which will start at `0` and end at `1`. There won't be a `next.progress` since `B` is the last screen.
+- The interpolator is called for `A`: Here, the `current.progress` will stay at the value of `1` and won't change, since the current transition is running for `B`, not `A`. The `next.progress` value represents the progress of `B` and will start at `0` and end at `1`.
+
+Say we want to animate both screens during the transition. The easiest way to do it would be to combine the progress value of current and next screens:
+
+```js
+const progress = Animated.add(
+ current.progress.interpolate({
+ inputRange: [0, 1],
+ outputRange: [0, 1],
+ extrapolate: 'clamp',
+ }),
+ next
+ ? next.progress.interpolate({
+ inputRange: [0, 1],
+ outputRange: [0, 1],
+ extrapolate: 'clamp',
+ })
+ : 0
+);
+```
+
+Here, the screen `A` will have both `current.progress` and `next.progress`, and since `current.progress` stays at `1` and `next.progress` is changing, combined, the progress will change from `1` to `2`. The screen `B` will only have `current.progress` which will change from `0` to `1`. So, we can apply different interpolations for `0-1` and `1-2` to animate focused screen and unfocused screen respectively.
+
+A config which translates the previous screen slightly to the left, and translates the current screen from the right edge would look like this:
+
+```js
+const forSlide = ({ current, next, inverted, layouts: { screen } }) => {
+ const progress = Animated.add(
+ current.progress.interpolate({
+ inputRange: [0, 1],
+ outputRange: [0, 1],
+ extrapolate: 'clamp',
+ }),
+ next
+ ? next.progress.interpolate({
+ inputRange: [0, 1],
+ outputRange: [0, 1],
+ extrapolate: 'clamp',
+ })
+ : 0
+ );
+
+ return {
+ cardStyle: {
+ transform: [
+ {
+ translateX: Animated.multiply(
+ progress.interpolate({
+ inputRange: [0, 1, 2],
+ outputRange: [
+ screen.width, // Focused, but offscreen in the beginning
+ 0, // Fully focused
+ screen.width * -0.3, // Fully unfocused
+ ],
+ extrapolate: 'clamp',
+ }),
+ inverted
+ ),
+ },
+ ],
+ },
+ };
+};
+```
+
+- `headerStyleInterpolator` - This is a function which specifies interpolated styles for various parts of the header. It is expected to return at least empty object, possibly containing interpolated styles for left label and button, right button, title and background. Supported properties are:
+ - `leftLabelStyle` - Style for the label of the left button (back button label).
+ - `leftButtonStyle` - Style for the left button (usually the back button).
+ - `rightButtonStyle` - Style for the right button.
+ - `titleStyle` - Style for the header title text.
+ - `backgroundStyle` - Style for the header background.
+
+ The function receives the following properties in it's argument:
+ - `current` - Values for the current screen (the screen which owns this header).
+ - `progress` - Animated node representing the progress value of the current screen. `0` when screen should start coming into view, `0.5` when it's mid-way, `1` when it should be fully in view.
+ - `next` - Values for the screen after this one in the stack. This can be `undefined` in case the screen animating is the last one.
+ - `progress` - Animated node representing the progress value of the next screen.
+ - `layouts` - Layout measurements for various items we use for animation. Each layout object contain `height` and `width` properties.
+ - `screen` - Layout of the whole screen.
+ - `title` - Layout of the title element. Might be `undefined` when not rendering a title.
+ - `leftLabel` - Layout of the back button label. Might be `undefined` when not rendering a back button label.
+
+ A config that just fades the elements looks like this:
+
+ ```js
+ const forFade = ({ current, next }) => {
+ const opacity = Animated.add(
+ current.progress,
+ next ? next.progress : 0
+ ).interpolate({
+ inputRange: [0, 1, 2],
+ outputRange: [0, 1, 0],
+ });
+
+ return {
+ leftButtonStyle: { opacity },
+ rightButtonStyle: { opacity },
+ titleStyle: { opacity },
+ backgroundStyle: { opacity },
+ };
+ };
+ ```
+
+ We can pass this function in `headerStyleInterpolator` option:
+
+ ```js name="Custom Header Style Interpolator" snack static2dynamic
+ import * as React from 'react';
+ import { Text, View } from 'react-native';
+ import {
+ createStaticNavigation,
+ useNavigation,
+ } from '@react-navigation/native';
+ import { Button } from '@react-navigation/elements';
+ import { createStackNavigator } from '@react-navigation/stack';
+
+ function HomeScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ Home Screen
+
+
+ );
+ }
+
+ function ProfileScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ Profile Screen
+
+
+ );
+ }
+
+ // codeblock-focus-start
+ const forFade = ({ current, next }) => {
+ const opacity = Animated.add(
+ current.progress,
+ next ? next.progress : 0
+ ).interpolate({
+ inputRange: [0, 1, 2],
+ outputRange: [0, 1, 0],
+ });
+
+ return {
+ leftButtonStyle: { opacity },
+ rightButtonStyle: { opacity },
+ titleStyle: { opacity },
+ backgroundStyle: { opacity },
+ };
+ };
+
+ const MyStack = createStackNavigator({
+ screens: {
+ Home: HomeScreen,
+ Profile: {
+ screen: ProfileScreen,
+ options: {
+ headerStyleInterpolator: forFade,
+ },
+ },
+ },
+ });
+ // codeblock-focus-end
+
+ const Navigation = createStaticNavigation(MyStack);
+
+ export default function App() {
+ return ;
+ }
+ ```
+
+### Pre-made configs
+
+With these options, it's possible to build custom transition animations for screens. We also export various configs from the library with ready-made animations which you can use:
+
+#### `TransitionSpecs`
+
+- `TransitionIOSSpec` - Exact values from UINavigationController's animation configuration.
+- `FadeInFromBottomAndroidSpec` - Configuration for activity open animation from Android Nougat.
+- `FadeOutToBottomAndroidSpec` - Configuration for activity close animation from Android Nougat.
+- `RevealFromBottomAndroidSpec` - Approximate configuration for activity open animation from Android Pie.
+
+Example:
+
+```js
+import { TransitionSpecs } from '@react-navigation/stack';
+
+// ...
+
+;
+```
+
+#### `CardStyleInterpolators`
+
+- `forHorizontalIOS` - Standard iOS-style slide in from the right.
+- `forVerticalIOS` - Standard iOS-style slide in from the bottom (used for modals).
+- `forModalPresentationIOS` - Standard iOS-style modal animation in iOS 13.
+- `forFadeFromBottomAndroid` - Standard Android-style fade in from the bottom for Android Oreo.
+- `forRevealFromBottomAndroid` - Standard Android-style reveal from the bottom for Android Pie.
+
+Example configuration for Android Oreo style vertical screen fade animation:
+
+```js name="Card Style Interpolators" snack static2dynamic
+import * as React from 'react';
+import { Text, View } from 'react-native';
+import {
+ createStaticNavigation,
+ useNavigation,
+} from '@react-navigation/native';
+import { Button } from '@react-navigation/elements';
+// codeblock-focus-start
+import {
+ createStackNavigator,
+ CardStyleInterpolators,
+} from '@react-navigation/stack';
+// codeblock-focus-end
+
+function HomeScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ Home Screen
+
+
+ );
+}
+
+function ProfileScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ Profile Screen
+
+
+ );
+}
+
+// codeblock-focus-start
+const MyStack = createStackNavigator({
+ screens: {
+ Home: HomeScreen,
+ Profile: {
+ screen: ProfileScreen,
+ options: {
+ title: 'Profile',
+ cardStyleInterpolator: CardStyleInterpolators.forFadeFromBottomAndroid,
+ },
+ },
+ },
+});
+// codeblock-focus-end
+
+const Navigation = createStaticNavigation(MyStack);
+
+export default function App() {
+ return ;
+}
+```
+
+#### `HeaderStyleInterpolators`
+
+- `forUIKit` - Standard UIKit style animation for the header where the title fades into the back button label.
+- `forFade` - Simple fade animation for the header elements.
+- `forStatic` - Simple translate animation to translate the header along with the sliding screen.
+
+Example configuration for default iOS animation for header elements where the title fades into the back button:
+
+```js name="Header Style Interpolators" snack static2dynamic
+import * as React from 'react';
+import { Text, View } from 'react-native';
+import {
+ createStaticNavigation,
+ useNavigation,
+} from '@react-navigation/native';
+import { Button } from '@react-navigation/elements';
+// codeblock-focus-start
+import {
+ createStackNavigator,
+ HeaderStyleInterpolators,
+} from '@react-navigation/stack';
+// codeblock-focus-end
+
+function HomeScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ Home Screen
+
+
+ );
+}
+
+function ProfileScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ Profile Screen
+
+
+ );
+}
+
+// codeblock-focus-start
+const MyStack = createStackNavigator({
+ screens: {
+ Home: HomeScreen,
+ Profile: {
+ screen: ProfileScreen,
+ options: {
+ title: 'Profile',
+ headerStyleInterpolator: HeaderStyleInterpolators.forUIKit,
+ },
+ },
+ },
+});
+// codeblock-focus-end
+
+const Navigation = createStaticNavigation(MyStack);
+
+export default function App() {
+ return ;
+}
+```
+
+:::warning
+
+Always define your animation configuration at the top-level of the file to ensure that the references don't change across re-renders. This is important for smooth and reliable transition animations.
+
+:::
+
+#### `TransitionPresets`
+
+We export various transition presets which bundle various set of these options together to match certain native animations. A transition preset is an object containing few animation related screen options exported under `TransitionPresets`. Currently the following presets are available:
+
+- `SlideFromRightIOS` - Standard iOS navigation transition.
+- `ModalSlideFromBottomIOS` - Standard iOS navigation transition for modals.
+- `ModalPresentationIOS` - Standard iOS modal presentation style (introduced in iOS 13).
+- `FadeFromBottomAndroid` - Standard Android navigation transition when opening or closing an Activity on Android < 9 (Oreo).
+- `RevealFromBottomAndroid` - Standard Android navigation transition when opening or closing an Activity on Android 9 (Pie).
+- `ScaleFromCenterAndroid` - Standard Android navigation transition when opening or closing an Activity on Android >= 10.
+- `DefaultTransition` - Default navigation transition for the current platform.
+- `ModalTransition` - Default modal transition for the current platform.
+
+You can spread these presets in `options` to customize the animation for a screen:
+
+```js name="Transition Presets - Modal Slide" snack static2dynamic
+import * as React from 'react';
+import { Text, View } from 'react-native';
+import {
+ createStaticNavigation,
+ useNavigation,
+} from '@react-navigation/native';
+import { Button } from '@react-navigation/elements';
+// codeblock-focus-start
+import {
+ createStackNavigator,
+ TransitionPresets,
+} from '@react-navigation/stack';
+// codeblock-focus-end
+
+function HomeScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ Home Screen
+
+
+ );
+}
+
+function ProfileScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ Profile Screen
+
+
+ );
+}
+
+// codeblock-focus-start
+const MyStack = createStackNavigator({
+ screens: {
+ Home: HomeScreen,
+ Profile: {
+ screen: ProfileScreen,
+ options: {
+ title: 'Profile',
+ ...TransitionPresets.ModalSlideFromBottomIOS,
+ },
+ },
+ },
+});
+// codeblock-focus-end
+
+const Navigation = createStaticNavigation(MyStack);
+
+export default function App() {
+ return ;
+}
+```
+
+If you want to customize the transition animations for all of the screens in the navigator, you can specify it in `screenOptions` prop for the navigator.
+
+Example configuration for iOS modal presentation style:
+
+```js name="Transition Presets - Modal Presentation" snack static2dynamic
+import * as React from 'react';
+import { Text, View } from 'react-native';
+import {
+ createStaticNavigation,
+ useNavigation,
+} from '@react-navigation/native';
+import { Button } from '@react-navigation/elements';
+// codeblock-focus-start
+import {
+ createStackNavigator,
+ TransitionPresets,
+} from '@react-navigation/stack';
+// codeblock-focus-end
+
+function HomeScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ Home Screen
+
+
+ );
+}
+
+function ProfileScreen() {
+ const navigation = useNavigation();
+
+ return (
+
+ Profile Screen
+
+
+ );
+}
+
+// codeblock-focus-start
+const MyStack = createStackNavigator({
+ initialRouteName: 'Home',
+ screenOptions: {
+ headerShown: false,
+ ...TransitionPresets.ModalPresentationIOS,
+ },
+ screens: {
+ Home: HomeScreen,
+ Profile: ProfileScreen,
+ },
+});
+// codeblock-focus-end
+
+const Navigation = createStaticNavigation(MyStack);
+
+export default function App() {
+ return ;
+}
+```
+
+### Transparent modals
+
+A transparent modal is like a modal dialog which overlays the screen. The previous screen still stays visible underneath. To get a transparent modal screen, you can specify `presentation: 'transparentModal'` in the screen's options.
+
+Example:
+
+```js
+
+
+
+
+```
+
+Now, when you navigate to the `Modal` screen, it'll have a transparent background and the `Home` screen will be visible underneath.
+
+In addition to `presentation`, you might want to optionally specify few more things to get a modal dialog like behavior:
+
+- Disable the header with `headerShown: false`
+- Enable the overlay with `cardOverlayEnabled: true` (you can't tap the overlay to close the screen this way, see below for alternatives)
+
+If you want to further customize how the dialog animates, or want to close the screen when tapping the overlay etc., you can use the `useCardAnimation` hook to customize elements inside your screen.
+
+Example:
+
+```js
+import { Animated, View, Text, Pressable, StyleSheet } from 'react-native';
+import { useTheme, useNavigation } from '@react-navigation/native';
+import { useCardAnimation } from '@react-navigation/stack';
+import { Button } from '@react-navigation/elements';
+
+function ModalScreen() {
+ const navigation = useNavigation();
+ const { colors } = useTheme();
+ const { current } = useCardAnimation();
+
+ return (
+
+
+
+
+ Mise en place is a French term that literally means “put in place.” It
+ also refers to a way cooks in professional kitchens and restaurants
+ set up their work stations—first by gathering all ingredients for a
+ recipes, partially preparing them (like measuring out and chopping),
+ and setting them all near each other. Setting up mise en place before
+ cooking is another top tip for home cooks, as it seriously helps with
+ organization. It’ll pretty much guarantee you never forget to add an
+ ingredient and save you time from running back and forth from the
+ pantry ten times.
+
+
+
+
+ );
+}
+```
+
+Here we animate the scale of the dialog, and also add an overlay to close the dialog.
diff --git a/versioned_docs/version-8.x/state-persistence.md b/versioned_docs/version-8.x/state-persistence.md
new file mode 100755
index 00000000000..1e9cc94e24f
--- /dev/null
+++ b/versioned_docs/version-8.x/state-persistence.md
@@ -0,0 +1,314 @@
+---
+id: state-persistence
+title: State persistence
+sidebar_label: State persistence
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+You might want to save the user's location in the app, so that they are immediately returned to the same location after the app is restarted.
+
+This is especially valuable during development because it allows the developer to stay on the same screen when they refresh the app.
+
+## Usage
+
+To be able to persist the [navigation state](navigation-state.md), we can use the `onStateChange` and `initialState` props of the container.
+
+- `onStateChange` - This prop notifies us of any state changes. We can persist the state in this callback.
+- `initialState` - This prop allows us to pass an initial state to use for [navigation state](navigation-state.md). We can pass the restored state in this prop.
+
+
+
+
+```js name="Persisting the navigation state" snack dependencies=@react-native-async-storage/async-storage
+import * as React from 'react';
+// codeblock-focus-start
+import { Platform, View, Linking } from 'react-native';
+import AsyncStorage from '@react-native-async-storage/async-storage';
+import {
+ useNavigation,
+ createStaticNavigation,
+} from '@react-navigation/native';
+// codeblock-focus-end
+import { Button } from '@react-navigation/elements';
+import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+
+function A() {
+ return ;
+}
+
+function B() {
+ const navigation = useNavigation();
+
+ return (
+
+
+
+ );
+}
+
+function C() {
+ const navigation = useNavigation();
+
+ return (
+
+
+
+ );
+}
+
+function D() {
+ return ;
+}
+
+const HomeStackScreen = createNativeStackNavigator({
+ screens: {
+ A: A,
+ },
+});
+
+const SettingsStackScreen = createNativeStackNavigator({
+ screens: {
+ B: B,
+ C: C,
+ D: D,
+ },
+});
+
+const Tab = createBottomTabNavigator({
+ screens: {
+ Home: {
+ screen: HomeStackScreen,
+ options: {
+ headerShown: false,
+ tabBarLabel: 'Home!',
+ },
+ },
+ Settings: {
+ screen: SettingsStackScreen,
+ options: {
+ headerShown: false,
+ tabBarLabel: 'Settings!',
+ },
+ },
+ },
+});
+
+const Navigation = createStaticNavigation(Tab);
+
+// codeblock-focus-start
+
+const PERSISTENCE_KEY = 'NAVIGATION_STATE_V1';
+
+export default function App() {
+ const [isReady, setIsReady] = React.useState(Platform.OS === 'web'); // Don't persist state on web since it's based on URL
+ const [initialState, setInitialState] = React.useState();
+
+ React.useEffect(() => {
+ const restoreState = async () => {
+ try {
+ const initialUrl = await Linking.getInitialURL();
+
+ if (Platform.OS !== 'web' && initialUrl == null) {
+ const savedState = await AsyncStorage.getItem(PERSISTENCE_KEY);
+ const state = savedState ? JSON.parse(savedState) : undefined;
+
+ if (state !== undefined) {
+ setInitialState(state);
+ }
+ }
+ } finally {
+ setIsReady(true);
+ }
+ };
+
+ if (!isReady) {
+ restoreState();
+ }
+ }, [isReady]);
+
+ if (!isReady) {
+ return null;
+ }
+
+ return (
+
+ AsyncStorage.setItem(PERSISTENCE_KEY, JSON.stringify(state))
+ }
+ />
+ );
+}
+// codeblock-focus-end
+```
+
+
+
+
+```js name="Persisting the navigation state" snack dependencies=@react-native-async-storage/async-storage
+import * as React from 'react';
+// codeblock-focus-start
+import { Platform, View, Linking } from 'react-native';
+import AsyncStorage from '@react-native-async-storage/async-storage';
+import { NavigationContainer, useNavigation } from '@react-navigation/native';
+// codeblock-focus-end
+import { Button } from '@react-navigation/elements';
+import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+
+const Tab = createBottomTabNavigator();
+const HomeStack = createNativeStackNavigator();
+const SettingsStack = createNativeStackNavigator();
+
+function A() {
+ return ;
+}
+
+function B() {
+ const navigation = useNavigation();
+
+ return (
+
+
+
+ );
+}
+
+function C() {
+ const navigation = useNavigation();
+
+ return (
+
+
+
+ );
+}
+
+function D() {
+ return ;
+}
+
+function HomeStackScreen() {
+ return (
+
+
+
+ );
+}
+
+function SettingsStackScreen() {
+ return (
+
+
+
+
+
+ );
+}
+
+function RootTabs() {
+ return (
+
+
+
+
+ );
+}
+
+// codeblock-focus-start
+
+const PERSISTENCE_KEY = 'NAVIGATION_STATE_V1';
+
+export default function App() {
+ const [isReady, setIsReady] = React.useState(Platform.OS === 'web'); // Don't persist state on web since it's based on URL
+ const [initialState, setInitialState] = React.useState();
+
+ React.useEffect(() => {
+ const restoreState = async () => {
+ try {
+ const initialUrl = await Linking.getInitialURL();
+
+ if (initialUrl == null) {
+ // Only restore state if there's no deep link
+ const savedStateString = await AsyncStorage.getItem(PERSISTENCE_KEY);
+ const state = savedStateString
+ ? JSON.parse(savedStateString)
+ : undefined;
+
+ if (state !== undefined) {
+ setInitialState(state);
+ }
+ }
+ } finally {
+ setIsReady(true);
+ }
+ };
+
+ if (!isReady) {
+ restoreState();
+ }
+ }, [isReady]);
+
+ if (!isReady) {
+ return null;
+ }
+
+ return (
+
+ AsyncStorage.setItem(PERSISTENCE_KEY, JSON.stringify(state))
+ }
+ >
+
+
+ );
+}
+// codeblock-focus-end
+```
+
+
+
+
+:::warning
+
+It is recommended to use an [error boundary](https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary) in your app and clear the persisted state if an error occurs. This will ensure that the app doesn't get stuck in an error state if a screen crashes.
+
+:::
+
+### Development Mode
+
+This feature is particularly useful in development mode. You can enable it selectively using the following approach:
+
+```js
+const [isReady, setIsReady] = React.useState(__DEV__ ? false : true);
+```
+
+While it can be used for production as well, use it with caution as it can make the app unusable if the app is crashing on a particular screen - as the user will still be on the same screen after restarting. So if you are using it in production, make sure to clear the persisted state if an error occurs.
+
+### Loading View
+
+Because the state is restored asynchronously, the app must render an empty/loading view for a moment before we have the initial state. To handle this, we can return a loading view when `isReady` is `false`:
+
+```js
+if (!isReady) {
+ return ;
+}
+```
+
+## Warning: Serializable State
+
+Each param, route, and navigation state must be fully serializable for this feature to work. Typically, you would serialize the state as a JSON string. This means that your routes and params must contain no functions, class instances, or recursive data structures. React Navigation already [warns you during development](troubleshooting.md#i-get-the-warning-non-serializable-values-were-found-in-the-navigation-state) if it encounters non-serializable data, so watch out for the warning if you plan to persist navigation state.
+
+You can modify the initial state object before passing it to container, but note that if your `initialState` isn't a [valid navigation state](navigation-state.md#stale-state-objects), React Navigation may not be able to handle the situation gracefully in some scenarios.
diff --git a/versioned_docs/version-8.x/static-configuration.md b/versioned_docs/version-8.x/static-configuration.md
new file mode 100644
index 00000000000..c71211f50af
--- /dev/null
+++ b/versioned_docs/version-8.x/static-configuration.md
@@ -0,0 +1,296 @@
+---
+id: static-configuration
+title: Static configuration
+sidebar_label: Static configuration
+---
+
+The bulk of the static configuration is done using the `createXNavigator` functions, e.g. [`createNativeStackNavigator`](native-stack-navigator.md), [`createBottomTabNavigator`](bottom-tab-navigator.md), [`createDrawerNavigator`](drawer-navigator.md) etc. We'll refer to these functions as `createXNavigator` in the rest of this guide.
+
+## `createXNavigator`
+
+The `createXNavigator` functions take one argument, which is an object with the following properties:
+
+- Same props as the navigator component, e.g. `id`, `initialRouteName`, `screenOptions` etc. See [Navigator](navigator.md) as well as the docs for each navigator for more details on the props they accept.
+- `screens` - an object containing configuration for each screen in the navigator.
+- `groups` - an optional object containing groups of screens (equivalent to [`Group`](group.md) in the dynamic API).
+
+For example:
+
+```js
+const RootStack = createNativeStackNavigator({
+ initialRouteName: 'Home',
+ screenOptions: {
+ headerTintColor: 'white',
+ headerStyle: {
+ backgroundColor: 'tomato',
+ },
+ },
+ screens: {
+ Home: HomeScreen,
+ Profile: ProfileScreen,
+ },
+});
+```
+
+### `screens`
+
+The `screens` object can contain key value pairs where the key is the name of the screen and the value can be several things:
+
+- A component to render:
+
+ ```js
+ const RootStack = createNativeStackNavigator({
+ screens: {
+ Home: HomeScreen,
+ },
+ });
+ ```
+
+- A navigator configured using `createXNavigator` for nested navigators:
+
+ ```js
+ const HomeTabs = createBottomTabNavigator({
+ screens: {
+ Groups: GroupsScreen,
+ Chats: ChatsScreen,
+ },
+ });
+
+ const RootStack = createNativeStackNavigator({
+ screens: {
+ Home: HomeTabs,
+ },
+ });
+ ```
+
+- An object containing configuration for the screen. This configuration contains the various properties:
+
+ ```js
+ const RootStack = createNativeStackNavigator({
+ screens: {
+ Home: {
+ screen: HomeScreen,
+ linking: {
+ path: 'home',
+ },
+ },
+ },
+ });
+ ```
+
+ See [Screen configuration](#screen-configuration) for more details.
+
+### `groups`
+
+The `groups` object can contain key-value pairs where the key is the name of the group and the value is the group configuration.
+
+The configuration object for a screen accepts the [properties described in the Group page](group.md). In addition, the following properties are available when using static configuration:
+
+- `if` - this can be used to conditionally render the group and works the same as the [`if` property in the screen configuration](#if).
+- `screens` - an object containing configuration for each screen in the group. The configuration is the same as the [`screens` object in the navigator configuration](#screens).
+
+Example:
+
+```js
+const RootStack = createNativeStackNavigator({
+ screens: {
+ Home: HomeScreen,
+ Profile: ProfileScreen,
+ },
+ groups: {
+ Guest: {
+ if: useIsGuest,
+ screenOptions: {
+ headerShown: false,
+ },
+ screens: {
+ // ...
+ },
+ },
+ User: {
+ if: useIsUser,
+ screens: {
+ // ...
+ },
+ },
+ },
+});
+```
+
+### Screen configuration
+
+The configuration object for a screen accepts the [properties described in the Screen page](screen.md). In addition, the following properties are available when using static configuration:
+
+#### `createXScreen`
+
+Each navigator exports a helper function to create screen configurations with proper TypeScript types. These helpers enable type inference for the params in the configuration.
+
+Example usage:
+
+```js
+import {
+ createNativeStackNavigator,
+ createNativeStackScreen,
+} from '@react-navigation/native-stack';
+
+const Stack = createNativeStackNavigator({
+ screens: {
+ Profile: createNativeStackScreen({
+ screen: ProfileScreen,
+ options: ({ route }) => {
+ const userId = route.params.userId;
+
+ return {
+ title: `${userId}'s profile`,
+ };
+ },
+ }),
+ },
+});
+```
+
+Each navigator exports its own helper function:
+
+- `createNativeStackScreen` from `@react-navigation/native-stack`
+- `createStackScreen` from `@react-navigation/stack`
+- `createBottomTabScreen` from `@react-navigation/bottom-tabs`
+- `createDrawerScreen` from `@react-navigation/drawer`
+- `createMaterialTopTabScreen` from `@react-navigation/material-top-tabs`
+
+#### `linking`
+
+[Linking configuration](configuring-links.md) for the screen. It can be either a string for a path or an object with the linking configuration:
+
+```js
+const RootStack = createNativeStackNavigator({
+ screens: {
+ Profile: {
+ screen: ProfileScreen,
+ linking: {
+ path: 'u/:userId',
+ parse: {
+ userId: (id) => id.replace(/^@/, ''),
+ },
+ stringify: {
+ userId: (id) => `@${id}`,
+ },
+ },
+ },
+ Chat: {
+ screen: ChatScreen,
+ linking: 'chat/:chatId',
+ },
+ },
+});
+```
+
+The `linking` object supports the same configuration options described in [Configuring links](configuring-links.md) such as `parse`, `stringify` and `exact`.
+
+To make deep links work on native apps, you also need to [configure your app](deep-linking.md) and pass `prefixes` to the navigation component returned by [`createStaticNavigation`](static-configuration.md#createstaticnavigation):
+
+```js
+const Navigation = createStaticNavigation(RootStack);
+
+const linking = {
+ prefixes: ['https://example.com', 'example://'],
+};
+
+function App() {
+ return ;
+}
+```
+
+#### `if`
+
+Callback to determine whether the screen should be rendered or not. It doesn't receive any arguments. This can be useful for conditional rendering of screens, e.g. - if you want to render a different screen for logged in users.
+
+You can use a custom hook to use custom logic to determine the return value:
+
+```js
+const useIsLoggedIn = () => {
+ const { isLoggedIn } = React.useContext(AuthContext);
+
+ return isLoggedIn;
+};
+
+const RootStack = createNativeStackNavigator({
+ screens: {
+ Home: {
+ screen: HomeScreen,
+ if: useIsLoggedIn,
+ },
+ },
+});
+```
+
+The above example will only render the `HomeScreen` if the user is logged in.
+
+For more details, see [Authentication flow](auth-flow.md?config=static).
+
+## `createStaticNavigation`
+
+The `createStaticNavigation` function takes the static config returned by `createXNavigator` functions and returns a React component to render:
+
+```js
+const Navigation = createStaticNavigation(RootStack);
+
+function App() {
+ return ;
+}
+```
+
+This component is a wrapper around the `NavigationContainer` component and accepts the [same props and ref as the `NavigationContainer`](navigation-container.md) component. It is intended to be rendered once at the root of your app similar to how you'd use `NavigationContainer` component.
+
+### Differences in the `linking` prop
+
+Similar to `NavigationContainer`, the component returned by `createStaticNavigation` also accepts a [`linking`](navigation-container.md#linking) prop. However, there are some key differences:
+
+1. It's not possible to pass a full `config` object to the `linking` prop. It can only accept [`path`](configuring-links.md#apps-under-subpaths) and an [`initialRouteName` for the root navigator](configuring-links.md#rendering-an-initial-route).
+2. The linking config is collected from the [`linking`](#linking) properties specified in the screen configuration.
+3. It's possible to pass `enabled: 'auto'` to automatically generate paths for all leaf screens:
+
+ ```js
+ const Navigation = createStaticNavigation(RootStack);
+
+ const linking = {
+ enabled: 'auto',
+ prefixes: ['https://example.com', 'example://'],
+ };
+
+ function App() {
+ return ;
+ }
+ ```
+
+ See [How does automatic path generation work](configuring-links.md#how-does-automatic-path-generation-work) for more details.
+
+## `createComponentForStaticNavigation`
+
+The `createComponentForStaticNavigation` function takes the static config returned by `createXNavigator` functions and returns a React component to render. The second argument is a name for the component that'd be used in React DevTools:
+
+```js
+const RootStackNavigator = createComponentForStaticNavigation(
+ RootStack,
+ 'RootNavigator'
+);
+```
+
+The returned component doesn't take any props. All of the configuration is inferred from the static config. It's essentially the same as defining a component using the dynamic API.
+
+This looks similar to `createStaticNavigation` however they are very different. When using static configuration, you'd never use this function directly. The only time you'd use this is if you're migrating away from static configuration and want to reuse existing code you wrote instead of rewriting it to the dynamic API. See [Combining static and dynamic APIs](combine-static-with-dynamic.md) for more details.
+
+## `createPathConfigForStaticNavigation`
+
+The `createPathConfigForStaticNavigation` function takes the static config returned by `createXNavigator` functions and returns a path config object that can be used within the linking config.
+
+```js
+const config = {
+ screens: {
+ Home: {
+ screens: createPathConfigForStaticNavigation(HomeTabs),
+ },
+ },
+};
+```
+
+Similar to `createComponentForStaticNavigation`, this is intended to be used when migrating away from static configuration. See [Combining static and dynamic APIs](combine-static-with-dynamic.md) for more details.
diff --git a/versioned_docs/version-8.x/status-bar.md b/versioned_docs/version-8.x/status-bar.md
new file mode 100755
index 00000000000..9b97b4820db
--- /dev/null
+++ b/versioned_docs/version-8.x/status-bar.md
@@ -0,0 +1,453 @@
+---
+id: status-bar
+title: Different status bar configuration based on route
+sidebar_label: Status bar configuration
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+If you don't have a navigation header, or your navigation header changes color based on the route, you'll want to ensure that the correct color is used for the content.
+
+## Stack
+
+This is a simple task when using a stack. You can render the `StatusBar` component, which is exposed by React Native, and set your config.
+
+
+
+
+```js name="Different status bar" snack
+import * as React from 'react';
+import { View, Text, StatusBar, StyleSheet } from 'react-native';
+import {
+ createStaticNavigation,
+ useNavigation,
+} from '@react-navigation/native';
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+import { Button } from '@react-navigation/elements';
+import { useSafeAreaInsets } from 'react-native-safe-area-context';
+
+function Screen1() {
+ const navigation = useNavigation();
+ const insets = useSafeAreaInsets();
+
+ return (
+
+ // highlight-start
+
+ // highlight-end
+ Light Screen
+
+
+ );
+}
+
+function Screen2() {
+ const navigation = useNavigation();
+ const insets = useSafeAreaInsets();
+
+ return (
+
+ // highlight-start
+
+ // highlight-end
+ Dark Screen
+
+
+ );
+}
+
+const RootStack = createNativeStackNavigator({
+ screenOptions: {
+ headerShown: false,
+ },
+ screens: {
+ Screen1: Screen1,
+ Screen2: Screen2,
+ },
+});
+
+const Navigation = createStaticNavigation(RootStack);
+
+export default function App() {
+ return ;
+}
+
+const styles = StyleSheet.create({
+ container: { flex: 1, justifyContent: 'center', alignItems: 'center' },
+});
+```
+
+
+
+
+
+```js name="Different status bar" snack
+import * as React from 'react';
+import { View, Text, StatusBar, StyleSheet } from 'react-native';
+import { NavigationContainer, useNavigation } from '@react-navigation/native';
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+import { Button } from '@react-navigation/elements';
+import {
+ SafeAreaProvider,
+ useSafeAreaInsets,
+} from 'react-native-safe-area-context';
+
+function Screen1() {
+ const navigation = useNavigation();
+ const insets = useSafeAreaInsets();
+
+ return (
+
+ // highlight-start
+
+ // highlight-end
+ Light Screen
+
+
+ );
+}
+
+function Screen2() {
+ const navigation = useNavigation();
+ const insets = useSafeAreaInsets();
+
+ return (
+
+ // highlight-start
+
+ // highlight-end
+ Dark Screen
+
+
+ );
+}
+
+const Stack = createNativeStackNavigator();
+
+export default function App() {
+ return (
+
+
+
+
+
+
+
+
+ );
+}
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ justifyContent: 'center',
+ alignItems: 'center',
+ },
+});
+```
+
+
+
+
+
+
+
+
+## Tabs and Drawer
+
+If you're using a tab or drawer navigator, it's a bit more complex because all of the screens in the navigator might be rendered at once and kept rendered - that means that the last `StatusBar` config you set will be used (likely on the final tab of your tab navigator, not what the user is seeing).
+
+To fix this, we'll have to do make the status bar component aware of screen focus and render it only when the screen is focused. We can achieve this by using the [`useIsFocused` hook](use-is-focused.md) and creating a wrapper component:
+
+```js
+import * as React from 'react';
+import { StatusBar } from 'react-native';
+import { useIsFocused } from '@react-navigation/native';
+
+function FocusAwareStatusBar(props) {
+ const isFocused = useIsFocused();
+
+ return isFocused ? : null;
+}
+```
+
+Now, our screens (both `Screen1.js` and `Screen2.js`) will use the `FocusAwareStatusBar` component instead of the `StatusBar` component from React Native:
+
+
+
+
+```js name="Different status bar based on tabs" snack
+import * as React from 'react';
+import { View, Text, StatusBar, StyleSheet } from 'react-native';
+import { useIsFocused } from '@react-navigation/native';
+import {
+ createStaticNavigation,
+ useNavigation,
+} from '@react-navigation/native';
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+import { Button } from '@react-navigation/elements';
+import { useSafeAreaInsets } from 'react-native-safe-area-context';
+
+function FocusAwareStatusBar(props) {
+ const isFocused = useIsFocused();
+
+ return isFocused ? : null;
+}
+
+// codeblock-focus-start
+function Screen1() {
+ const navigation = useNavigation();
+ const insets = useSafeAreaInsets();
+
+ return (
+
+
+ Light Screen
+
+
+ );
+}
+
+function Screen2() {
+ const navigation = useNavigation();
+ const insets = useSafeAreaInsets();
+
+ return (
+
+
+ Dark Screen
+
+
+ );
+}
+// codeblock-focus-end
+
+const RootStack = createNativeStackNavigator({
+ screenOptions: {
+ headerShown: false,
+ },
+ screens: {
+ Screen1: Screen1,
+ Screen2: Screen2,
+ },
+});
+
+const Navigation = createStaticNavigation(RootStack);
+
+export default function App() {
+ return ;
+}
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ justifyContent: 'center',
+ alignItems: 'center',
+ },
+});
+```
+
+
+
+
+```js name="Different status bar based on tabs" snack
+import * as React from 'react';
+import { View, Text, StatusBar, StyleSheet } from 'react-native';
+import { useIsFocused } from '@react-navigation/native';
+import { NavigationContainer, useNavigation } from '@react-navigation/native';
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+import { Button } from '@react-navigation/elements';
+import {
+ SafeAreaProvider,
+ useSafeAreaInsets,
+} from 'react-native-safe-area-context';
+
+function FocusAwareStatusBar(props) {
+ const isFocused = useIsFocused();
+
+ return isFocused ? : null;
+}
+
+// codeblock-focus-start
+function Screen1() {
+ const navigation = useNavigation();
+ const insets = useSafeAreaInsets();
+
+ return (
+
+
+ Light Screen
+
+
+ );
+}
+
+function Screen2() {
+ const navigation = useNavigation();
+ const insets = useSafeAreaInsets();
+
+ return (
+
+
+ Dark Screen
+
+
+ );
+}
+// codeblock-focus-end
+
+const Stack = createNativeStackNavigator();
+
+export default function App() {
+ return (
+
+
+
+
+
+
+
+
+ );
+}
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ justifyContent: 'center',
+ alignItems: 'center',
+ },
+});
+```
+
+
+
+
+Although not necessary, you can use the `FocusAwareStatusBar` component in the screens of the native stack navigator as well.
+
+