mrkeyoor.com_
Sat 08 Aug 20:58 UTC
npmMobileupdated 08 Aug 2026

@react-navigation/native

@react-navigation/native is the runtime foundation of React Navigation for React Native and web. It owns the top-level navigation container, state, linking, themes, focus hooks, refs, route-path conversion, back-button integration, and TypeScript helpers. It does not render a stack, tabs, or drawer by itself. You add a navigator package such as @react-navigation/native-stack or @react-navigation/bottom-tabs, then place that navigator under the container or use version 7's static configuration API.

Verdict

The default choice for custom React Native navigation, with excellent composition and a real cost in companion packages and platform configuration. Expo projects that prefer filesystem routes should start with Expo Router instead of assembling this layer directly.

API stability4/5NavigationContainer, route and navigation props, nested navigators, hooks, actions, themes, and linking have a mature shape, while version 7 adds a static API without removing the dynamic API. Major upgrades still require work, and the repository now develops version 8 on main while 7.x remains the stable branch, so teams should pin documentation and packages to the same major.
Docs5/5The official site has versioned, task-based guides for installation, static and dynamic configuration, every navigator, TypeScript, params, linking, persistence, themes, navigation events, authentication flows, testing, web, and troubleshooting. It also calls out native package installation and rebuild requirements. The main risk is arriving at next-version material instead of the stable-major page.
Maintenance5/5npm published 7.3.16 in August 2026, the repository was pushed the same day, stable 7.x has its own branch, and version 8 development is visible rather than hidden. The monorepo is large and its issue count includes many packages and pull requests, but release activity, platform adaptation, and explicit branch maintenance show a highly active project.
Ecosystem5/5The package anchors stacks, native stacks, bottom and top tabs, drawers, elements, devtools, routers, React Native Web support, Expo Router, and many community integrations. Millions of weekly downloads and broad tutorial coverage make answers easy to find. That breadth also means a navigation setup often spans several packages whose versions and native prerequisites must stay compatible.

Use it if

  • You need composable stack, tab, drawer, and modal navigation in a React Native application
  • You want URL and app-link mapping, navigation-state persistence, themes, and focus lifecycle from one ecosystem
  • You need a dynamic component API or version 7's static configuration with inferred TypeScript route parameters
  • Your application targets Expo, bare React Native, or React Native Web and can install platform dependencies
Skip it if

Setup reality

Installing @react-navigation/native is the first step, not the whole setup. Version 7.3.16 peers on React 18.2 or newer and React Native, and its package dependencies provide the core state machinery. Most actual navigators also require react-native-screens and react-native-safe-area-context. In Expo, use npx expo install for those native packages so versions match the Expo SDK. In a bare React Native app, install them with your package manager and run npx pod-install ios on macOS; Android and older React Native versions may need the platform setup documented by each dependency. Then install at least one navigator, such as @react-navigation/native-stack, bottom-tabs, drawer, stack, or material-top-tabs. Some of those add react-native-gesture-handler or react-native-reanimated and their own Babel or native requirements. Dynamic configuration wraps the root navigator in one NavigationContainer. Static configuration instead builds a navigator object and passes it through createStaticNavigation, which supplies its own container. Do not nest ordinary containers to isolate feature flows; navigator nesting and screen groups are usually the right model. Route params should contain minimal serializable identifiers, not entire domain objects, because navigation state may be persisted or encoded into URLs. Deep linking is not just a linking object. Custom schemes, Universal Links, Android App Links, domain association files, and a rebuilt binary all need separate platform work, and deferred deep linking after installation needs another service or custom infrastructure. If you provide initialState, the container source says initial deep-link state will not be handled, so persistence code must skip restoration when a URL launched the app and should usually skip custom state on web. Navigation refs are an escape hatch for code outside components; they are not ready immediately, do not replace screen hooks, and can make linking and test flows harder. Focus is a navigation lifecycle rather than React mount lifecycle, so subscriptions and data refreshes belong in useFocusEffect with memoized callbacks and cleanup. Back interception and unsaved-form protection must redispatch the blocked action after confirmation. Finally, verify examples against the version 7 docs or 7.x branch because the repository's default branch now describes the upcoming major version 8.

Patterns

Create a dynamic native stackcreate-native-stack

import { NavigationContainer } from '@react-navigation/native'
import { createNativeStackNavigator } from '@react-navigation/native-stack'

const Stack = createNativeStackNavigator()

export default function App() {
  return (
    <NavigationContainer>
      <Stack.Navigator>
        <Stack.Screen name="Home" component={HomeScreen} />
        <Stack.Screen name="Details" component={DetailsScreen} />
      </Stack.Navigator>
    </NavigationContainer>
  )
}

Install @react-navigation/native-stack separately; the native package does not render a stack.

