Skip to content

Commit 695aed0

Browse files
committed
Document usage with standard-navigation
1 parent 363eb6b commit 695aed0

6 files changed

Lines changed: 507 additions & 2 deletions

File tree

versioned_docs/version-7.x/custom-navigators.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@ React Navigation provides a few built-in navigators, but they might not always f
1010

1111
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](deep-linking.md), [state persistence](state-persistence.md), and other features that built-in navigators support.
1212

13+
:::tip
14+
15+
If you're publishing a navigator library, see [Standard navigator](standard-navigator.md) to build a navigator that can integrate with multiple navigation libraries such as React Navigation and Expo Router.
16+
17+
:::
18+
1319
## Overview
1420

1521
Under the hood, navigators are plain React components that use the [`useNavigationBuilder`](#usenavigationbuilder) hook.
Lines changed: 245 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,245 @@
1+
---
2+
id: standard-navigator
3+
title: Standard navigator
4+
sidebar_label: Standard navigator
5+
---
6+
7+
The [`standard-navigation`](https://github.com/react-navigation/standard-navigation/) package provides a standard API for writing navigators that can work with multiple navigation libraries, such as React Navigation and Expo Router.
8+
9+
This is primarily useful for library authors. If you don't plan to publish the library and are building a navigator for use with your existing React Navigation setup, see [Custom navigators](custom-navigators.md) instead.
10+
11+
## Project structure
12+
13+
Install `standard-navigation` as a regular dependency in your navigator library:
14+
15+
```bash npm2yarn
16+
npm install standard-navigation
17+
```
18+
19+
Keep the standard navigator implementation independent from any specific navigation library. Then expose separate entry points for each navigation library:
20+
21+
```text
22+
my-navigator/
23+
package.json
24+
src/
25+
MyTabNavigator.tsx
26+
react-navigation.tsx
27+
expo-router.tsx
28+
```
29+
30+
Your package exports can point to those entry points:
31+
32+
```json
33+
{
34+
"exports": {
35+
".": {
36+
"types": "./lib/typescript/index.d.ts",
37+
"default": "./lib/module/index.js"
38+
},
39+
"./react-navigation": {
40+
"types": "./lib/typescript/react-navigation.d.ts",
41+
"default": "./lib/module/react-navigation.js"
42+
},
43+
"./expo-router": {
44+
"types": "./lib/typescript/expo-router.d.ts",
45+
"default": "./lib/module/expo-router.js"
46+
}
47+
},
48+
"dependencies": {
49+
"standard-navigation": "^0.0.5"
50+
}
51+
}
52+
```
53+
54+
## Standard navigator implementation
55+
56+
The standard navigator file should export the navigator object created with `createStandardNavigator`. This file shouldn't import React Navigation or Expo Router APIs.
57+
58+
To create a standard navigator, use the `createStandardNavigator` function from `standard-navigation`, and pass it a component that renders the desired UI.
59+
60+
Example:
61+
62+
```tsx title="src/MyTabNavigator.tsx"
63+
import * as React from 'react';
64+
import {
65+
Text,
66+
Pressable,
67+
type StyleProp,
68+
StyleSheet,
69+
View,
70+
type ViewStyle,
71+
} from 'react-native';
72+
import { createStandardNavigator } from 'standard-navigation';
73+
74+
export type MyTabOptions = {
75+
title?: string;
76+
};
77+
78+
export type MyTabEventMap = {
79+
tabPress: {
80+
data: { isAlreadyFocused: boolean };
81+
canPreventDefault: true;
82+
};
83+
};
84+
85+
export type MyTabNavigatorProps = {
86+
tabBarStyle?: StyleProp<ViewStyle>;
87+
contentStyle?: StyleProp<ViewStyle>;
88+
};
89+
90+
export const MyTabNavigator = createStandardNavigator<
91+
MyTabOptions,
92+
MyTabEventMap,
93+
MyTabNavigatorProps
94+
>(({ state, descriptors, actions, emitter, tabBarStyle, contentStyle }) => {
95+
return (
96+
<View style={{ flex: 1 }}>
97+
<View style={[{ flexDirection: 'row' }, tabBarStyle]}>
98+
{state.routes.map((route, index) => (
99+
<Pressable
100+
key={route.key}
101+
onPress={() => {
102+
const isFocused = state.index === index;
103+
const event = emitter.emit({
104+
type: 'tabPress',
105+
target: route.key,
106+
canPreventDefault: true,
107+
data: { isAlreadyFocused: isFocused },
108+
});
109+
110+
if (!isFocused && !event.defaultPrevented) {
111+
actions.navigate(route.name, route.params);
112+
}
113+
}}
114+
style={{ flex: 1 }}
115+
>
116+
<Text>{descriptors[route.key].options.title ?? route.name}</Text>
117+
</Pressable>
118+
))}
119+
</View>
120+
<View style={[{ flex: 1 }, contentStyle]}>
121+
{state.routes.map((route, i) => {
122+
return (
123+
<View
124+
key={route.key}
125+
style={[
126+
StyleSheet.absoluteFill,
127+
{ display: i === state.index ? 'flex' : 'none' },
128+
]}
129+
>
130+
{descriptors[route.key].render()}
131+
</View>
132+
);
133+
})}
134+
</View>
135+
</View>
136+
);
137+
});
138+
```
139+
140+
The `createStandardNavigator` function accepts three generic arguments:
141+
142+
- `MyTabOptions` - The type of the options available for each screen.
143+
- `MyTabEventMap` - The type of the events that can be emitted by the navigator.
144+
- `MyTabNavigatorProps` - The type of any additional props accepted by the navigator.
145+
146+
The callback receives `state`, `descriptors`, `actions`, and `emitter` from the navigation library integration:
147+
148+
- `state.routes` contains `{ key, name, params, href }` objects.
149+
- `descriptors[route.key].options` contains the screen options.
150+
- `descriptors[route.key].render()` renders the screen.
151+
- `actions.navigate(name, params)` and `actions.back()` perform navigation.
152+
- `emitter.emit(...)` emits navigator events to screen listeners.
153+
154+
## React Navigation entry point
155+
156+
The React Navigation entry point should wrap the standard navigator with `createStandardNavigationFactories` from `@react-navigation/native`.
157+
158+
```tsx title="src/react-navigation.tsx"
159+
import {
160+
createStandardNavigationFactories,
161+
type NavigationProp,
162+
type ParamListBase,
163+
type RouteProp,
164+
type StandardNavigationTypeBagBase,
165+
type TabActionHelpers,
166+
type TabNavigationState,
167+
TabRouter,
168+
type TabRouterOptions,
169+
} from '@react-navigation/native';
170+
171+
import {
172+
MyTabNavigator,
173+
type MyTabEventMap,
174+
type MyTabNavigatorProps,
175+
type MyTabOptions,
176+
} from './MyTabNavigator';
177+
178+
export type MyTabNavigationProp<
179+
ParamList extends ParamListBase,
180+
RouteName extends keyof ParamList = keyof ParamList,
181+
NavigatorID extends string | undefined = undefined,
182+
> = NavigationProp<
183+
ParamList,
184+
RouteName,
185+
NavigatorID,
186+
TabNavigationState<ParamList>,
187+
MyTabOptions,
188+
MyTabEventMap
189+
> &
190+
TabActionHelpers<ParamList>;
191+
192+
export type MyTabScreenProps<
193+
ParamList extends ParamListBase,
194+
RouteName extends keyof ParamList = keyof ParamList,
195+
NavigatorID extends string | undefined = undefined,
196+
> = {
197+
navigation: MyTabNavigationProp<ParamList, RouteName, NavigatorID>;
198+
route: RouteProp<ParamList, RouteName>;
199+
};
200+
201+
export interface MyTabTypeBag extends StandardNavigationTypeBagBase {
202+
State: TabNavigationState<this['ParamList']>;
203+
ActionHelpers: TabActionHelpers<this['ParamList']>;
204+
ScreenOptions: MyTabOptions;
205+
EventMap: MyTabEventMap;
206+
RouterOptions: TabRouterOptions;
207+
}
208+
209+
export const {
210+
createNavigator: createMyTabNavigator,
211+
createScreen: createMyTabScreen,
212+
} = createStandardNavigationFactories<MyTabTypeBag, MyTabNavigatorProps>(
213+
MyTabNavigator,
214+
TabRouter
215+
);
216+
```
217+
218+
Consumers can then use the React Navigation entry point:
219+
220+
```tsx static2dynamic
221+
import { createStaticNavigation } from '@react-navigation/native';
222+
import {
223+
createMyTabNavigator,
224+
createMyTabScreen,
225+
} from 'my-navigator/react-navigation';
226+
227+
const MyTabs = createMyTabNavigator({
228+
screens: {
229+
Home: createMyTabScreen({
230+
screen: HomeScreen,
231+
options: { title: 'Home' },
232+
}),
233+
Feed: createMyTabScreen({
234+
screen: FeedScreen,
235+
options: { title: 'Feed' },
236+
}),
237+
},
238+
});
239+
240+
const Navigation = createStaticNavigation(MyTabs);
241+
```
242+
243+
## Expo Router entry point
244+
245+
Work in progress.

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@ React Navigation provides a few built-in navigators, but they might not always f
1010

1111
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](deep-linking.md), [state persistence](state-persistence.md), and other features that built-in navigators support.
1212

13+
:::tip
14+
15+
If you're publishing a navigator library, see [Standard navigator](standard-navigator.md) to build a navigator that can integrate with multiple navigation libraries such as React Navigation and Expo Router.
16+
17+
:::
18+
1319
## Overview
1420

1521
Under the hood, navigators are plain React components that use the [`useNavigationBuilder`](#usenavigationbuilder) hook.

0 commit comments

Comments
 (0)