Skip to content

Commit df47938

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

6 files changed

Lines changed: 543 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: 266 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,266 @@
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+
:::note
155+
156+
For stack navigators, `state.routes` array contains the history of visited screens until `state.index`, and the route objects after `state.index` represent [preloaded](navigation-actions.md#preload) routes.
157+
158+
:::
159+
160+
## React Navigation entry point
161+
162+
The React Navigation entry point should wrap the standard navigator with `createStandardNavigationFactories` from `@react-navigation/native`:
163+
164+
```tsx title="src/react-navigation.tsx"
165+
import {
166+
createStandardNavigationFactories,
167+
type NavigationProp,
168+
type ParamListBase,
169+
type RouteProp,
170+
type StandardNavigationTypeBagBase,
171+
type TabActionHelpers,
172+
type TabNavigationState,
173+
TabRouter,
174+
type TabRouterOptions,
175+
} from '@react-navigation/native';
176+
177+
import {
178+
MyTabNavigator,
179+
type MyTabEventMap,
180+
type MyTabNavigatorProps,
181+
type MyTabOptions,
182+
} from './MyTabNavigator';
183+
184+
export type MyTabNavigationProp<
185+
ParamList extends ParamListBase,
186+
RouteName extends keyof ParamList = keyof ParamList,
187+
NavigatorID extends string | undefined = undefined,
188+
> = NavigationProp<
189+
ParamList,
190+
RouteName,
191+
NavigatorID,
192+
TabNavigationState<ParamList>,
193+
MyTabOptions,
194+
MyTabEventMap
195+
> &
196+
TabActionHelpers<ParamList>;
197+
198+
export type MyTabScreenProps<
199+
ParamList extends ParamListBase,
200+
RouteName extends keyof ParamList = keyof ParamList,
201+
NavigatorID extends string | undefined = undefined,
202+
> = {
203+
navigation: MyTabNavigationProp<ParamList, RouteName, NavigatorID>;
204+
route: RouteProp<ParamList, RouteName>;
205+
};
206+
207+
export interface MyTabTypeBag extends StandardNavigationTypeBagBase {
208+
State: TabNavigationState<this['ParamList']>;
209+
ActionHelpers: TabActionHelpers<this['ParamList']>;
210+
ScreenOptions: MyTabOptions;
211+
EventMap: MyTabEventMap;
212+
RouterOptions: TabRouterOptions;
213+
}
214+
215+
export const {
216+
createNavigator: createMyTabNavigator,
217+
createScreen: createMyTabScreen,
218+
} = createStandardNavigationFactories<MyTabTypeBag, MyTabNavigatorProps>(
219+
MyTabNavigator,
220+
TabRouter
221+
);
222+
```
223+
224+
The `createStandardNavigationFactories` function accepts two generic arguments:
225+
226+
- The type bag for the navigator (e.g. `MyTabTypeBag`), which includes the state, action helpers, screen options, event map, and router options types.
227+
- The type of any additional props accepted by the navigator (e.g. `MyTabNavigatorProps`).
228+
229+
It accepts 3 arguments:
230+
231+
- The standard navigator component.
232+
- The router factory function from React Navigation (e.g. `TabRouter`, `StackRouter`, etc.).
233+
- An optional function to map `{ navigation, state }` to custom props for the navigator component, in case you need any specific state or action helpers not available in the standard ones.
234+
235+
It returns an object with `createNavigator` and `createScreen` functions that can be used to create the navigator and screens for React Navigation. These should be exported from the entry point.
236+
237+
Additionally, you can export custom navigation prop and screen prop types (e.g. `MyTabNavigationProp` and `MyTabScreenProps`) that can be used by consumers for type annotations.
238+
239+
Consumers can then use the React Navigation entry point:
240+
241+
```tsx static2dynamic
242+
import { createStaticNavigation } from '@react-navigation/native';
243+
import {
244+
createMyTabNavigator,
245+
createMyTabScreen,
246+
} from 'my-navigator/react-navigation';
247+
248+
const MyTabs = createMyTabNavigator({
249+
screens: {
250+
Home: createMyTabScreen({
251+
screen: HomeScreen,
252+
options: { title: 'Home' },
253+
}),
254+
Feed: createMyTabScreen({
255+
screen: FeedScreen,
256+
options: { title: 'Feed' },
257+
}),
258+
},
259+
});
260+
261+
const Navigation = createStaticNavigation(MyTabs);
262+
```
263+
264+
## Expo Router entry point
265+
266+
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)