Build a version 7 static navigatoruse-static-navigation

import { createStaticNavigation } from '@react-navigation/native'
import { createNativeStackNavigator } from '@react-navigation/native-stack'

const RootStack = createNativeStackNavigator({
  screens: {
    Home: HomeScreen,
    Details: DetailsScreen,
  },
})

const Navigation = createStaticNavigation(RootStack)
export default function App() {
  return <Navigation />
}

createStaticNavigation supplies the root container; do not wrap Navigation in another NavigationContainer.

Navigate with serializable route paramsnavigate-with-params

function HomeScreen({ navigation }) {
  return (
    <Button
      title="Open profile"
      onPress={() => navigation.navigate('Profile', { userId: 'jane' })}
    />
  )
}

function ProfileScreen({ route }) {
  return <Text>User: {route.params.userId}</Text>
}

Pass identifiers and small serializable values, then load domain data in the destination screen.

Map app links to screensconfigure-deep-links

const linking = {
  prefixes: ['myapp://', 'https://app.example.com'],
  config: {
    screens: {
      Home: '',
      Profile: {
        path: 'users/:userId',
      },
      NotFound: '*',
    },
  },
}

<NavigationContainer linking={linking} fallback={<Loading />}>
  <RootNavigator />
</NavigationContainer>

The JavaScript mapping is only one layer; configure iOS and Android associations or schemes and rebuild the installed app.

Extend the navigation themeset-theme

import { DarkTheme, NavigationContainer } from '@react-navigation/native'

const theme = {
  ...DarkTheme,
  colors: {
    ...DarkTheme.colors,
    primary: '#7dd3fc',
    background: '#0f172a',
    card: '#111827',
  },
}

<NavigationContainer theme={theme}>
  <RootNavigator />
</NavigationContainer>

Extend every colors object rather than replacing it, or required theme colors disappear.

Refresh data when a screen is focusedrun-on-focus

import * as React from 'react'
import { useFocusEffect } from '@react-navigation/native'

function OrdersScreen() {
  useFocusEffect(
    React.useCallback(() => {
      const controller = new AbortController()
      loadOrders({ signal: controller.signal })
      return () => controller.abort()
    }, []),
  )
  return <OrdersList />
}

Wrap the callback in useCallback; otherwise a new function can rerun the effect on every focused render.

Confirm before discarding unsaved changesprevent-leaving

import { Alert } from 'react-native'
import { usePreventRemove } from '@react-navigation/native'

usePreventRemove(hasUnsavedChanges, ({ data }) => {
  Alert.alert('Discard changes?', 'Your edits will be lost.', [
    { text: 'Stay', style: 'cancel' },
    {
      text: 'Discard',
      style: 'destructive',
      onPress: () => navigation.dispatch(data.action),
    },
  ])
})

Redispatch the captured action only after confirmation; calling goBack can lose the original remove action's intent.

Use a guarded root navigation refnavigate-outside-component

import { createNavigationContainerRef } from '@react-navigation/native'

export const navigationRef = createNavigationContainerRef()

export function openInbox() {
  if (navigationRef.isReady()) {
    navigationRef.navigate('Inbox')
  }
}

<NavigationContainer ref={navigationRef}>
  <RootNavigator />
</NavigationContainer>

Prefer useNavigation inside screens; refs are for exceptional code paths and must be checked for readiness.

Infer route params from static configurationtype-static-routes

import type { StaticParamList } from '@react-navigation/native'

type RootStackParamList = StaticParamList<typeof RootStack>

declare global {
  namespace ReactNavigation {
    interface RootParamList extends RootStackParamList {}
  }
}

The static API can infer route names and params; dynamic configuration usually needs an explicit param-list type.

Persist container state on native platformspersist-navigation-state

const [initialState, setInitialState] = React.useState()
const [ready, setReady] = React.useState(false)

React.useEffect(() => {
  AsyncStorage.getItem('NAVIGATION_STATE').then((value) => {
    if (value) setInitialState(JSON.parse(value))
    setReady(true)
  })
}, [])

if (!ready) return <Loading />
return (
  <NavigationContainer
    initialState={initialState}
    onStateChange={(state) =>
      AsyncStorage.setItem('NAVIGATION_STATE', JSON.stringify(state))
    }
  >
    <RootNavigator />
  </NavigationContainer>
)

Do not restore custom initialState when an incoming deep link should define startup state, and avoid this pattern on web where the URL is the state source.

Alternatives

PackageRegistryPick it when
expo-routernpmYou use Expo and want file-based routes, layouts, URL-first navigation, and React Navigation underneath
react-native-navigationnpmYou prefer Wix's native-navigation architecture and accept heavier platform-specific setup
solitonpmYou share screens between React Native and Next.js and need an adapter across native and web routing