Introduction
What @apexnative/navigation is, how it differs from a router in the React tree, and which apps it fits.
@apexnative/navigation gives a React Native app the navigation of the platform itself: the stacks, tabs, sheets and gestures of iOS and Android. You describe the screens and how they are arranged in TypeScript; the native side shows them and reports back.
import { createNavigation } from '@apexnative/navigation';
// your own components
import { Details, Home } from './src/screens';
export const Navigation = createNavigation({
screens: { Home, Details },
root: 'Home',
});
Home opens in a native stack. When Home calls navigation.push('Details'), Details slides in with the system transition, and the swipe back iOS or the back button Android returns to Home. The Quick start builds this app step by step.
The package is a pre-release: the banner at the top of each page says which one and how to install it, and the API can still change before 1.0.
Three ways to build navigation
Navigation for React Native is built in one of three ways. They answer two questions differently: who owns the navigation state, and how your code speaks to it.
All three can show native screens. The difference is not in how a screen is drawn, but in where the state lives, who decides what the user sees, and what your code has to say to change it.
Stack, Tabs, SideMenu. A screen sends commands without ids.willMount, the app's context as services, navigation outside React.The platform owns the navigation. React describes it.
Describe, don't command
When the platform owns the navigation, the app still has to speak to it. Commands with string ids and JSON layouts work, but every mistake in them shows up at run time: a wrong name, a missing prop, an id of a screen that is gone. The library keeps the native model and gives it a React API, so the same mistakes show up in the editor.
navigator.push(screenId, {
name: 'Details',
props: { id: 42 },
});
const navigation = useNavigation();
navigation.push('Details', { props: { id: '42' } });
// 'Detail' or a missing id: a TypeScript error
The platform already knows how to navigate
iOS and iPadOS have UINavigationController, UITabBarController, sheets with detents and UISplitViewController. Android has its back stack, the back button and the Material components. Each of them holds years of decisions about gestures, accessibility, layout on every screen size, memory and multitasking.
A router that keeps its own model of the navigation has to mirror all of that into its state, and keep up with every release of the OS. The platform already does it. So the library gives React access to these containers instead of replacing them, and a new tab bar or toolbar of the OS arrives with its beta, not a year later.
The native look is a consequence, not the goal. The goal is native behavior: the same gestures, transitions, accessibility and memory use as in an app written in Swift or Kotlin.
Principles
Seven rules that every part of the API follows. When two of them pull apart, the order decides.
The platform leads
Native is the source of truth
App lifecycle is first-class
New OS features, fast
Customization without losing native behavior
Performance is a requirement
Rendering and logic apart
navigation works outside React, willMount prepares the app, and the app's context is a set of services, not a store.How it works
The library has three layers. Your app describes the screens and the layout. A thin layer in JavaScript, the engine, turns that into commands. The platform does the navigation and sends events back.
defineScreencreateNavigationuseNavigationuseScreenAppearpushshowModalswitchToscreenDidAppearstateChangedUINavigationControllerUITabBarControllerUISplitViewControllersheetsActivityBottomNavigationViewMaterialToolbarback pressThe life of one push, from the tap to the next render:
- 1A screen of your app asks for the next one.
navigation.push('Details') - 2The engine turns it into a layout and sends it to the native side.
- 3The platform creates the container and the React root of the screen.
- 4The transition runs natively, with the system's gestures.
- 5Events come back, and hooks render again.
screenDidAppear · stateChanged
What it means for your code
Four habits follow from the model. Each of them comes from the same fact: the platform, not React, holds the screens.
- Read the state, don't keep a copy. The platform owns the stack, so a copy in React state goes stale after the first swipe back. Read it with
useNavigationState, and listen to events instead of guessing. - One React root per screen, by default. Each screen renders on its own, so a provider around one screen doesn't reach the others. Providers for the whole app or for every screen go to
createNavigation; withappProvider, all screens share one React tree. More: One tree. - No safe area code. Every screen keeps its top and sides clear of the status bar, the cutout and the bars by itself (the bottom is free: a list adds
useSafeAreaInsets().bottom), withoutSafeAreaViewor a safe area library. The native side measures each screen, so the insets are right under a top bar, in a sheet and in a tab. More: The safe area. - Native screens are screens like the others. A SwiftUI, UIKit or Compose screen is pushed, presented and put in tabs the same way as a React screen. More: Native screens.
When it fits
- Apps that should look and behave like the platform, on iPhone, iPad and Android.
- Apps with native screens, or a team that also writes Swift or Kotlin.
- Apps where startup, transitions and memory matter.
- A web version of the same app: the web is not a target.
- One identical, fully custom UI on every platform, drawn in JavaScript.
How these docs are built
Each section of the sidebar is one part of the API, and goes the same way:
- Writtenthe page and its API reference
- API reviewednames, types, defaults
- CheckediOS, iPadOS and Android, in the Showcase
- Stablethe API stays as documented
The dot next to a section shows where it is.
| Dot | Status |
|---|---|
| Stable: checked on every platform | |
| In review: can still change | |
| Draft |
Where to go next
- To try it: Installation adds the navigation to a new app or the app you have, and the Quick start builds two screens in a stack.
- To learn the model: Starting the app, then Screens and props.
- Coming from another library: From React Navigation, From Expo Router and From react-native-navigation map what you use now to this API.