Skip to content

Commit c77b408

Browse files
committed
Document sharing screens in navigators
1 parent 0017175 commit c77b408

3 files changed

Lines changed: 230 additions & 0 deletions

File tree

versioned_docs/version-8.x/configuring-links.md

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,7 @@ When using automatic path generation with `enabled: 'auto'`, the following rules
274274
- Screen names will be converted from `PascalCase` to `kebab-case` to use as the path (e.g. `NewsFeed` -> `news-feed`).
275275
- Unless a screen has explicit empty path (`path: ''`) to use for the homepage, the first leaf screen encountered will be used as the homepage.
276276
- 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.
277+
- If the same screen is used in multiple nested navigators with the same path pattern, the path will be marked as [shared](#shared-paths) automatically. This is detected by comparing the screen component or navigator reference.
277278

278279
Let's say we have the following navigation structure:
279280

@@ -1179,6 +1180,87 @@ const config = {
11791180

11801181
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`.
11811182

1183+
## Shared paths
1184+
1185+
Sometimes the same screen is present in multiple nested navigators. For example, each tab can have its own stack, and each stack can contain a `Profile` screen. In this case, you may want `/profile/jane` to keep the current tab focused and open the `Profile` screen in that tab.
1186+
1187+
React Navigation supports this with shared paths:
1188+
1189+
<Tabs groupId="config" queryString="config">
1190+
<TabItem value="static" label="Static" default>
1191+
1192+
When using static configuration with automatic path generation (default behavior), shared paths are detected automatically when the same screen component or navigator reference appears in multiple branches with the same full path pattern:
1193+
1194+
```js
1195+
const Profile = createNativeStackScreen({
1196+
screen: ProfileScreen,
1197+
linking: 'profile/:id',
1198+
});
1199+
1200+
const FeedStack = createNativeStackNavigator({
1201+
screens: {
1202+
Feed: FeedScreen,
1203+
Profile,
1204+
},
1205+
});
1206+
1207+
const SearchStack = createNativeStackNavigator({
1208+
screens: {
1209+
Search: SearchScreen,
1210+
Profile,
1211+
},
1212+
});
1213+
```
1214+
1215+
You can also set `shared` explicitly, which can be useful if the screen component is different but you still want to share the path:
1216+
1217+
```js
1218+
const Profile = createNativeStackScreen({
1219+
screen: ProfileScreen,
1220+
linking: {
1221+
path: 'profile/:id',
1222+
shared: true,
1223+
},
1224+
});
1225+
```
1226+
1227+
</TabItem>
1228+
<TabItem value="dynamic" label="Dynamic">
1229+
1230+
When using dynamic configuration, specify `shared: true` for every screen config that should accept the same path:
1231+
1232+
```js
1233+
const config = {
1234+
screens: {
1235+
FeedTab: {
1236+
screens: {
1237+
Feed: '',
1238+
Profile: {
1239+
path: 'profile/:id',
1240+
shared: true,
1241+
},
1242+
},
1243+
},
1244+
SearchTab: {
1245+
screens: {
1246+
Search: 'search',
1247+
Profile: {
1248+
path: 'profile/:id',
1249+
shared: true,
1250+
},
1251+
},
1252+
},
1253+
},
1254+
};
1255+
```
1256+
1257+
</TabItem>
1258+
</Tabs>
1259+
1260+
When a shared path matches more than one screen, React Navigation uses the current navigation state to choose the matching branch when possible to handle deep links.
1261+
1262+
The first matching path is also the canonical path used when generating URLs. If you use [`alias`](#alias-for-paths), you need to explicitly mark the alias itself as `shared: true` when desired.
1263+
11821264
## Omitting a screen from path
11831265

11841266
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:
@@ -1506,6 +1588,7 @@ Each item in the `alias` array can be a string matching the syntax of the `path`
15061588

15071589
- `path` (required) - The path pattern to match.
15081590
- `exact` - Whether to match the path exactly. Defaults to `false`. See [Matching exact paths](#matching-exact-paths) for more details.
1591+
- `shared` - Whether this alias can resolve to multiple routes. Defaults to `false`. See [Shared paths](#shared-paths) for more details.
15091592
- `parse` - Function to parse path segments into param values. See [Passing params](#passing-params) for more details.
15101593

15111594
## Advanced cases

versioned_docs/version-8.x/nesting-navigators.md

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -591,6 +591,116 @@ function RootStack() {
591591
</TabItem>
592592
</Tabs>
593593

594+
## Sharing the same screen in multiple navigators
595+
596+
A common pattern in mobile apps is to nest stack navigators inside each tab of a bottom tab navigator. Often, there can be some screens that exist in each stack.
597+
598+
Let's say you have a `FeedStack` tab and a `SearchStack` tab, and both of them can navigate to a `Profile` screen. You can add the `Profile` screen to both stacks, so that when you navigate to it from the `Feed` screen, the `FeedStack` tab stays selected and the tab bar remains visible. Pressing back from `Profile` will take you back to the previous screen in the same tab.
599+
600+
<Tabs groupId="config" queryString="config">
601+
<TabItem value="static" label="Static" default>
602+
603+
```js
604+
// highlight-start
605+
const Profile = createNativeStackScreen({
606+
screen: ProfileScreen,
607+
linking: 'profile/:id',
608+
});
609+
// highlight-end
610+
611+
const FeedStack = createNativeStackNavigator({
612+
screens: {
613+
Feed: FeedScreen,
614+
// highlight-next-line
615+
Profile,
616+
},
617+
});
618+
619+
const SearchStack = createNativeStackNavigator({
620+
screens: {
621+
Search: SearchScreen,
622+
// highlight-next-line
623+
Profile,
624+
},
625+
});
626+
627+
const HomeTabs = createBottomTabNavigator({
628+
screens: {
629+
FeedTab: createBottomTabScreen({
630+
screen: FeedStack,
631+
options: {
632+
title: 'Feed',
633+
},
634+
}),
635+
SearchTab: createBottomTabScreen({
636+
screen: SearchStack,
637+
options: {
638+
title: 'Search',
639+
},
640+
}),
641+
},
642+
});
643+
```
644+
645+
</TabItem>
646+
<TabItem value="dynamic" label="Dynamic">
647+
648+
```js
649+
function FeedStack() {
650+
return (
651+
<Stack.Navigator>
652+
<Stack.Screen name="Feed" component={FeedScreen} />
653+
<Stack.Screen
654+
name="Profile"
655+
// highlight-next-line
656+
component={ProfileScreen}
657+
/>
658+
</Stack.Navigator>
659+
);
660+
}
661+
662+
function SearchStack() {
663+
return (
664+
<Stack.Navigator>
665+
<Stack.Screen name="Search" component={SearchScreen} />
666+
<Stack.Screen
667+
name="Profile"
668+
// highlight-next-line
669+
component={ProfileScreen}
670+
/>
671+
</Stack.Navigator>
672+
);
673+
}
674+
675+
function HomeTabs() {
676+
return (
677+
<Tab.Navigator>
678+
<Tab.Screen
679+
name="FeedTab"
680+
component={FeedStack}
681+
options={{
682+
title: 'Feed',
683+
}}
684+
/>
685+
<Tab.Screen
686+
name="SearchTab"
687+
component={SearchStack}
688+
options={{
689+
title: 'Search',
690+
}}
691+
/>
692+
</Tab.Navigator>
693+
);
694+
}
695+
```
696+
697+
</TabItem>
698+
</Tabs>
699+
700+
Each `Profile` screen belongs to the stack where it was opened. Calling `navigate('Profile', { id: 'jane' })` from inside `FeedStack` opens `Profile` in `FeedStack`, while the same call from inside `SearchStack` opens it in `SearchStack`.
701+
702+
For deep linking and browser URL integration, you can [configure the same path for both screens](configuring-links.md#shared-paths) so that the URL is the same regardless of which stack it was opened from.
703+
594704
## Best practices when nesting
595705

596706
We recommend keeping navigator nesting to a minimum. Try to achieve the behavior you want with as little nesting as possible.

versioned_docs/version-8.x/upgrading-from-7.x.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1163,6 +1163,43 @@ const RootStack = createNativeStackNavigator({
11631163
});
11641164
```
11651165

1166+
### Multiple screens can now share the same path pattern for deep linking
1167+
1168+
In React Navigation 7, each screen needed to have a unique path pattern for deep linking.
1169+
1170+
React Navigation 8, we now supported shared paths, which allows multiple screens to share the same path pattern. This is useful for cases where the same screen appears in multiple nested navigators, but you want them to use a single URL. e.g. each tab contains its own stack with a `Profile` screen, while both copies still use a single URL such as `/profile/jane`.
1171+
1172+
When using static configuration, this is detected automatically. It can also be explicitly specified with `shared: true` when using dynamic configuration, or when automatic detection won't detect this in static configuration:
1173+
1174+
```js
1175+
const config = {
1176+
screens: {
1177+
FeedTab: {
1178+
screens: {
1179+
Feed: '',
1180+
Profile: {
1181+
path: 'profile/:id',
1182+
shared: true,
1183+
},
1184+
},
1185+
},
1186+
SearchTab: {
1187+
screens: {
1188+
Search: 'search',
1189+
Profile: {
1190+
path: 'profile/:id',
1191+
shared: true,
1192+
},
1193+
},
1194+
},
1195+
},
1196+
};
1197+
```
1198+
1199+
When handling deep links that have shared paths, React Navigation will try to automatically select the correct navigator based on the current navigation state.
1200+
1201+
See [Shared paths](configuring-links.md#shared-paths) for more details.
1202+
11661203
### Navigators now accept a `router` prop
11671204

11681205
A router defines how the navigator updates its state based on navigation actions. Previously, custom routers could only be used by [creating a custom navigator](custom-navigators.md#extending-navigators).

0 commit comments

Comments
 (0)