React Native Navigation Codebase
Architecture Overview
RNN has three layers that mirror each other:
JS/TS (src/) → TurboModule bridge → iOS native (ios/)
→ Android native (android/)
A navigation command (e.g. push) flows:
Navigation.push() → Commands.ts → processing pipeline → NativeCommandsSender.ts
- TurboModule:
RNNTurboModule (iOS) / NavigationTurboModule.kt (Android)
- iOS:
RNNCommandsHandler → RNNViewControllerFactory → UIKit controllers
- Android:
Navigator → LayoutFactory → View-based controllers (no Fragments)
Read ARCHITECTURE.md for the full overview.
Key Cross-Layer Mappings
Layout Types → Native Controllers
| JS Layout Type |
iOS Controller |
Android Controller |
component |
RNNComponentViewController |
ComponentViewController |
stack |
RNNStackController (UINavigationController) |
StackController |
bottomTabs |
RNNBottomTabsController (UITabBarController) |
BottomTabsController |
sideMenu |
RNNSideMenuViewController (MMDrawerController) |
SideMenuController (DrawerLayout) |
topTabs |
RNNTopTabsViewController |
TopTabsController (ViewPager) |
splitView |
RNNSplitViewController |
N/A (iOS only) |
externalComponent |
RNNExternalViewController |
ExternalComponentViewController |
Options → Presenters
Each controller type has a Presenter that applies options to views:
| iOS Controller |
iOS Presenter |
Android Presenter |
RNNComponentViewController |
RNNComponentPresenter |
ComponentPresenter |
RNNStackController |
RNNStackPresenter + TopBarPresenter |
StackPresenter |
RNNBottomTabsController |
RNNBottomTabsPresenter |
BottomTabsPresenter |
RNNSideMenuViewController |
RNNSideMenuPresenter |
SideMenuPresenter |
Events (same names both platforms)
| Event |
Trigger |
RNN.ComponentDidAppear |
Screen becomes visible |
RNN.ComponentDidDisappear |
Screen hidden |
RNN.NavigationButtonPressed |
TopBar button tap |
RNN.BottomTabSelected |
Tab changed |
RNN.ModalDismissed |
Modal dismissed |
RNN.ScreenPopped |
Screen popped from stack |
RNN.CommandCompleted |
Any command finished |
Where to Find Things
By task: "I need to fix/change X"
| Task |
JS File(s) |
iOS File(s) |
Android File(s) |
| Command execution |
src/commands/Commands.ts |
ios/RNNCommandsHandler.mm |
react/NavigationTurboModule.kt |
| Layout creation |
src/commands/LayoutTreeParser.ts |
ios/RNNViewControllerFactory.mm |
options/LayoutFactory.java |
| Options processing |
src/commands/OptionsProcessor.ts |
ios/RNNNavigationOptions.mm |
options/Options.java |
| Options application |
— |
ios/*Presenter.mm |
viewcontrollers/*Presenter.java |
| TopBar |
src/interfaces/Options.ts (TopBarOptions) |
ios/TopBarPresenter.mm, ios/RNNUIBarButtonItem.mm |
views/stack/topbar/ |
| Bottom tabs |
src/interfaces/Options.ts (BottomTabsOptions) |
ios/RNNBottomTabsPresenter.mm |
viewcontrollers/bottomtabs/ |
| Modals |
src/commands/Commands.ts |
ios/RNNModalManager.mm |
viewcontrollers/modal/ModalStack.java |
| Overlays |
src/commands/Commands.ts |
ios/RNNOverlayManager.mm |
viewcontrollers/overlay/OverlayManager.kt |
| Animations |
src/interfaces/Options.ts (AnimationOptions) |
ios/ScreenAnimationController.mm |
viewcontrollers/stack/StackAnimator.kt |
| React view rendering |
— |
ios/RNNReactView.mm |
react/ReactView.java |
| Events to JS |
src/adapters/NativeEventsReceiver.ts |
ios/RNNEventEmitter.mm |
react/events/EventEmitter.java |
| Component registration |
src/components/ComponentRegistry.ts |
— |
— |
By directory
src/ — JS public API, commands, processing pipeline. See src/ARCHITECTURE.md
ios/ — All Obj-C/C++ native code. See ios/ARCHITECTURE.md
ios/TurboModules/ — New architecture entry points (RNNTurboModule, RNNTurboManager, RNNTurboCommandsHandler)
android/src/main/java/com/reactnativenavigation/ — All Java/Kotlin native code. See android/ARCHITECTURE.md
playground/ — Demo app for development and E2E tests
playground/src/screens/ — Test screens exercising every feature
playground/e2e/ — Detox E2E tests
Options Resolution Order
Options are applied in ascending priority:
- Default options (from
Navigation.setDefaultOptions()) — lowest priority
- Static options (from component class or
Navigation.registerComponent)
- Options passed in the layout call (e.g.
push, setRoot)
mergeOptions() — runtime override, highest priority
JS Processing Pipeline (exact order)
API layout → OptionsCrawler.crawl() → LayoutProcessor.process()
→ LayoutTreeParser.parse() → LayoutTreeCrawler.crawl()
→ OptionsProcessor (colors, assets, custom) → NativeCommandsSender
iOS Patterns
- All controllers conform to
RNNLayoutProtocol
RNNBasePresenter subclasses apply options — applyOptionsOnInit:, applyOptions:, mergeOptions:resolvedOptions:
- Commands run on main thread (
RCTExecuteOnMainQueue)
- React views:
RNNReactView wraps RCTSurfaceHostingView (new arch)
- Overlays use separate
UIWindow instances (RNNOverlayWindow)
RNNReactComponentRegistry caches React component instances
Android Patterns
- View-based, NOT Fragment-based
- All commands dispatched via
UiThread.post()
ViewController<T extends ViewGroup> is the base — createView() is abstract
ParentController extends ChildController extends ViewController
- Bottom tabs use
AHBottomNavigation library
- Three root layouts in
NavigationActivity: rootLayout, modalsLayout, overlaysLayout
- Tab attachment modes:
Together, OnSwitchToTab, AfterInitialTab
Development Workflow
Playground app
yarn start — Metro bundler
yarn xcode — Open iOS project
yarn studio — Open Android project
yarn pod-install — Install iOS pods
Testing
yarn test-js — Jest unit tests
yarn test-unit-ios — iOS native unit tests (XCTest)
yarn test-unit-android — Android native unit tests (JUnit + Robolectric)
yarn test-e2e-ios-ci / yarn test-e2e-android-ci — Detox E2E tests
Building
yarn prepare — Builds src/ → lib/ (ESM + types)
- Codegen config:
rnnavigation in package.json
Engine Integration (Wix One App)
RNN is a standalone library but also a core dependency of mobile-apps-engine (the Wix One App platform). Changes to RNN's public API surface can break engine.
Version Management
- Version pinned in two files (must match):
packages/native/mobile-apps-engine-native/package.json
packages/wix-one-app-storage/package.json
yarn.config.cjs has a constraint that reads the version from mobile-apps-engine-native and validates all workspaces use the same version. It's a validation rule — bumping the native package.json is sufficient.
iOS Integration
- Engine's
AppDelegate subclasses RNNAppDelegate — breaking changes to that class will break engine.
- Podfile resolves
ReactNativeNavigation pod from node_modules.
- Xcode project has header search paths pointing into
node_modules/react-native-navigation/ios/**.
Android Integration
- Engine's
EngineRN.kt extends NavigationApplication and uses NavigationPackage / NavigationReactNativeHost.
missingDimensionStrategy "RNN.reactNativeVersion" is set in app/build.gradle — may need updating if RNN changes its flavor dimensions.
- Build variables
RNNKotlinVersion, RNNKotlinStdlib, RNNKotlinCoroutinesCore are forwarded from engine's Kotlin config.
Active Patches (in engine's setup)
- OptionsProcessor.js — engine replaces RNN's
OptionsProcessor.js at setup time via patchRNNOptionsProcessor() in packages/cli/mobile-apps-engine-setup/src/index.js. The patched copy lives at packages/cli/mobile-apps-engine-setup/etc/OptionsProcessor.js. It adds:
- Custom iOS color processing with
DynamicColorIOS (dark/light/dynamic object shapes)
- Custom Android color processing wrapping colors in
{dark, light} objects with semantic/resource_paths support
- Component ID uses
value.name instead of uniqueIdProvider.generate()
- Any changes to
src/commands/OptionsProcessor.ts require checking if the engine patch needs updating.
JS Wrappers
Engine wraps RNN's Navigation API in several services:
Navigator.ts — root layout, overlays, error screens, tab navigation
BottomTabsBackHandler.ts — back handling on bottom tabs via Navigation.events()
TabsManager.ts — tab management using RNN Layout types
- Various other services import
Layout, LayoutRoot, LayoutSideMenu, OptionsSideMenu from RNN
Upgrade Checklist
- Bump version in both
package.json files
- Verify
OptionsProcessor.js patch still applies (diff upstream changes)
- Check
RNNAppDelegate API compatibility (iOS)
- Check
NavigationApplication / NavigationPackage / NavigationReactNativeHost API compatibility (Android)
- Verify
missingDimensionStrategy value is still valid
- Check
react-native-navigation-hooks and rnn-copilot compatibility
- Run
yarn install to update lockfile
- Verify test mocks (
react-native-navigation/Mock) still work
Common Gotchas
- iOS uses UIKit subclasses (UINavigationController, UITabBarController); Android uses custom View hierarchy
splitView is iOS-only
- Side menu: iOS uses MMDrawerController (3rd party); Android uses DrawerLayout (native)
- Options that exist in JS types may not be implemented on both platforms — check the presenter
passProps are stored in JS Store, not sent to native (cleared before bridge crossing)
- The
lib/ folder is generated — never edit it, edit src/ instead
1---2name: rnn-codebase-23description: Navigate and work with the react-native-navigation (RNN) codebase. Use when fixing bugs, adding features, tracing command flows, understanding options resolution, or working across JS/iOS/Android layers in this repo.4---56# React Native Navigation Codebase78## Architecture Overview910RNN has three layers that mirror each other:1112```13JS/TS (src/) → TurboModule bridge → iOS native (ios/)14 → Android native (android/)15```1617A navigation command (e.g. `push`) flows:181. `Navigation.push()` → `Commands.ts` → processing pipeline → `NativeCommandsSender.ts`192. TurboModule: `RNNTurboModule` (iOS) / `NavigationTurboModule.kt` (Android)203. iOS: `RNNCommandsHandler` → `RNNViewControllerFactory` → UIKit controllers214. Android: `Navigator` → `LayoutFactory` → View-based controllers (no Fragments)2223Read [ARCHITECTURE.md](../../ARCHITECTURE.md) for the full overview.2425## Key Cross-Layer Mappings2627### Layout Types → Native Controllers2829| JS Layout Type | iOS Controller | Android Controller |30|----------------|---------------|-------------------|31| `component` | `RNNComponentViewController` | `ComponentViewController` |32| `stack` | `RNNStackController` (UINavigationController) | `StackController` |33| `bottomTabs` | `RNNBottomTabsController` (UITabBarController) | `BottomTabsController` |34| `sideMenu` | `RNNSideMenuViewController` (MMDrawerController) | `SideMenuController` (DrawerLayout) |35| `topTabs` | `RNNTopTabsViewController` | `TopTabsController` (ViewPager) |36| `splitView` | `RNNSplitViewController` | N/A (iOS only) |37| `externalComponent` | `RNNExternalViewController` | `ExternalComponentViewController` |3839### Options → Presenters4041Each controller type has a Presenter that applies options to views:4243| iOS Controller | iOS Presenter | Android Presenter |44|----------------|--------------|-------------------|45| `RNNComponentViewController` | `RNNComponentPresenter` | `ComponentPresenter` |46| `RNNStackController` | `RNNStackPresenter` + `TopBarPresenter` | `StackPresenter` |47| `RNNBottomTabsController` | `RNNBottomTabsPresenter` | `BottomTabsPresenter` |48| `RNNSideMenuViewController` | `RNNSideMenuPresenter` | `SideMenuPresenter` |4950### Events (same names both platforms)5152| Event | Trigger |53|-------|---------|54| `RNN.ComponentDidAppear` | Screen becomes visible |55| `RNN.ComponentDidDisappear` | Screen hidden |56| `RNN.NavigationButtonPressed` | TopBar button tap |57| `RNN.BottomTabSelected` | Tab changed |58| `RNN.ModalDismissed` | Modal dismissed |59| `RNN.ScreenPopped` | Screen popped from stack |60| `RNN.CommandCompleted` | Any command finished |6162## Where to Find Things6364### By task: "I need to fix/change X"6566| Task | JS File(s) | iOS File(s) | Android File(s) |67|------|-----------|------------|----------------|68| Command execution | `src/commands/Commands.ts` | `ios/RNNCommandsHandler.mm` | `react/NavigationTurboModule.kt` |69| Layout creation | `src/commands/LayoutTreeParser.ts` | `ios/RNNViewControllerFactory.mm` | `options/LayoutFactory.java` |70| Options processing | `src/commands/OptionsProcessor.ts` | `ios/RNNNavigationOptions.mm` | `options/Options.java` |71| Options application | — | `ios/*Presenter.mm` | `viewcontrollers/*Presenter.java` |72| TopBar | `src/interfaces/Options.ts` (TopBarOptions) | `ios/TopBarPresenter.mm`, `ios/RNNUIBarButtonItem.mm` | `views/stack/topbar/` |73| Bottom tabs | `src/interfaces/Options.ts` (BottomTabsOptions) | `ios/RNNBottomTabsPresenter.mm` | `viewcontrollers/bottomtabs/` |74| Modals | `src/commands/Commands.ts` | `ios/RNNModalManager.mm` | `viewcontrollers/modal/ModalStack.java` |75| Overlays | `src/commands/Commands.ts` | `ios/RNNOverlayManager.mm` | `viewcontrollers/overlay/OverlayManager.kt` |76| Animations | `src/interfaces/Options.ts` (AnimationOptions) | `ios/ScreenAnimationController.mm` | `viewcontrollers/stack/StackAnimator.kt` |77| React view rendering | — | `ios/RNNReactView.mm` | `react/ReactView.java` |78| Events to JS | `src/adapters/NativeEventsReceiver.ts` | `ios/RNNEventEmitter.mm` | `react/events/EventEmitter.java` |79| Component registration | `src/components/ComponentRegistry.ts` | — | — |8081### By directory8283- **`src/`** — JS public API, commands, processing pipeline. See [src/ARCHITECTURE.md](../../src/ARCHITECTURE.md)84- **`ios/`** — All Obj-C/C++ native code. See [ios/ARCHITECTURE.md](../../ios/ARCHITECTURE.md)85- **`ios/TurboModules/`** — New architecture entry points (`RNNTurboModule`, `RNNTurboManager`, `RNNTurboCommandsHandler`)86- **`android/src/main/java/com/reactnativenavigation/`** — All Java/Kotlin native code. See [android/ARCHITECTURE.md](../../android/ARCHITECTURE.md)87- **`playground/`** — Demo app for development and E2E tests88- **`playground/src/screens/`** — Test screens exercising every feature89- **`playground/e2e/`** — Detox E2E tests9091## Options Resolution Order9293Options are applied in ascending priority:941. Default options (from `Navigation.setDefaultOptions()`) — lowest priority952. Static options (from component class or `Navigation.registerComponent`)963. Options passed in the layout call (e.g. `push`, `setRoot`)974. `mergeOptions()` — runtime override, highest priority9899## JS Processing Pipeline (exact order)100101```102API layout → OptionsCrawler.crawl() → LayoutProcessor.process()103 → LayoutTreeParser.parse() → LayoutTreeCrawler.crawl()104 → OptionsProcessor (colors, assets, custom) → NativeCommandsSender105```106107## iOS Patterns108109- All controllers conform to `RNNLayoutProtocol`110- `RNNBasePresenter` subclasses apply options — `applyOptionsOnInit:`, `applyOptions:`, `mergeOptions:resolvedOptions:`111- Commands run on main thread (`RCTExecuteOnMainQueue`)112- React views: `RNNReactView` wraps `RCTSurfaceHostingView` (new arch)113- Overlays use separate `UIWindow` instances (`RNNOverlayWindow`)114- `RNNReactComponentRegistry` caches React component instances115116## Android Patterns117118- View-based, NOT Fragment-based119- All commands dispatched via `UiThread.post()`120- `ViewController<T extends ViewGroup>` is the base — `createView()` is abstract121- `ParentController` extends `ChildController` extends `ViewController`122- Bottom tabs use `AHBottomNavigation` library123- Three root layouts in `NavigationActivity`: rootLayout, modalsLayout, overlaysLayout124- Tab attachment modes: `Together`, `OnSwitchToTab`, `AfterInitialTab`125126## Development Workflow127128### Playground app129- `yarn start` — Metro bundler130- `yarn xcode` — Open iOS project131- `yarn studio` — Open Android project132- `yarn pod-install` — Install iOS pods133134### Testing135- `yarn test-js` — Jest unit tests136- `yarn test-unit-ios` — iOS native unit tests (XCTest)137- `yarn test-unit-android` — Android native unit tests (JUnit + Robolectric)138- `yarn test-e2e-ios-ci` / `yarn test-e2e-android-ci` — Detox E2E tests139140### Building141- `yarn prepare` — Builds `src/` → `lib/` (ESM + types)142- Codegen config: `rnnavigation` in `package.json`143144## Engine Integration (Wix One App)145146RNN is a standalone library but also a core dependency of `mobile-apps-engine` (the Wix One App platform). Changes to RNN's public API surface can break engine.147148### Version Management149150- Version pinned in two files (must match):151 - `packages/native/mobile-apps-engine-native/package.json`152 - `packages/wix-one-app-storage/package.json`153- `yarn.config.cjs` has a constraint that reads the version from `mobile-apps-engine-native` and validates all workspaces use the same version. It's a validation rule — bumping the native package.json is sufficient.154155### iOS Integration156157- Engine's `AppDelegate` subclasses `RNNAppDelegate` — breaking changes to that class will break engine.158- Podfile resolves `ReactNativeNavigation` pod from node_modules.159- Xcode project has header search paths pointing into `node_modules/react-native-navigation/ios/**`.160161### Android Integration162163- Engine's `EngineRN.kt` extends `NavigationApplication` and uses `NavigationPackage` / `NavigationReactNativeHost`.164- `missingDimensionStrategy "RNN.reactNativeVersion"` is set in `app/build.gradle` — may need updating if RNN changes its flavor dimensions.165- Build variables `RNNKotlinVersion`, `RNNKotlinStdlib`, `RNNKotlinCoroutinesCore` are forwarded from engine's Kotlin config.166167### Active Patches (in engine's setup)168169- **OptionsProcessor.js** — engine replaces RNN's `OptionsProcessor.js` at setup time via `patchRNNOptionsProcessor()` in `packages/cli/mobile-apps-engine-setup/src/index.js`. The patched copy lives at `packages/cli/mobile-apps-engine-setup/etc/OptionsProcessor.js`. It adds:170 - Custom iOS color processing with `DynamicColorIOS` (dark/light/dynamic object shapes)171 - Custom Android color processing wrapping colors in `{dark, light}` objects with `semantic`/`resource_paths` support172 - Component ID uses `value.name` instead of `uniqueIdProvider.generate()`173- Any changes to `src/commands/OptionsProcessor.ts` require checking if the engine patch needs updating.174175### JS Wrappers176177Engine wraps RNN's `Navigation` API in several services:178- `Navigator.ts` — root layout, overlays, error screens, tab navigation179- `BottomTabsBackHandler.ts` — back handling on bottom tabs via `Navigation.events()`180- `TabsManager.ts` — tab management using RNN `Layout` types181- Various other services import `Layout`, `LayoutRoot`, `LayoutSideMenu`, `OptionsSideMenu` from RNN182183### Upgrade Checklist1841851. Bump version in both `package.json` files1862. Verify `OptionsProcessor.js` patch still applies (diff upstream changes)1873. Check `RNNAppDelegate` API compatibility (iOS)1884. Check `NavigationApplication` / `NavigationPackage` / `NavigationReactNativeHost` API compatibility (Android)1895. Verify `missingDimensionStrategy` value is still valid1906. Check `react-native-navigation-hooks` and `rnn-copilot` compatibility1917. Run `yarn install` to update lockfile1928. Verify test mocks (`react-native-navigation/Mock`) still work193194## Common Gotchas195196- iOS uses UIKit subclasses (UINavigationController, UITabBarController); Android uses custom View hierarchy197- `splitView` is iOS-only198- Side menu: iOS uses MMDrawerController (3rd party); Android uses DrawerLayout (native)199- Options that exist in JS types may not be implemented on both platforms — check the presenter200- `passProps` are stored in JS `Store`, not sent to native (cleared before bridge crossing)201- The `lib/` folder is generated — never edit it, edit `src/` instead