new·The score now tells you which way it movedA brain's exam only ever grows: its own material writes questions, and so does every question a real caller asked and did not get answered. The score is a percentage over that growing set, so a brain that learned more could post a smaller number — and this week three did. One of them answered two MORE questions than the week before and showed eighteen points less. Printed as a single percentage, that reads as decline to a reader and as punishment to anyone who contributes material.all news →
mozg.beta
Sign in

Expo · Router · all subjects

custom-navigators

22 notes, read out of this brain and free to use. Each one was extracted from a source and is re-checked against its exam.

createProps custom props typing

Declare props returned by createProps in the third type argument to NavigatorContentProps so NavigatorContent receives them typed. createProps receives raw Expo Router state and dispatch which are internal and may have breaking changes, so prefer using standard state, actions, and emitter when possible.

Standard navigator API for library authors

NavigatorContent implements the standard navigator contract defined by the standard-navigation package. The state, descriptors, actions, and emitter are framework-agnostic and the same whether created by unstable_createStandardRouterNavigator or by calling createStandardNavigator directly.

unstable_createStandardRouterNavigator API overview

unstable_createStandardRouterNavigator turns a content component into a navigator for use as a layout. It takes two required arguments: NavigatorContent (a component that renders the navigator's UI, receiving state, descriptors, actions, and emitter) and router (StackRouter or TabRouter imported from expo-router). The returned navigator has a .Screen child for declaring screens, usable in _layout files like built-in layouts.

NavigatorContent props table

NavigatorContent receives the following properties: (1) state — the current navigation state with index and routes, each route having key, name, params, and href; (2) descriptors — a map keyed by route.key with each descriptor exposing the screen's resolved options and a render() function; (3) actions — functions to change navigation state: navigate(name, params?) and back(); (4) emitter — an object with emit() method for sending events to screens.

Typed events in NavigatorContent

Declare custom events in the second type argument to NavigatorContentProps. Each key is an event name with a value describing the event's data and canPreventDefault boolean. emitter.emit is then typed against that map, rejecting unknown event names and mismatched payloads. unstable_createStandardRouterNavigator infers the event map from the component, so you do not pass it at the call site.

Custom navigator options parameter

Both unstable_createStandardRouterNavigator and unstable_integrateWithRouter accept an optional options object as third argument with two fields: (1) useOnlyUserDefinedScreens (boolean, default false) — when true, only screens declared with <Navigator.Screen> are rendered and filesystem routes are ignored; (2) createProps (function) — derives extra props for NavigatorContent from router state, useful for router-specific information not in standard state and actions.

createStandardNavigator for reusable navigators

Library authors should call createStandardNavigator (from standard-navigation package) directly to create a framework-agnostic navigator. It takes two type arguments: the per-screen options type and the event map type. The resulting navigator depends only on the standard contract and runs on Expo Router, React Navigation, or any other framework implementing it.

unstable_integrateWithRouter for Expo Router integration

Wire a standard navigator into Expo Router using unstable_integrateWithRouter, passing the navigator and a router (StackRouter or TabRouter). The returned component works like unstable_createStandardRouterNavigator output, including the .Screen child and same options parameter.

Library entry points structure for multi-framework navigators

Keep navigator content and standard navigator framework-agnostic in one place, then expose one entry point per framework: TabsContent.tsx (navigator UI implementing standard navigator API), index.ts (root framework-agnostic export), react-navigation.ts (React Navigation integration), expo-router.ts (Expo Router integration). Map each to subpath exports in package.json.

Package.json subpath exports for framework-specific integrations

Map framework-specific entry points using Node.js subpath exports in package.json. Example structure: '.' points to root (framework-agnostic), './react-navigation' points to React Navigation build output, './expo-router' points to Expo Router build output. Each subpath should have types and default fields pointing to .d.ts and .js build files respectively.

Minimal tab navigator example

Example creating a custom tab navigator with unstable_createStandardRouterNavigator: ```tsx import { unstable_createStandardRouterNavigator, TabRouter, type NavigatorContentProps, } from 'expo-router'; import { Pressable, Text, View } from 'react-native'; type TabsContentProps = NavigatorContentProps<{ title?: string }>; function TabsContent({ state, descriptors, actions }: TabsContentProps) { const focusedRoute = state.routes[state.index]; return ( <View style={{ flex: 1 }}> <View style={{ flex: 1 }}>{descriptors[focusedRoute.key].render()}</View> <View style={{ flexDirection: 'row' }}> {state.routes.map(route => ( <Pressable key={route.key} style={{ flex: 1, padding: 16 }} onPress={() => actions.navigate(route.name)}> <Text>{descriptors[route.key].options.title ?? route.name}</Text> </Pressable> ))} </View> </View> ); } export const Tabs = unstable_createStandardRouterNavigator(TabsContent, TabRouter); ```

Custom navigator in _layout file

Use a custom navigator in a _layout file like built-in layouts: ```tsx import { Tabs } from '../components/Tabs'; export default function Layout() { return ( <Tabs> <Tabs.Screen name="index" options={{ title: 'Home' }} /> <Tabs.Screen name="settings" options={{ title: 'Settings' }} /> </Tabs> ); } ```

Typed events example in NavigatorContent

Example declaring typed events in NavigatorContent: ```tsx type TabsContentProps = NavigatorContentProps< { title?: string }, { tabPress: { data: undefined; canPreventDefault: true } } >; function TabsContent({ emitter }: TabsContentProps) { emitter.emit({ type: 'tabPress', canPreventDefault: true }); // ... } ```

createProps example with custom props

Example using createProps to add custom props to NavigatorContent: ```tsx export const Tabs = unstable_createStandardRouterNavigator(TabsContent, TabRouter, { useOnlyUserDefinedScreens: true, createProps: ({ state, dispatch }) => ({ activeRouteKey: state.routes[state.index].key, preload: (name: string) => dispatch({ type: 'PRELOAD', payload: { name } }), }), }); type TabsContentProps = NavigatorContentProps< { title?: string }, Record<string, never>, { activeRouteKey: string; preload: (name: string) => void } >; function TabsContent({ activeRouteKey, preload }: TabsContentProps) { // ... } ```

createStandardNavigator library example

Example creating a framework-agnostic navigator for library distribution: ```tsx import { createStandardNavigator } from 'standard-navigation'; import { TabsContent } from './TabsContent'; export const navigator = createStandardNavigator< { title?: string }, { tabPress: { data: undefined; canPreventDefault: true } } >(TabsContent); ```

unstable_integrateWithRouter library example

Example integrating a standard navigator with Expo Router in a library: ```tsx import { unstable_integrateWithRouter, TabRouter } from 'expo-router'; import { navigator } from './index'; export const Tabs = unstable_integrateWithRouter(navigator, TabRouter); ```

Custom navigator API status and availability

The custom navigator API described in this documentation is in alpha status and available in SDK 56 and later. The API is subject to breaking changes.

Protected routes with custom navigators

Protected is available for custom navigators using the withLayoutContext hook.

Prefetch behavior with custom navigators

Custom navigators may implement prefetching differently or not support it at all. Expo Router navigators will render the target screen off-screen to enable preloading when prefetch is set.

Port custom navigators using withLayoutContext

If your project has a custom navigator, you can port it to Expo Router by using the withLayoutContext function. Example: export const CustomNavigator = withLayoutContext(createCustomNavigator().Navigator);

Rewrite custom navigators using Navigator component and useNavigationBuilder

To rewrite a custom navigator in Expo Router, use the Navigator component, which wraps the useNavigationBuilder hook from React Navigation. The return value of useNavigationBuilder can be accessed with Navigator.useContext() from inside the <Navigator /> component. Properties are passed using props of the <Navigator /> component, including initialRouteName, screenOptions, and router. All children of <Navigator /> are rendered as-is.

Navigator.useContext and Navigator.Slot for custom navigators

Navigator.useContext allows access to the React Navigation state, navigation, descriptors, and router for the custom navigator. Navigator.Slot is a React component used to render the currently selected route and can only be rendered inside a <Navigator /> component.

Give your agent this brain