The Navigation router: scenes instead of routes for React and React Native
Scene-Based Navigation for React and React Native
At a glance
- What is it?
- The Navigation router treats navigation as a set of flat scenes rather than a tree of routes, and it ships one API for both React and React Native with native components on mobile. The trade-off is a smaller feature set than a general purpose router, and a state model you have to learn before you write a single link.
- Who is it for?
- Adopt the Navigation router if your application is a flat set of screens and you want one router that also renders native navigation on Android and iOS. Do not adopt it if you need nested layouts, complex route matching or the wider plugin ecosystem a conventional React router offers.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository received new commits within the last day.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 24, 2026, and from our analysis. They are not legal advice.
Editorial analysis
States and scenes replace the route tree
Most React routers make you describe your application as routes. The Navigation router asks you to describe it as scenes, and it draws a line between the two that is worth understanding before you install anything.
You create one State per scene, and the router generates interim routes for you. The documentation makes the point that you do not need to define your real routes up front and can add them later without changing any code, because with scene-based navigation there are no hard-coded URLs sitting in your source that need updating.
For React the setup is a single object literal passed to StateNavigator:
import { StateNavigator } from 'navigation';
const stateNavigator = new StateNavigator([
{ key: 'hello', route: '' },
{ key: 'world' }
]);Scenes are always flat, which the documentation presents as a feature because nesting routes is where router configuration usually turns painful. The escape hatch is that a Scene can have more than one route, so nested route behaviour is still available.
The consequence for a team is real though. If your application genuinely depends on nested layouts or on matching arbitrary URL patterns, you are re-expressing that in terms of scenes, and the flat list becomes the organizing idea of the whole codebase.
SceneView renders the UI, NavigationLink switches the scene
In React, each State gets a SceneView component, and the whole tree sits inside a NavigationHandler:
<NavigationHandler stateNavigator={stateNavigator}>
<SceneView active="hello"><Hello /></SceneView>
<SceneView active="world"><World /></SceneView>
</NavigationHandler>Because all scenes are declared as siblings, the router renders whichever one matches the active state and gets out of the way, which is the design goal stated in the README: once a scene is active, you build the UI however you want.
Navigation happens through a link component rather than a route string:
import { NavigationLink } from 'navigation-react';
const Hello = () => (
<NavigationLink
stateKey="world"
navigationData={{ size: 20 }}>
Hello
</NavigationLink>
);You give it a state key and the data for the destination. The router builds the URL, and if you have configured more than one route for that state it picks the best match.
Data arrives in the destination scene through a hook:
import { useNavigationEvent } from 'navigation-react';
const World = () => {
const { data } = useNavigationEvent();
return (
<div style={{ fontSize: data.size }}>
World
</div>
);
};The documentation describes the data as strongly typed, so the size you read in the destination is a number rather than an untyped blob. In TypeScript code the navigation data is part of the contract between two scenes, which is a reasonable way to catch a mismatched payload at compile time.
The same router on React Native, rendered with native primitives
The same core library drives React Native. The install line gains one package:
`npm install navigation navigation-react navigation-react-native`
The Native setup mirrors the web one with different components. Scenes are wrapped in a NavigationStack rather than rendered flat:
<NavigationHandler stateNavigator={stateNavigator}>
<NavigationStack>
<Scene stateKey="hello"><Hello /></Scene>
<Scene stateKey="world"><World /></Scene>
</NavigationStack>
</NavigationHandler>The documentation states that these components render to the same native primitives as other native apps, and gives one concrete example: the TabBar component renders to a BottomNavigationView on Android and a UITabBarController on iOS. That is the argument for using this library on mobile rather than a web router in a wrapper.
Navigation on Native is imperative, driven through the stateNavigator obtained from the event hook:
import { useNavigationEvent } from 'navigation-react';
const Hello = () => {
const { stateNavigator } = useNavigationEvent();
return (
<Button title="Hello"
onPress={() => {
stateNavigator.navigate('world', { size: 20 });
}} />
);
};The destination reads the same data shape it would on the web, using the native Text component instead of a div.
One Native-specific option stands out. A state can be declared with trackCrumbTrail: true, which the documentation describes as dropping a breadcrumb every time the router visits that scene. The metaphor is the trail of crumbs from the fairy tale, and it exists so you can build a stack of scenes rather than a flat set.
Installing the packages and building from source
For an application, the web install is two packages:
`npm install navigation navigation-react`
The core package holds the state model and the router logic, the React package holds the components and hooks, and the Native package adds the native components.
The repository itself is a TypeScript monorepo with a gulp build. The top level holds a Navigation directory, a NavigationReact directory, a NavigationReactMobile directory, a NavigationReactNative directory, a NavigationReactNativeWeb directory, a types directory and a build directory. The package.json describes itself as the data-first JavaScript router and declares an Apache-2.0 licence.
If you have cloned the repository, the documented build steps are:
npm install
npm run buildRunning `npm test` executes the unit tests, and `npm run package` outputs the npm packages into the `build/npm` folder. The devDependencies list confirms the shape of the toolchain: rollup with a TypeScript and a sucrase plugin for bundling, gulp with gulp-mocha and gulp-typescript for the task and test layer, and jsdom for the test environment. The local packages are consumed from build/npm through file: dependencies, which is why the package step has to run before the local packages resolve.
One router for two platforms, and the cost of that choice
The pitch is that you no longer need a separate routing library for React and React Native, and the README claims this does not compromise the user experience. For teams maintaining a web app and a mobile app from one shared codebase, the appeal is obvious: one state model, one navigation data contract, one set of scene definitions.
The cost is that the library covers less ground than the general purpose React routers. The README and the documentation pages describe a specific set of primitives: states, scenes, handlers, stacks and links. There is no mention of loaders, route middleware, data fetch orchestration, error boundaries at the route level or a plugin model. If your application grows the kind of routing layer that talks to a server for each route, you will be building that yourself around a scene list.
The same flatness that removes hard-coded URLs also removes the nesting. A master and detail page, which the documentation names as the example where nested routes get hard, has to be modelled as sibling scenes with a shared parent, not as a nested route tree.
There is a second practical consideration. The published packages are versioned on separate tracks. The recent releases show NavigationReact at v4.22.0 and NavigationReactMobile at v3.22.0 on 2026-10-16, with NavigationReactNative at v9.37.0 on 2026-09-23. Three packages with three version lines means an upgrade touches more than one dependency, and you need to keep them compatible rather than upgrading one at a time.
What the docs leave open
Several practical questions are left unanswered by the documentation, and each one matters before a team commits to the router.
The first is testing. The README notes that `npm test` runs the unit tests and that BrowserStack helped with cross browser testing, but it does not describe how to write a test against a scene transition, how to mock navigation data, or how to assert on URL changes in a test. For a library whose main surface is transitions, that is a gap you will have to fill yourself.
The second is migration. There is no guide for moving an existing React Router or React Navigation setup over to scenes, and no mention of how the two systems can coexist during a gradual migration.
The third is the URL contract. The documentation says the router builds the URL and uses the best match when a state has more than one route, but it does not spell out how that match is scored, how query parameters are handled, or how the URL shape is customised beyond the route property on a state. If your application depends on specific URL formats for deep links or for a server, you need to verify that behaviour yourself.
The fourth is version support. The documentation does not state which React versions are supported, nor what the peer dependency ranges are for the published packages.
Editorial conclusion
Adopt the Navigation router if your application is a flat set of screens and you want one router that also renders native navigation on Android and iOS. Do not adopt it if you need nested layouts, complex route matching or the wider plugin ecosystem a conventional React router offers. Before committing, write down the scenes of your app and check that each maps to one state key, because the flat scene list is the constraint the library cannot work around, and read the React Native setup notes before you assume the web and mobile examples are interchangeable.
Frequently asked questions
How do I install the Navigation router for React?
Install two packages with npm install navigation navigation-react. The core package holds the state model and the router, the React package provides the components such as NavigationHandler, SceneView and NavigationLink.
What is the difference between a State and a Scene in the Navigation router?
A State is the entry you declare for each scene in the StateNavigator, and a Scene is the component that renders the UI for that state. On React you use SceneView with an active attribute, on React Native you use Scene with a stateKey inside a NavigationStack.
How do I pass data between scenes in the Navigation router?
Pass it as navigationData when you trigger a transition, or as the second argument in the stateNavigator call under React Native. The destination reads it with the data property from useNavigationEvent, and the documentation describes that data as strongly typed.
Does the Navigation router render native navigation components on Android and iOS?
Yes. The documentation states that the React Native components render to the same native primitives as other native apps, and gives TabBar rendering to a BottomNavigationView on Android and a UITabBarController on iOS as the example.
How do I build the Navigation router from a clone of the repository?
Run npm install and then npm run build. The unit tests run with npm test, and npm run package outputs the npm packages into the build/npm folder.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/grahammendick-navigation)Was this useful?