Technical Documentation: wix/react-native-navigation
ℹ️ Provenance: Multi-Tier Ingestion · CodeWiki Reference
Overview (`README.md`)
React Native Navigation
React Native Navigation provides 100% native platform navigation on both iOS and Android for React Native apps. The JavaScript API is simple and cross-platform - just install it in your app and give your users the native feel they deserve. Ready to get started? Check out the docs.
Quick Links
Requirements
Apps using React Native Navigation may target iOS 11 and Android 5.0 (API 21). You may use Windows, macOS or Linux as your development operating system.
Installation
As react-native-navigation is a native navigation library - integrating it into your app will require editing native files. Follow the installation guides in the documentation.
Releases
Repository administrators: see the release runbook for manual releases,
npm trusted publishing setup, and recovery.
Overview (`playground/README.md`)
Navigation Playground
A local example project using react-native-navigation.
The target app for all e2e tests.
Overview (`test-app/README.md`)
This is a new React Native project, bootstrapped using @react-native-community/cli.
Getting Started
Note: Make sure you have completed the Set Up Your Environment guide before proceeding.
Step 1: Start Metro
First, you will need to run Metro, the JavaScript build tool for React Native.
To start the Metro dev server, run the following command from the root of your React Native project:
# Using npm
npm start
# OR using Yarn
yarn startStep 2: Build and run your app
With Metro running, open a new terminal window/pane from the root of your React Native project, and use one of the following commands to build and run your Android or iOS app:
Android
# Using npm
npm run android
# OR using Yarn
yarn androidiOS
For iOS, remember to install CocoaPods dependencies (this only needs to be run on first clone or after updating native deps).
The first time you create a new project, run the Ruby bundler to install CocoaPods itself:
bundle installThen, and every time you update your native dependencies, run:
bundle exec pod installFor more information, please visit CocoaPods Getting Started guide.
# Using npm
npm run ios
# OR using Yarn
yarn iosIf everything is set up correctly, you should see your new app running in the Android Emulator, iOS Simulator, or your connected device.
This is one way to run your app — you can also build it directly from Android Studio or Xcode.
Step 3: Modify your app
Now that you have successfully run the app, let's make changes!
Open App.tsx in your text editor of choice and make some changes. When you save, your app will automatically update and reflect these changes — this is powered by Fast Refresh.
When you want to forcefully reload, for example to reset the state of your app, you can perform a full reload:
- Android: Press the R key twice or select "Reload" from the Dev Menu, accessed via Ctrl + M (Windows/Linux) or Cmd ⌘ + M (macOS).
- iOS: Press R in iOS Simulator.
Congratulations! :tada:
You've successfully run and modified your React Native App. :partying_face:
Now what?
- If you want to add this new React Native code to an existing application, check out the Integration guide.
- If you're curious to learn more about React Native, check out the docs.
Troubleshooting
If you're having issues getting the above steps to work, see the Troubleshooting page.
Learn More
To learn more about React Native, take a look at the following resources:
- React Native Website - learn more about React Native.
- Getting Started - an overview of React Native and how setup your environment.
- Learn the Basics - a guided tour of the React Native basics.
- Blog - read the latest official React Native Blog posts.
@facebook/react-native- the Open Source; GitHub repository for React Native.
RELEASING (`docs/RELEASING.md`)
Releasing React Native Navigation
The Release workflow
is the only supported npm publisher. Repository admins start it manually on master.
Both regular releases and snapshots use npm trusted publishing with GitHub OIDC.
Buildkite runs native tests; it no longer versions or publishes packages. Its expired npm
credentials must not be renewed as a fallback.
One-time configuration
A repository admin and an npm package administrator must complete these steps before
enabling publication. Creating or merging the workflow does not configure npm trust.
Create the GitHub environment
npm-release. Under deployment branches and tags,
select only the branchmaster. Protectmasterand review changes to release
workflows and helpers. Required environment reviewers are optional; enabling them
introduces approvals for the record, publish, and finalize jobs.In the npm settings for
react-native-navigation, add a GitHub Actions trusted publisher:Field Value Organization wixRepository react-native-navigationWorkflow filename release.ymlEnvironment npm-releaseAllowed action Direct npm publishExplicitly allow direct publication: a stage-only configuration cannot complete this
workflow. Standalonenpm dist-tagpermission is not needed. See
npm trusted publishing.Enable GitHub Actions for this repository. Check Wix's Actions policy permits the pinned checkout, setup-node, upload-artifact,
and download-artifact actions. Allow workflow-created pull requests. Ensure existing
repository rules permit version branches and release tags without a bypass.Check the GitHub commit status
buildkite/react-native-navigationrepresents all
Android/iOS lanes in.buildkite/pipeline.sh. It must finish successfully on the
exactmastercommit selected by the release run. The status creator must match
the Buildkite app ID/login pinned inscripts/release/rnn.cjs. A failed, absent, or pending status
blocks real publication, including snapshots. A dry run reports it without publishing.Verify a dry run and inspect its artifact. Then set the repository Actions variable
RELEASE_PUBLISH_ENABLEDtotrue. Without it, publish and resume fail closed.Run an intentional snapshot publication and verify the npm version, integrity,
provenance, andsnapshotdist-tag. Confirm thatlatestdid not change.Remove obsolete release-only npm secret injection and restrict traditional token
publishing in npm's package settings. Do not renew expired tokens. A shared credential
must not be revoked until its other consumers have been identified.
No npm write token is needed in GitHub. GITHUB_TOKEN performs repository bookkeeping;
only the publish job has id-token: write. Publishing runs on a GitHub-hosted Ubuntu
runner. The workflow uses Node 24.22.1, including npm 11.19.1; update this tested pair
together. Older npm releases can execute prepare during npm pack --ignore-scripts.
Start a release
Open Actions → Release → Run workflow, select master, and supply:
| Input | Meaning |
|---|---|
operation |
dry-run (default), publish, or resume |
kind |
release or snapshot |
version |
Exact semver for releases; empty for snapshots |
npm_tag |
latest for stable versions, next for prereleases, snapshot for snapshots |
documentation_version |
Optional numeric x.y.z documentation snapshot |
remove_documentation_version |
Optional existing version to remove while generating its replacement |
source_run_id |
Original publishing run ID, for resume only |
Examples:
- Stable:
kind=release,version=8.9.1,npm_tag=latest. - Prerelease:
kind=release,version=8.9.0-rc.1,npm_tag=next. - Snapshot:
kind=snapshot, leave version empty,npm_tag=snapshot.
The selected version must be newer than npm latest. Build metadata such as +build.1
is intentionally unsupported. Snapshots use -snapshot.gha.<run_id>, with the
base derived from the greater of the source package version and npm latest. The
run attempt is not part of the version. Snapshots do not create tags, GitHub releases,
version PRs, or documentation changes.
Users with write access may see GitHub's Run workflow button, but both the original
actor and the user rerunning/resuming the workflow must have repository-admin permission.
The workflow rechecks authorization before every job that changes external state.
What the workflow does
- Records the exact source SHA, chosen version/channel, and native CI result.
- Installs the locked Yarn dependencies, changes release metadata, builds JS and
declarations with Bob, and runs JavaScript and release-helper tests on Linux. - Optionally installs the locked website dependencies, generates the requested docs
version, and builds the site. Removal requires an existing numeric version; replacing
an existing version requires explicitly selecting that same version for removal. - Creates a local release commit containing only the version and optional versioned
docs, then packs the already built package. Package contents and exported paths are
checked. Android/iOS sources ship in the package; this job does not compile native apps. - Uploads a versioned manifest, the tarball, release notes, and a Git bundle as
release-<run_id>-<run_attempt>. A dry run ends here with no external release mutations. - For real releases, persists
ci/update-version-<version>, then publishes the verified
tarball using OIDC. The publisher does not install project dependencies or rebuild it. - Verifies npm integrity, tags the source commit on
masterwith the bare version (for example8.9.1), creates a GitHub release, and opens a version/documentation PR with thereleaselabel. An existing PR template is included with its checkboxes left for review.
Release notes include merged PRs from the preceding reachable stable tag through the
source commit. They preserve feature/enhancement/fix categories, platform groups, andskip-changelog exclusions. No second gren invocation or timed retry is involved.
Review and merge the version PR after publication. Documentation deploys through the
existing documentation workflow after that merge. Events created with GITHUB_TOKEN do not
start Actions workflows, so Release checks will not run on the version PR; Buildkite still
builds it through its webhook. Inspect the PR's checks before merging. The release workflow
does not invoke engine notification.
Resume a partial release
Use Re-run failed jobs when the original run still has its preparation outputs, or
dispatch operation=resume with source_run_id set to the original publish run.
Other release inputs are ignored in resume mode. Do not enter a different version to
recover from a GitHub or network error.
Recovery validates the upstream workflow identity, source SHA, native CI, artifact
integrity, release commit, and package metadata. It downloads the saved artifacts and
does not rebuild against the current branch. If the version is still absent from npm andmaster has advanced, re-run the original publishing run instead of dispatching a new
resume run: npm provenance must identify the source commit that built the package. A new
resume dispatch may finish GitHub bookkeeping for an already published matching tarball.
| State | Result |
|---|---|
| Version absent from npm | Publish the original tarball, provided its channel would not move backward |
| Same version and same integrity already published | Skip publication and finish GitHub steps |
| Same version with different integrity | Stop; investigate the conflicting publication |
| Registry failure or lost response | Fail without choosing another version; resume when healthy |
| Existing branch/tag/PR matches | Reuse it |
| Existing branch/tag/PR conflicts | Stop; never force-push or move a tag |
| Dry-run artifact selected | Reject; start a fresh publish run |
| Artifact expired/deleted | Stop automatic recovery |
Artifacts request 90-day retention, subject to organization limits. Retain artifacts until
the version PR is merged and the release is verified. After artifact loss, an admin must
investigate the existing registry version and persisted release branch manually; do not
reconstruct and publish different bytes under the same version or automatically fall back
to the old pipeline. npm and GitHub do not provide a cross-service atomic transaction.
The concurrency group serializes all release attempts. GitHub may replace a pending run
when another dispatch is queued; it does not cancel the running publisher. Check each run's
state rather than assuming every click is queued indefinitely.
Validate changes to the release tooling
With Node 24.22.1 and npm 11.19.1:
node --test scripts/release/*.test.cjs
node .yarn/releases/yarn-4.12.0.cjs install --immutable
node .yarn/releases/yarn-4.12.0.cjs prepare
node .yarn/releases/yarn-4.12.0.cjs test-js
RELEASE_DIR=/tmp/rnn-package node scripts/release/index.cjs pack-checkpack-check only prepares and inspects a local tarball. The read-only Release checks
workflow runs the tests and clean Linux package check on PRs. A real OIDC publication
still needs the default-branch workflow and the npm/GitHub configuration above.
Removed release paths and retained consumers
The Buildkite release form, publishing job, legacy scripts/release.js, gren config,
and root/playground github-release-notes dependencies have been removed. Automatic
Buildkite snapshots are replaced by manual snapshots. Native Buildkite testing remains.
Outside the repository, remove obsolete release schedules/hooks and release-onlyNPM_TOKEN/NPM_EMAIL injection. Inspect any old Jenkins configuration referenced by
historical setup; the old README badge alone does not prove an active publisher exists.
Keep the tracked registry-only .npmrc, documentation deployment's GH_PAGES_DEPLOY,
and any Git credential still used by scripts/test-snapshot.js to record reference images.
Do not delete shared secrets merely because the old publisher also used them.
For the next repository, use the migration checklist.
Release Migration Checklist (`docs/release-migration-checklist.md`)
Migrating another repository to trusted publishing
RNN establishes a release contract: admin dispatch, explicit versions, immutable build
artifacts, OIDC publication, and recovery of the same package bytes. Copy the contract
before extracting shared code. Repository-specific build and package policy lives inscripts/release/rnn.cjs; the current workflow and manifest validator intentionally accept
only RNN. Another repository must implement and test its own adapter and authorization.
Inventory before changing a publisher
- Read the target repository's instructions and release tests.
- List every publishable package, its source directory, version authority, dist-tags,
Git tag convention, and dependency relationship to the other packages. - Trace release-trigger inputs, lifecycle hooks, generated artifacts, documentation,
release notes, PRs, and any native packaging commands. - Identify native CI evidence by exact commit. Distinguish the tools needed to produce
the npm package from tools needed only for testing. - Inventory CI schedules, webhooks, release credentials, and other consumers of shared
credentials. A release script deletion alone does not remove an external trigger.
Implement the repository adapter
- Keep a stable repository-local
release.ymlentry point and protected environment. - Configure allowed branches, admin authorization, packages, runners, build commands,
expected tarball contents, version/dependency rewrites, and tag naming. - Resolve the complete package/version plan before the first publish. Use an ordered
package list and store each package's integrity in the manifest. - Build without npm publishing credentials. Native preparation can use separate runner
jobs; the OIDC publisher must run on a supported GitHub-hosted runner. - Preserve artifacts and verify their original workflow/source identity during recovery.
- Publish each package only if absent. If already present, require matching integrity.
Never bump all versions after a partial publish. Multi-package publication is not atomic. - Register npm trust separately for every package. If introducing reusable workflows,
check npm's caller-workflow identity rules and grant the required OIDC permissions at
the caller/callee boundaries. Pin shared implementations to reviewed commit SHAs.
Known differences from RNN
| Repository | Migration requirement |
|---|---|
| react-native-navigation | One package, Bob JS/types build, native sources, bare version Git tags |
| react-native-ui-lib | Separate uilib-native and react-native-ui-lib versions; native package first when selected; rewrite workspace dependencies; preserve package-qualified tags |
| Detox | Discover and validate publishable workspaces; preserve synchronized versions; trace Android and iOS native artifact packaging before choosing runners |
UI-lib's starting points are scripts/release/release.js, releaseUtils.js, and the
existing release-script tests. Its dependency rewrite must use the planned or verified
published native version, including when only one package needs publication.
Detox's starting points are scripts/ci.release.js, scripts/utils/publishNewVersion.js,scripts/ci.android-release.js, scripts/ci.ios-release.sh, and the Buildkite release
pipelines. Android packaging invokes the SDK and Gradle. Resolve the current iOS packaging
entry point before replacing its artifact download flow. Do not assume RNN's Linux-only
package preparation applies to Detox.
Clean up and verify
- Delete obsolete publishing jobs, forms, entry points, credential-writing helpers,
retry-by-version-increment code, dead dependencies, and stale instructions. - Preserve native tests, useful history, documentation deployment, and shared credentials
with remaining consumers. - Test dry-run non-mutation, unauthorized dispatch/rerun, version/channel validation,
source CI gating, package completeness, and lost-response/partial-publication recovery. - Verify clean runner builds, then a deliberate prerelease/snapshot using real OIDC.
- Document one-time configuration, remaining infrastructure cleanup, and artifact-loss
recovery. Expired token-based publishers are not an automatic fallback.
Extract the common publisher, authorization, and artifact-state helpers only after a
second adopter confirms their interface. Build commands, package graphs, documentation,
and version conventions should remain repository-specific.
Api Api Component (`website/docs/api/api-component.mdx`)
id: component title: Component sidebar_label: Component
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
`registerComponent`
Every screen component in your app must be registered with a unique name. The component itself is a traditional React component extending React.Component or React.PureComponent. It can also be a HOC to provide context (or a Redux store) or a functional component. Similar to React Native's AppRegistry.registerComponent.
Parameters
| Name | Required | Type | Description |
|---|---|---|---|
| componentName | Yes | string | Unique component name - not to be confused with componentId |
| componentProvider | Yes | Function | Anonymous function that returns the React component to register, OR the component wrapped with Providers |
| concreteComponentProvider | No | Function | Anonymous function that returns the concrete React component. If your component is wrapped with Providers, this must be an instance of the concrete component. |
Examples
Registering a component
Navigation.registerComponent(`navigation.playground.WelcomeScreen`, () => WelcomeScreen);Registering a component wrapped with Providers
import { Provider } from 'react-redux';
const store = createStore();
Navigation.registerComponent(`navigation.playground.MyScreen`, () => (props) =>
<Provider store={store}>
<MyScreen {...props} />
</Provider>,
() => MyScreen)
);`setLazyComponentRegistrator`
Pass a function that will allow you to register a component lazily. When encountering an unknown component name, this function will be called, giving you a chance to register the component before an error is thrown.
Parameters
| Name | Required | Type | Description |
|---|---|---|---|
| lazyRegistratorFn | Yes | (lazyComponentRequest: string | number) => void |
Example
Navigation.setLazyComponentRegistrator((componentName) => {
if (componentName === 'navigation.playground.LazyScreen') {
Navigation.registerComponent(Screens.LazilyRegisteredScreen, () => LazyScreen);
}
});`updateProps`
Update props of a component registered with registerComponent. Updating props triggers the component lifecycle methods related to updating.
Parameters
| Name | Required | Type | Description |
|---|---|---|---|
| componentId | Yes | string | Unique component id |
| options | Yes | object | Props object to pass to the component |
| callback | No | Function | Function that will be executed once inner setState is completed |
Example
Navigation.updateProps('MY_COMPONENT', {
// props to pass to the component
};:::importantupdateProps is called with a componentId. This is the same unique id components have in props. Don't confuse it with the componentName we use when registering components with Navigation.registerComponent.
:::
Api Api Events (`website/docs/api/api-events.mdx`)
id: events title: Events sidebar_label: Events
onAppLaunched
Called once the app is launched. Used to set the initial layout of the Application - after that the app is ready for user interaction.
const appLaunchedListener = Navigation.events().registerAppLaunchedListener(() => {});RNN automatically unsubscribes components when they unmount, therefore unsubscribing isn't actually mandatory if you subscribed in componentDidMount.
But you can use the following method to unsubscribe manually:
appLaunchedListener.remove();componentWillAppear
Called each time before this component appears on the screen (notifies that the component view is about to be added to the view hierarchy).
class MyComponent extends Component {
componentDidMount() {
this.navigationEventListener = Navigation.events().bindComponent(this);
}
componentWillUnmount() {
// Not mandatory
if (this.navigationEventListener) {
this.navigationEventListener.remove();
}
}
componentWillAppear() {}
}componentDidAppear
Called each time this component appears on the screen (attached to the view hierarchy).
class MyComponent extends Component {
componentDidMount() {
this.navigationEventListener = Navigation.events().bindComponent(this);
}
componentWillUnmount() {
// Not mandatory
if (this.navigationEventListener) {
this.navigationEventListener.remove();
}
}
componentDidAppear() {}
}This event can be observed globally as well:
// Subscribe
const screenEventListener = Navigation.events().registerComponentDidAppearListener(({ componentId, componentName, passProps }) => {
});
...
// Unsubscribe
screenEventListener.remove();| Parameter | Description |
|---|---|
| componentId | Id of the appearing component |
| componentName | Registered name used when registering the component with Navigation.registerComponent() |
| passProps | props passed to the component |
componentDidDisappear
Called each time this component disappears from the screen (detached from the view hierarchy).
class MyComponent extends Component {
componentDidMount() {
this.navigationEventListener = Navigation.events().bindComponent(this);
}
componentWillUnmount() {
// Not mandatory
if (this.navigationEventListener) {
this.navigationEventListener.remove();
}
}
componentDidDisappear() {}
}This event can be observed globally as well:
// Subscribe
const screenEventListener = Navigation.events().registerComponentDidDisappearListener(({ componentId, componentName }) => {
});
...
// Unsubscribe
screenEventListener.remove();| Parameter | Description |
|---|---|
| componentId | Id of the disappearing component |
| componentName | Registered name used when registering the component with Navigation.registerComponent() |
registerCommandListener
The commandListener is called whenever a Navigation command (i.e push, pop, showModal etc) is invoked.
// Subscribe
const commandListener = Navigation.events().registerCommandListener((name, params) => {
});
...
// Unsubscribe
commandListener.remove();| Parameter | Description |
|---|---|
| name | The name of the command that was invoked. For example push |
| params | commandId: Each command is assigned a unique IdcomponentId: Optional, the componentId passed to the commandlayout: Optional, if the command invoked created a screen. Slim representation of the component and its options |
registerCommandCompletedListener
Invoked when a command finishes executing in native. If the command contains animations, for example pushed screen animation, the listener is invoked after the animation ends.
// Subscribe
const commandCompletedListener = Navigation.events().registerCommandCompletedListener(({ commandId, completionTime, params }) => {
});
...
// Unsubscribe
commandCompletedListener.remove();| Parameter | Description |
|---|---|
| commandId | Id of the completed command |
| completionTime | Timestamp when the command, and consecutive animations, completed. |
registerModalDismissedListener
Invoked when a modal is dismissed.
// Subscribe
const modalDismissedListener = Navigation.events().registerModalDismissedListener(({ componentId, modalsDismissed }) => {
});
...
// Unsubscribe
modalDismissedListener.remove();| Parameter | Description |
|---|---|
| componentId | Id of the modal |
| modalsDismissed | Number of modals that were dismissed |
registerModalAttemptedToDismissListener(iOS 13+ only)
Invoked only on iOS pageSheet modal when swipeToDismiss flag is set to true and modal was swiped down to dismiss.
// Subscribe
const modalAttemptedToDismissListener = Navigation.events().registerModalAttemptedToDismissListener(({ componentId }) => {
});
...
// Unsubscribe
modalAttemptedToDismissListener.remove();| Parameter | Description |
|---|---|
| componentId | Id of the modal a user is attempting to dismiss |
registerScreenPoppedListener
Invoked when the screen is popped.
// Subscribe
const screenPoppedListener = Navigation.events().registerScreenPoppedListener(({ componentId }) => {
});
...
// Unsubscribe
screenPoppedListener.remove();| Parameter | Description |
|---|---|
| componentId | Id of the screen which was popped |
registerBottomTabPressedListener
Invoked when a bottom tab is pressed by the user, including when selectTabOnPress is false (the selected tab does not change).
// Subscribe
const bottomTabEventListener = Navigation.events().registerBottomTabPressedListener(({ tabIndex }) => {
});
...
// Unsubscribe
bottomTabEventListener.remove();| Parameter | Description |
|---|---|
| tabIndex | Index of the tab that was pressed |
Works with custom bottom tab items on iOS and Android.
registerBottomTabSelectedListener
Invoked when BottomTab is selected by a user.
// Subscribe
const bottomTabEventListener = Navigation.events().registerBottomTabSelectedListener(({ selectedTabIndex, unselectedTabIndex }) => {
});
...
// Unsubscribe
bottomTabEventListener.remove();| Parameter | Description |
|---|---|
| selectedTabIndex | Index of the newly selected tab |
| unselectedTabIndex | Index of the previously selected tab |
Works with custom bottom tab items on iOS and Android.
registerBottomTabLongPressedListener
Invoked when BottomTab is long pressed by a user. iOS only. Not emitted for custom bottom tab items — long press is handled on the native tab bar, which is hidden when every tab uses bottomTab.component.
// Subscribe
const bottomTabEventListener = Navigation.events().registerBottomTabLongPressedListener(({ selectedTabIndex }) => {
});
...
// Unsubscribe
bottomTabEventListener.remove();navigationButtonPressed event
This event is emitted whenever a TopBar button is pressed by a user.
class MyComponent extends Component {
componentDidMount() {
this.navigationEventListener = Navigation.events().bindComponent(this);
}
componentWillUnmount() {
// Unregistering listeners bound to components isn't mandatory since RNN handles the unregistration for you
if (this.navigationEventListener) {
this.navigationEventListener.remove();
}
}
navigationButtonPressed({ buttonId }) {}
}This event can be observed globally as well:
// Subscribe
const navigationButtonEventListener = Navigation.events().registerNavigationButtonPressedListener(({ buttonId }) => {
});
...
// Unsubscribe
navigationButtonEventListener.remove();| Parameter | Description |
|---|---|
| buttonId | buttonId: id of the pressed button |
searchBarUpdated (iOS 11+ only)
Called whenever the SearchBar of NavigationBar is updated.
class MyComponent extends Component {
componentDidMount() {
this.navigationEventListener = Navigation.events().bindComponent(this);
}
componentWillUnmount() {
// Not mandatory
if (this.navigationEventListener) {
this.navigationEventListener.remove();
}
}
searchBarUpdated({ text, isFocused }) {}
}searchBarCancelPressed (iOS 11+ only)
Called when the cancel button of the SearchBar from NavigationBar is pressed.
class MyComponent extends Component {
componentDidMount() {
this.navigationEventListener = Navigation.events().bindComponent(this);
}
componentWillUnmount() {
// Not mandatory
if (this.navigationEventListener) {
this.navigationEventListener.remove();
}
}
searchBarCancelPressed() {}
}previewCompleted (iOS 11.4+ only)
Called when preview peek is completed.
class MyComponent extends Component {
componentDidMount() {
this.navigationEventListener = Navigation.events().bindComponent(this);
}
componentWillUnmount() {
// Not mandatory
if (this.navigationEventListener) {
this.navigationEventListener.remove();
}
}
previewCompleted({ previewComponentId }) {}
}Api Api Modal (`website/docs/api/api-modal.mdx`)
id: modal title: Modal sidebar_label: Modal
`showModal()`
Show a screen as a modal.
Parameters
| Name | Required | Type | Description |
|---|---|---|---|
| layout | Yes | Layout | Any type of layout. BottomTabs, Stack, SideMenu, Component |
Navigation.showModal({
stack: {
children: [{
component: {
name: 'example.ModalScreen',
passProps: {
text: 'stack with one child'
},
options: {
topBar: {
title: {
text: 'Modal'
}
}
}
}
}]
}
});`dismissModal()`
Dismiss the current modal.
Parameters
| Name | Required | Type | Description |
|---|---|---|---|
| componentId | Yes | string | Any component id presented in the modal |
| mergeOptions | No | Options | Options to be merged before dismissing the Modal. |
Navigation.dismissModal(this.props.componentId);`dismissAllModals()`
Dismiss all current modals at the same time.
Parameters
| Name | Required | Type | Description |
|---|---|---|---|
| mergeOptions | No | Options | Options to be merged before dismissing all modals. |
Navigation.dismissAllModals();Api Api Options (`website/docs/api/api-options.mdx`)
id: options-api title: Options Commands sidebar_label: Options commands
`setDefaultOptions`
Set default options for all screens. Useful for declaring a consistent style across the app.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| options | Options | Yes | Options root |
Example
Navigation.setDefaultOptions({
bottomTab: {
textColor: 'black',
selectedTextColor: 'blue',
},
});`mergeOptions`
Change navigation options of a component or layout.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| componentId | string | Yes | The component or layout id |
| options | Options | Yes | Options root |
Example
Navigation.mergeOptions('componentId', {
bottomTabs: {
visible: false,
},
});Api Api Overlay (`website/docs/api/api-overlay.mdx`)
id: overlay title: Overlay sidebar_label: Overlay
`showOverlay()`
Shows a component as an overlay.
| Name | Required | Type | Description |
|---|---|---|---|
| layout | Yes | Layout | Any type of layout. Typically, Component layout is used in Overlays but it's possible to load any other layout (BottomTabs, Stack, SideMenu, Component) |
Navigation.showOverlay({
component: {
name: 'example.Overlay',
options: {
layout: {
componentBackgroundColor: 'transparent',
},
overlay: {
interceptTouchOutside: true
}
}
}
});`dismissOverlay()`
Dismisses an overlay matching the given overlay componentId.
Parameters
| Name | Required | Type | Description |
|---|---|---|---|
| componentId | Yes | string | The component id presented in Overlay |
Navigation.dismissOverlay(this.props.componentId);`dismissAllOverlays()`
Dismisses all overlays.
Navigation.dismissAllOverlays();Api Api Root (`website/docs/api/api-root.mdx`)
id: root title: Root sidebar_label: Root
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
`setRoot()`
Used to set the UI of the application. Read more about the root hierarchy level in the docs section.
Parameters
| Name | Required | Type | Description |
|---|---|---|---|
| layout | Yes | Layout | Any type of layout. BottomTabs, Stack, SideMenu, Component |
Example
<Tabs
defaultValue='stack'
values={[
{ label: 'With Stack layout', value: 'stack' },
{ label: 'With BottomTabs layout', value: 'bottomTabs' }
]
}>
Navigation.setRoot({
root: {
stack: {
children: [{
component: {
name: 'example.FirstTabScreen',
passProps: {
text: 'This is tab 1'
}
}
}],
options: {
bottomTab: {
text: 'Tab 1',
icon: require('../images/one.png'),
testID: 'FIRST_TAB_BAR_BUTTON'
}
}
}
}
});Navigation.setRoot({
root: {
bottomTabs: {
children: [{
stack: {
children: [{
component: {
name: 'example.FirstTabScreen',
passProps: {
text: 'This is tab 1'
}
}
}],
options: {
bottomTab: {
text: 'Tab 1',
icon: require('../images/one.png'),
testID: 'FIRST_TAB_BAR_BUTTON'
}
}
}
},
{
component: {
name: 'navigation.playground.TextScreen',
passProps: {
text: 'This is tab 2'
},
options: {
bottomTab: {
text: 'Tab 2',
icon: require('../images/two.png'),
testID: 'SECOND_TAB_BAR_BUTTON'
}
}
}
}]
}
}
});Api Api SideMenu (`website/docs/api/api-sideMenu.mdx`)
id: sideMenu-api title: Side Menu sidebar_label: Side Menu
This layout allows to implement sidemenus, which can be opened by swiping from one side towards the other side.
{
left: {
component: {}
},
center: {
stack: {
options: {},
children: [{
component: {}
}]
}
},
right: {
component: {}
}
}`center: Layout`
Center component, contains the main application.
`left?: Layout`
Contain the left component layout.
`right?: Layout`
Contain the right component layout.
Api Api Stack (`website/docs/api/api-stack.mdx`)
id: stack title: Stack sidebar_label: Stack
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
`push()`
Push a screen into the stack and update the display according to the screen options.
Parameters
| Name | Required | Type | Description |
|---|---|---|---|
| componentId | Yes | string | The componentId of a screen pushed into the stack, or the id of the stack. |
| layout | No | Layout | The layout being pushed into the stack. Any type of layout (except stack) can be pushed into stacks. Typically, Component layout is pushed into stacks but it's possible to push SideMenu or BottomTabs as well. |
Example
<Tabs
defaultValue='component'
values={[
{ label: 'Component', value: 'component', },
{ label: 'Update options on push', value: 'push', },
{ label: 'Push other layouts', value: 'otherLayout', },
]
}>
Navigation.push(this.props.componentId, {
component: {
name: 'example.PushedScreen'
}
});Navigation.push(this.props.componentId, {
component: {
name: 'example.PushedScreen',
options: {
topBar: {
title: {
text: 'Pushed screen title'
}
}
}
}
});Navigation.push(this.props.componentId, {
sideMenu: {
left: {
component: {
name: 'drawerScreen'
}
},
center: {
component: {
name: 'centerScreen'
}
}
}
});`pop()`
Pop the top screen from the stack.
Parameters
| Name | Required | Type | Description |
|---|---|---|---|
| componentId | Yes | string | The componentId of a screen pushed into the stack, or the stack id. |
| mergeOptions | No | Options | Options to be merged before popping the screen (optional). |
Navigation.pop(this.props.componentId);`popToRoot()`
Pop all screens pushed into the stack.
Parameters
| Name | Required | Type | Description |
|---|---|---|---|
| componentId | Yes | string | The componentId of a screen pushed into the stack, or the stack id. |
| mergeOptions | No | Options | Options to be merged before popping the screen (optional). |
Navigation.popToRoot(this.props.componentId);`popTo()`
Pop the stack to a given component.
Parameters
| Name | Required | Type | Description |
|---|---|---|---|
| componentId | Yes | string | The destination componentId |
| mergeOptions | No | Options | Options to be merged before popping the screen (optional). |
Navigation.popTo(componentId);`setStackRoot()`
Reset the stack to the given layout (accepts multiple children).
Parameters
| Name | Required | Type | Description |
|---|---|---|---|
| componentId | Yes | string | The componentId of a screen pushed into the stack, or the stack id. |
| layout | Yes | Layout or Layout[] | A single Component or array of components. |
Example
<Tabs
defaultValue='single'
values={[
{ label: 'Single child', value: 'single' },
{ label: 'Multiple children', value: 'multiple' }
]
}>
Navigation.setStackRoot(this.props.componentId, {
component: {
name: 'example.NewRootScreen'
}
});In the example below we reset the stack with two components. The first one will be the root component and the second (PushedScreen) will be displayed. Pressing the back button (either hardware or software) will pop it, revealing the root component - NewRootScreen.
Navigation.setStackRoot(this.props.componentId, [
{
component: {
name: 'NewRootScreen',
}
},
{
component: {
name: 'PushedScreen',
}
}
]);Api Layout BottomTabs (`website/docs/api/layout-BottomTabs.mdx`)
id: layout-bottomTabs title: Bottom Tabs sidebar_label: Bottom Tabs
A container view for managing a radio-style selection interface, where a selection determines which child view controller to display.
{
id: 'BOTTOM_TABS_LAYOUT',
children: [
{
component: {
id: 'HOME_SCREEN',
name: 'HomeScreen'
}
},
{
stack: {
id: 'PROFILE_TAB',
children: [
{
component: {
id: 'PROFILE_SCREEN',
name: 'ProfileScreen'
}
}
]
}
}
]
}`id`
| Type | Required | Description |
|---|---|---|
| string | No | Unique id used to interact with the view via the Navigation api, usually Navigation.mergeOptions which accepts the componentId as it's first argument. |
`children`
| Type | Required | Description |
|---|---|---|
| Layout[] | YES | Child layouts of any kind. |
`options`
| Type | Required | Description |
|---|---|---|
| Options | No | dynamic options which will apply to all screens in bottomTabs |
Api Layout Component (`website/docs/api/layout-component.mdx`)
id: layout-component title: Component sidebar_label: Component
{
name: "MyRegisteredComponent";
}`name`
| Type | Required | Description |
|---|---|---|
| string | Yes | Key used when registering the component with Navigation.registerComponent. |
`id`
| Type | Required | Description |
|---|---|---|
| string | No | Unique id used to interact with the view via the Navigation api, usually Navigation.mergeOptions which accepts the componentId as it's first argument. |
`options`
| Type | Required | Description |
|---|---|---|
| Options | No | dynamic options for the component |
`alignment`
| Type | Required | Description |
|---|---|---|
| enum('center', 'fill') | No | This option is relevant only to title components. fill will make the component stretch and consume all available space in the TopBar while center will center it in the middle of the TopBar. center is the default option in iOS while fill is the default for Android. |
`waitForRender`
| Type | Required | Description |
|---|---|---|
| boolean | No | Wait for this component to fully render before showing the screen. |
This option is useful for ensuring that both a child screen pushed into the stack and all of the TopBar components (title, background and buttons) are displayed to the user at the same time.
To enable this option, waitForRender in the relevant screen animation option needs to be enabled as well.
:::caution
This option might introduce delays when pushing screens and should be used with caution.
:::
`passProps`
| Type | Required | Description |
|---|---|---|
| object | No | A JavaScript object with props accessible inside the component using this.props. |
Api Layout Layout (`website/docs/api/layout-layout.mdx`)
id: layout-layout title: Layout sidebar_label: Layout
`id`
| Type | Required | Description |
|---|---|---|
| string | No | Unique id used to interact with the view via the Navigation API, usually Navigation.mergeOptions which accepts the componentId as it's first argument. |
`options`
| Type | Required | Description |
|---|---|---|
| Options | No | dynamic options for the layout |
`children`
| Type | Required | Description |
|---|---|---|
| Layout[] | Yes | Child layouts of any kind. |
Api Layout SideMenu (`website/docs/api/layout-sideMenu.mdx`)
id: layout-sideMenu title: Side Menu sidebar_label: Side Menu
This layout allows implementing sidemenus, which can be opened by swiping from one side towards the other side.
{
left: {
component: {}
},
center: {
stack: {
options: {},
children: [{
component: {}
}]
}
},
right: {
component: {}
}
}`center`
| Type | Required | Description |
|---|---|---|
| Layout | Yes | Center component, contains the main application. |
`left`
| Type | Required | Description |
|---|---|---|
| Layout | No | Contains the left component layout. |
`right`
| Type | Required | Description |
|---|---|---|
| Layout | No | Contains the right component layout. |
Api Layout SplitView (`website/docs/api/layout-splitView.mdx`)
id: layout-splitView title: SplitView sidebar_label: SplitView
A container view controller implementing a master-detail interface. See UISplitViewController docs.
Currently implemented only in iOS.
{
id: 'PROFILE_TAB',
master: {
component: {
id: 'MASTER_SCREEN',
name: 'MasterScreen'
}
},
detail: {
component: {
id: 'DETAIL_SCREEN',
name: 'DetailScreen'
}
}
}`id`
| Type | Required | Description |
|---|---|---|
| string | No | Unique id used to interact with the view via the Navigation api, usually Navigation.mergeOptions which accepts the componentId as it's first argument. |
`master`
| Type | Required | Description |
|---|---|---|
| Layout | YES | Set master layout (the smaller screen, sidebar) |
`detail`
| Type | Required | Description |
|---|---|---|
| Layout | YES | Set detail layout (the larger screen, flexes) |
`options`
| Type | Required | Description |
|---|---|---|
| Options | No | dynamic options which will apply to all screens |
Api Layout Stack (`website/docs/api/layout-stack.mdx`)
id: layout-stack title: Stack sidebar_label: Stack
A stack is a container layout promoting a hierarchical navigation. It is used to navigate between screens at consecutive levels of hierarchy, steps in a flow or across an app.
{
id: 'PROFILE_TAB',
children: [
{
component: {
id: 'PROFILE_SCREEN',
name: 'ProfileScreen'
}
}
]
}`id`
| Type | Required | Description |
|---|---|---|
| string | No | Unique id used to interact with the view via the Navigation api, usually Navigation.mergeOptions which accepts the componentId as it's first argument. |
`children`
| Type | Required | Description |
|---|---|---|
| Layout[] | YES | Child layouts of any kind. |
`options`
| Type | Required | Description |
|---|---|---|
| Options | No | Options that will apply to all screens in stack |
Api Options Animations (`website/docs/api/options-animations.mdx`)
id: options-animations title: Animations sidebar_label: Animations
Api Options BackButton (`website/docs/api/options-backButton.mdx`)
id: options-backButton title: Back Button Options sidebar_label: Back Button
Controls the back button styling.
const options = {
topBar: {
backButton: {},
},
};`accessibilityLabel`
Overrides the text that's read by the screen reader when the user interacts with the back button.
| Type | Required | Platform |
|---|---|---|
| string | No | Android |
`id`
Button id for reference press event.
| Type | Required | Platform |
|---|---|---|
| string | No | Android |
`color`
Change the back button color. This will change the text color as well.
| Type | Required | Platform |
|---|---|---|
| color | No | Both |
`icon`
Change the default back button icon.
| Type | Required | Platform |
|---|---|---|
| ImageResource | No | Both |
`sfSymbol`
SF Symbol to show as the back button
| Type | Required | Platform |
|---|---|---|
| string | No | iOS 13+ |
`showTitle`
Show or hide the text displayed next to the back button.
| Type | Required | Platform |
|---|---|---|
| number | No | iOS |
`displayMode`
Set display mode of the back button. Currently works for iOS 14+ only.
See: UINavigationItem.BackButtonDisplayMode
| Type | Required | Platform |
|---|---|---|
| enum('default', 'generic', 'minimal') | No | iOS |
`title`
Change the text displayed next to the title. Usually the back button shows the title of the previous screen.
| Type | Required | Platform |
|---|---|---|
| string | No | iOS |
`visible`
Hide or show the back button.
| Type | Required | Platform |
|---|---|---|
| boolean | No | Both |
`testID`
Used to interact with the back button in e2e tests.
| Type | Required | Platform |
|---|---|---|
| string | No | Both |
`enableMenu`
Enables iOS 14 back button menu display.
| Type | Required | Platform | Default |
|---|---|---|---|
| boolean | No | iOS | true |
`popStackOnPress`
Controls whether the default back button should pop screen or not.
| Type | Required | Default |
|---|---|---|
| boolean | No | true |
`hideSharedBackground`
Hide the shared Liquid-Glass / Platter background that UIKit draws behind the back button starting with iOS 26. RNN defaults this to true when the back button uses an iconBackground so the system Platter does not double-decorate it. Set to false to keep the system-drawn background.
| Type | Required | Platform | Default |
|---|---|---|---|
| boolean | No | iOS 26+ | true |
Api Options Background (`website/docs/api/options-background.mdx`)
id: options-background title: Background Options sidebar_label: Background
Controls the top bar background styling.
const options = {
topBar: {
background: {}
}
};`color`
Set the background color. Ignored if a component is specified.
| Type | Required | Platform |
|---|---|---|
| Color | No | Both |
`component`
Set a react component as the background. Useful when you need to show a gradient as background, for instance.
On Android, setting an id to the Component will prevent the component from being recreated each time it's used by a screen. The component will be created once and whenever possible it will be reused.
| Type | Required | Platform |
|---|---|---|
| Component | No | Both |
`clipToBounds`
Clip the top bar background to bounds if set to true.
| Type | Required | Platform |
|---|---|---|
| boolean | No | iOS |
`translucent`
Allows the NavBar to be translucent (blurred).
| Type | Required | Platform |
|---|---|---|
| boolean | No | iOS |
`blur`
Enable background blur.
| Type | Required | Platform |
|---|---|---|
| boolean | No | iOS |
Api Options BottomTab (`website/docs/api/options-bottomTab.mdx`)
id: options-bottomTab title: Bottom Tab Options sidebar_label: Bottom Tab
const options = {
bottomTab: {},
};Recommended: Custom bottom tabs guide for React tab items and examples.
`component`
Render a registered React component as the tab item content (icon + label area).
| Type | Required | Platform |
|---|---|---|
| object | No | Both |
bottomTab: {
component: {
name: 'MyTabItem',
passProps: { /* optional, merged at creation */ },
},
}Requirements: Every tab in the same bottomTabs layout must declare component, or RNN falls back to native rendering for all tabs.
Props delivered to the component: componentId, tabIndex, selected, badge (updated when selection/badge changes).
Ignored when set: text, icon, selectedIcon, sfSymbol, sfSelectedSymbol, iconColor, selectedIconColor, iconWidth, iconHeight, iconInsets, fontFamily, fontWeight, fontSize, selectedFontSize, textColor, selectedTextColor.
Native still owns selection, visibility, drawBehind, animations, and dotIndicator. Pair with bottomTabs.customRow to style the floating row.
Tab press and selection events match native bottom tabs; long press is not available on custom items. See Custom bottom tabs — Events.
`selectTabOnPress`
| Type | Required | Platform |
|---|---|---|
| boolean | No | Both |
`popToRoot`
Pop to root of stack by tapping on already selected tab.
| Type | Required | Platform |
|---|---|---|
| boolean | No | Android |
`badge`
| Type | Required | Platform |
|---|---|---|
| string | No | Both |
`badgeColor`
| Type | Required | Platform |
|---|---|---|
| color | No | Both |
`animateBadge`
| Type | Required | Platform |
|---|---|---|
| boolean | No | Android |
`disableIconTint`
| Type | Required | Platform |
|---|---|---|
| boolean | No | Both |
`dotIndicator`
| Type | Required | Platform |
|---|---|---|
| DotIndicator | No | Both |
`fontFamily`
| Type | Required | Platform |
|---|---|---|
| string | No | Both |
`fontStyle`
| Type | Required | Platform |
|---|---|---|
| enum('normal', 'italic') | No | Both |
`fontWeight`
| Type | Required | Platform |
|---|---|---|
| enum('normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900') | No | Both |
`fontSize`
| Type | Required | Platform |
|---|---|---|
| number | No | Both |
`icon`
| Type | Required | Platform |
|---|---|---|
| ImageResource | No | Both |
`iconWidth`
The width (in dp) of the icon.
| Type | Required | Platform |
|---|---|---|
| number | No | Android |
`iconHeight`
The height (in dp) of the icon.
| Type | Required | Platform |
|---|---|---|
| number | No | Android |
`iconColor`
| Type | Required | Platform |
|---|---|---|
| color | No | Both |
`selectedFontSize`
| Type | Required | Platform |
|---|---|---|
| number | No | Both |
`selectedIcon`
| Type | Required | Platform |
|---|---|---|
| ImageResource | No | Both |
`selectedIconColor`
| Type | Required | Platform |
|---|---|---|
| color | No | Both |
`iconInsets`
| Type | Required | Platform |
|---|---|---|
| IconInsets | No | Both |
`disableSelectedIconTint`
| Type | Required | Platform |
|---|---|---|
| boolean | No | Android |
`disableIconTint`
| Type | Required | Platform |
|---|---|---|
| boolean | No | Android |
`selectedTextColor`
| Type | Required | Platform |
|---|---|---|
| color | No | Both |
`testID`
| Type | Required | Platform |
|---|---|---|
| string | No | Both |
`accessibilityLabel`
Overrides the text that's read by the screen reader when the user interacts with the bottomTab button.
| Type | Required | Platform |
|---|---|---|
| string | No | iOS |
`text`
| Type | Required | Platform |
|---|---|---|
| string | No | Both |
`textColor`
| Type | Required | Platform |
|---|---|---|
| color | No | Both |
`sfSymbol`
Set the SF symbol as icon (will be used primarily).
| Type | Required | Platform |
|---|---|---|
| string | No | iOS 13+ |
`sfSelectedSymbol`
Set the SF symbol as selected icon (will be used primarily)
| Type | Required | Platform |
|---|---|---|
| string | No | iOS 13+ |
DotIndicator
color?: color
size?: number
visible?: boolean
animate?: boolean
IconInsets
top?: number
left?: number
right?: number
bottom?: number
Api Options BottomTabs (`website/docs/api/options-bottomTabs.mdx`)
id: options-bottomTabs title: Bottom Tabs Options sidebar_label: Bottom Tabs
Recommended: Read about Bottom Tabs in the Bottom Tabs Guide. For React tab items, see Custom bottom tabs.
const options = {
bottomTabs: {},
};`customRow`
Visual options for the floating row that hosts custom React tab cells. Only applies when every tab uses bottomTab.component.
| Type | Required | Platform |
|---|---|---|
| BottomTabsCustomRowOptions | No | Both |
bottomTabs: {
customRow: {
height: 67,
backgroundEffect: 'glass',
cornerRadius: 28,
horizontalMargin: 16,
bottomMargin: 0,
},
}On Android, customRow is forwarded from JS via RNNBottomTabsCustomRowModule (no change to existing BottomTabs layout code). On iOS, options are parsed natively from the layout tree.
BottomTabsCustomRowOptions
`height`
Content height of the row in points (iOS) / dp (Android). Excludes safe-area inset.
| Type | Required | Platform |
|---|---|---|
| number | No | Both |
Default: native tab content height; on iOS 26+ an extra 18pt is added when height is omitted.
Total visible row height = height + safe-area bottom + bottomMargin.
`backgroundColor`
Solid row background. When set, overrides backgroundEffect.
| Type | Required | Platform |
|---|---|---|
| color | No | Both |
`backgroundEffect`
| Type | Required | Platform |
|---|---|---|
enum(glass, blur, none) |
No | Both |
| Value | iOS | Android |
|---|---|---|
glass |
UIGlassEffect on iOS 26+, blur fallback |
Semi-opaque material chrome |
blur |
UIBlurEffect systemChromeMaterial |
Semi-opaque material chrome |
none |
Transparent | Transparent |
Default: glass on iOS 26+, blur on older iOS.
`cornerRadius`
Corner radius of the row background.
| Type | Required | Platform |
|---|---|---|
| number | No | Both |
Default: 28 on iOS 26+, 0 on older iOS / when omitted on Android until configured.
`horizontalMargin`
Horizontal inset from the screen edges.
| Type | Required | Platform |
|---|---|---|
| number | No | Both |
Default: 16 on iOS 26+, 0 when omitted.
`bottomMargin`
Distance between the row's bottom edge and the safe-area bottom.
| Type | Required | Platform |
|---|---|---|
| number | No | Both |
Default: 0.
`animate`
Controls whether toggling visibility states will be animated.
| Type | Required | Platform |
|---|---|---|
| boolean | No | Both |
`animateTabSelection`
Controls wether tab selection is animated or not
| Type | Required | Platform |
|---|---|---|
| boolean | No | Android |
`backgroundColor`
Specify the background color.
On Android - also applicable when translucence is applied, but a semi-transparent color should be used (e.g. rgba(255, 0, 0, 0.25)).
| Type | Required | Platform |
|---|---|---|
| color | No | Both |
`barStyle`
Controls whether the BottomTabs use dark (black) or light (default) visual style. Requires translucent: true.
| Type | Required | Platform |
|---|---|---|
| enum('default','black') | No | Both |
`layoutStyle`
Control the way the bottom tabs are laid out.
stretch: Fill the entire width of the screen.compact: Occupy the minimum width needed to hold tab buttons. Recommended for usage in conjunction withdrawBehind: true.
| Type | Required | Platform |
|---|---|---|
| enum('stretch','compact') | No | Android |
`currentTabId`
Select a tab by the id (componentId) of a child contained in it. See Selecting tabs programmatically for a detailed explanation.
| Type | Required | Platform |
|---|---|---|
| number | No | Both |
`currentTabIndex`
| Type | Required | Platform |
|---|---|---|
| number | No | Both |
`drawBehind`
| Type | Required | Platform |
|---|---|---|
| boolean | No | Both |
`elevation`
| Type | Required | Platform |
|---|---|---|
| number | No | Android |
`cornerRadius`
Specify a corner-radius in order to apply round corners to the tabs container.
Mainly suitable when used in conjunction with layoutStyle: 'compact'
| Type | Required | Platform |
|---|---|---|
| number(dip) | No | Android |
`bottomMargin`
Bottom-margin to set in order to enable a "hover" effect.
Works best when used in conjunction with layoutStyle: 'compact' and drawBehind: true.
| Type | Required | Platform |
|---|---|---|
| number(dip) | No | Android |
`hideShadow`
| Type | Required | Platform |
|---|---|---|
| boolean | No | iOS |
`hideOnScroll`
Hides the BottomTabs on scroll to increase the amount of content visible to the user.
The options requires that the scrollable view will be the root view of the screen and that it specifies nestedScrollEnabled: true.
| Type | Required | Platform |
|---|---|---|
| boolean | No | Android |
`preferLargeIcons`
| Type | Required | Platform |
|---|---|---|
| boolean | No | Android |
`tabsAttachMode`
| Type | Required | Platform |
|---|---|---|
| enum('together','afterInitialTab','onSwitchToTab') | No | Both |
`testID`
| Type | Required | Platform |
|---|---|---|
| string | No | Both |
`titleDisplayMode`
| Type | Required | Platform |
|---|---|---|
| enum('alwaysShow','showWhenActive','alwaysHide') | No | Both |
`translucent`
A Boolean value that indicates whether the BottomTabs should be translucent (and background-blurred).
For this option to function properly the screen needs to be drawn behind the BottomTabs; Make sure the list component you're using (FlatList for example) is the root component in the screen and not SafeAreaView. When SafeAreView is the root component it shifts the content above the BottomTabs and the translucent effect isn't visible.
Note: On Android, the option is experimental and should be enabled using a native feature-toggle called TAB_BAR_TRANSLUCENCE (pass it to your NavigationApplication class' c'tor).
| Type | Required | Platform |
|---|---|---|
| boolean | No | iOS - Full supportAndroid - Experimental |
`blurRadius`
Set a custom radius to be used in the blur effect. Higher is blurrier, but also more CPU-intensive.
Note: The blurring is performed following a bitmap downscale of x4.0, so ultimately the actual radius is (4*blurRadius).
| Type | Required | Platform |
|---|---|---|
| number | No | Android |
`visible`
| Type | Required | Platform |
|---|---|---|
| boolean | No | Both |
Api Options Button (`website/docs/api/options-button.mdx`)
id: options-button title: Button Options sidebar_label: Button
const options = {
topBar: {
leftButtons: [
{
id: 'id',
text: 'Button',
},
],
},
};`allCaps`
This option will set whether characters are all capitalized or not.
| Type | Required | Platform | Default |
|---|---|---|---|
| boolean | No | Android | true |
`fontSize`
If the button has text, this option is used to set font size in DP.
| Type | Required | Platform |
|---|---|---|
| number | No | Both |
`fontFamily`
| Type | Required | Platform |
|---|---|---|
| string | No | Both |
`fontStyle`
| Type | Required | Platform |
|---|---|---|
| enum('normal', 'italic') | No | Both |
`fontWeight`
| Type | Required | Platform |
|---|---|---|
| enum('normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900') | No | Both |
`id`
Buttons are identified by their id property. When a button is clicked, a buttonPress event is emitted to js, containing the id of the clicked button.
| Type | Required | Platform |
|---|---|---|
| string | Yes | Both |
`icon`
Button icon. If the button is pushed to the overflow menu, the button text is used instead.
| Type | Required | Platform |
|---|---|---|
| ImageResource | No | Both |
`sfSymbol`
SF Symbol to show as the back button
| Type | Required | Platform |
|---|---|---|
| string | No | iOS 13+ |
`text`
Button text. Ignored if an icon is specified, unless the button is displayed in the overflow menu.
| Type | Required | Platform |
|---|---|---|
| string | No | Both |
`showAsAction`
| Type | Required | Platform |
|---|---|---|
| enum('always', 'never', 'withText', 'ifRoom') | No | Android |
- ifRoom - Only add button to the TopBar if there is room for it, otherwise add it to the overflow menu.
- never - Never place this button in the TopBar. Instead, list the button in the overflow menu.
- always - Always place this button in the app bar.
`component`
Set a react component as this button's view which will be displayed instead of the regular view.
| Type | Required | Platform |
|---|---|---|
| Component | No | Both |
`iconInsets`
IconInsets are applied to the icon to translate its original position on the screen.
| Type | Required | Platform |
|---|---|---|
| IconInsets | No | iOS |
`systemItem`
System icon; ignored if an icon is specified. For more information, see apple's guidelines.
| Type | Required | Platform |
|---|---|---|
| enum('done', 'cancel', 'edit', 'save', 'add', 'flexibleSpace', 'fixedSpace', 'compose', 'reply', 'action', 'organize', 'bookmarks', 'search', 'refresh', 'stop', 'camera', 'trash', 'play', 'pause', 'rewind', 'fastForward', 'undo', 'redo') | No | iOS |
`iconBackground`
iconBackground is applied to the icon's background.
| Type | Required | Platform |
|---|---|---|
| iconBackground | No | Both |
`hideSharedBackground`
Hide the shared Liquid-Glass / Platter background that UIKit draws behind every bar button item starting with iOS 26. RNN defaults this to true for buttons that paint their own chrome (custom react component views and icons with iconBackground) so the system Platter does not double-decorate them. Set to false to keep the system-drawn background.
| Type | Required | Platform | Default |
|---|---|---|---|
| boolean | No | iOS 26+ | true |
Api Options Fab (`website/docs/api/options-fab.mdx`)
id: options-fab title: Fab sidebar_label: Fab
Controls the FAB button styling on Android.
const options = {
fab: {
id: 'id',
icon: require('path-to-fab-icon'),
backgroundColor: 'coral',
actions: [
{
id: 'id2',
icon: require('path-to-fab-icon-2),
}
]
},
};`id`
Button id for reference press event.
:::info Note
This option is mandatory when you are initiating the FAB button. However you do not need to pass in id when updating the Fab button options with Navigation.mergeOptions.
:::
| Type | Required | Platform |
|---|---|---|
| string | No | Android |
`icon`
Change the default back button icon.
| Type | Required | Platform |
|---|---|---|
| number | No | Android |
`iconColor`
Change the icon color of the FAB button.
| Type | Required | Platform |
|---|---|---|
| color | No | Android |
`backgroundColor`
Change the background color of the FAB button.
| Type | Required | Platform |
|---|---|---|
| color | No | Android |
`clickColor`
Change the on-clicked background color of the FAB button.
| Type | Required | Platform |
|---|---|---|
| color | No | Android |
`rippleColor`
Change the ripple color of the FAB button.
| Type | Required | Platform |
|---|---|---|
| color | No | Android |
`visible`
Show or hide the FAB button.
| Type | Required | Platform |
|---|---|---|
| boolean | No | Android |
`alignHorizontally`
Set the horizontal position of the FAB button on the screen.
| Type | Required | Platform |
|---|---|---|
| 'left' or 'right' | No | Android |
`hideOnScroll`
Hide the FAB button on scroll.
| Type | Required | Platform |
|---|---|---|
| boolean | No | Android |
`size`
Set the size of the FAB button.
| Type | Required | Platform |
|---|---|---|
| 'mini' or 'regular' | No | Android |
Api Options HardwareBackButton (`website/docs/api/options-hardwareBackButton.mdx`)
id: options-hardwareBackButton title: Hardware Back Button Options sidebar_label: Hardware Back Button
Controls Android hardware back button.
const options = {
hardwareBackButton: {},
};`dismissModalOnPress`
Controls whether the hardware back button should dismiss modal or not.
| Type | Required | Default | Platform |
|---|---|---|---|
| boolean | No | true | Android |
`popStackOnPress`
Controls whether the hardware back button should pop stacks or not.
| Type | Required | Default | Platform |
|---|---|---|---|
| boolean | No | true | Android |
`bottomTabsOnPress`
Controls hardware back button bottom tab selection behaviour,first will jump to first tab,previous will do reverse selection,exit (Default) will just exit the bottom tabs.
| Type | Required | Default | Platform |
|---|---|---|---|
| enum('exit', 'first', 'previous') | No | 'exit' | Android |
Api Options IconBackground (`website/docs/api/options-iconBackground.mdx`)
id: options-iconBackground title: iconBackground Options sidebar_label: iconBackground
`color`
Set the background color.
| Type | Required | Platform |
|---|---|---|
| Color | No | Both |
`width`
Set the background width.
| Type | Required | Platform |
|---|---|---|
| number | No | Both |
`height`
Set the background height.
| Type | Required | Platform |
|---|---|---|
| number | No | Both |
`cornerRadius`
Set the background cornerRadius.
| Type | Required | Platform |
|---|---|---|
| number | No | Both |
Api Options IconInsets (`website/docs/api/options-iconInsets.mdx`)
id: options-iconInsets title: IconInsets Options sidebar_label: IconInsets
`top`
Configure top inset
| Type | Required | Platform |
|---|---|---|
| number | No | Both |
`bottom`
Configure bottom inset
| Type | Required | Platform |
|---|---|---|
| number | No | Both |
`left`
Configure left inset
| Type | Required | Platform |
|---|---|---|
| number | No | Both |
`right`
Configure right inset
| Type | Required | Platform |
|---|---|---|
| number | No | Both |
Api Options ImageResource (`website/docs/api/options-imageResource.mdx`)
id: options-imageResource title: Image Resource sidebar_label: Image Resource
`ImageResource`
const options = {
topBar: {
leftButtons: [
icon: require('./backButton.png')
]
},
};The following icon types are available:
| Type | Required | Platform |
|---|---|---|
| ImageRequireSource | No | Both |
| string | No | Both |
| ImageSystemSource | No | iOS |
`ImageSystemSource`
`system`
You can use platform specific system icons for iOS only.
Example:
const options = {
topBar: {
leftButtons: [
icon: {
system: 'calendar',
fallback: require('./calendar.png')
}
]
},
};| Type | Required | Platform |
|---|---|---|
| string | Yes | iOS 13+ |
`fallback`
| Type | Required | Platform |
|---|---|---|
| FallbackSource | No | iOS 13+ |
`FallbackSource`
For iOS, SF Symbols is available only for iOS 13 and later. For iOS 12 and earlier you should use a fallback icon.
`ImageRequireSource`
| Type | Required | Platform |
|---|---|---|
| number | No | Both |
`string`
| Type | Required | Platform |
|---|---|---|
| number | No | Both |
Api Options LargeTitle (`website/docs/api/options-largeTitle.mdx`)
id: options-largeTitle title: Large Title Options sidebar_label: Large Title
Controls the top bar large title on iOS, available on iOS 11 and above.
const options = {
topBar: {
largeTitle: {},
},
};`visible`
Controls whether the large title is visible or not.
| Type | Required | Platform |
|---|---|---|
| boolean | No | iOS |
`fontSize`
Set the title font size. On Android this value is in sp.
| Type | Required | Platform |
|---|---|---|
| number | No | iOS |
`color`
Large title text color.
| Type | Required | Platform |
|---|---|---|
| Color | No | iOS |
`fontFamily`
Set the large title FontFamily.
| Type | Required | Platform |
|---|---|---|
| FontFamily | No | iOS |
`fontStyle`
| Type | Required | Platform |
|---|---|---|
| enum('normal', 'italic') | No | iOS |
`fontWeight`
| Type | Required | Platform |
|---|---|---|
| enum('normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900') | No | iOS |
Api Options Layout (`website/docs/api/options-layout.mdx`)
id: options-layout title: Layout Options sidebar_label: Layout
const options = {
layout: {},
};`fitSystemWindows`
| Type | Required | Platform |
|---|---|---|
| boolean | No | Both |
`backgroundColor`
Set the screen background color.
| Type | Required | Platform |
|---|---|---|
| Color | No | Both |
`componentBackgroundColor`
Set background color only for components. On Android, using this option instead of backgroundColor helps reduce overdraw.
| Type | Required | Platform |
|---|---|---|
| Color | No | Both |
`orientation`
Set the allowed orientations.
| Type | Required | Platform |
|---|---|---|
| ['portrait', 'landscape'] | No | Both |
`topMargin`
Set the layout top margin.
| Type | Required | Platform |
|---|---|---|
| number | No | Android |
`direction`
Set language direction, only works with DefaultOptions. locale is an Android specific option which sets the direction according to the device locale.
| Type | Required | Platform |
|---|---|---|
| enum('rtl', 'ltr', 'locale') | No | Both |
`autoHideHomeIndicator`
Controls the application's preferred home indicator auto-hiding.
| Type | Required | Platform |
|---|---|---|
| boolean | No | iOS |
Api Options Migration (`website/docs/api/options-migration.mdx`)
Common Options
navBarTextColor
Title text color
topBar: {
title: {
color: 'red'
}
}navBarTextFontSize
Title font size
topBar: {
title: {
fontSize: 18
}
}navBarTextFontFamily
Title font
topBar: {
title: {
fontFamily: 'Helvetica'
}
}navBarBackgroundColor
TopBar background color
topBar: {
background: {
color: 'red'
}
}navBarCustomView
Use a react view as the TopBar's background or use a React view instead of the textual title.
topBar: {
background: {
component: {
name: 'example.CustomTopBarBackground'
}
},
title: {
component: {
name: 'example.CustomTopBarTitle'
}
}
}navBarClipToBounds
Restrict the navbar background color to the navbar, and do not flow behind the status bar.
topBar: {
background: {
component: {
name: 'example.CustomTopBarBackground',
clipToBounds: true
}
},
}navBarComponentAlignment
Align the React view used as the title
topBar: {
title: {
component: {
name: 'example.CustomTopBarTitle',
alignment: 'center' | 'fill'
}
}
}navBarCustomViewInitialProps
Initial props passed to the React component
topBar: {
background: {
component: {
name: 'example.CustomTopBarBackground',
passProps: {}
}
},
title: {
component: {
name: 'example.CustomTopBarTitle',
passProps: {}
}
}
}navBarButtonColor
TopBar button color
topBar: {
rightButtons: [
{
color: 'red'
}
],
leftButtons: [
{
color: 'red'
}
],
backButton: {
color: 'red'
}
}navBarHidden
TopBar visibility. When setting visible: false, you probably want to set drawBehind: true as well. Use animate: false to toggle visibility without animation.
topBar: {
visible: false
}navBarTransparent
Transparent TopBar. Set drawBehind: true to draw the screen behind the transparent TopBar.
topBar: {
background: {
color: 'transparent'
}
}drawUnderNavBar
Draw the screen behind the TopBar, Useful when the TopBar is toggled or transparent
topBar: {
drawBehind: true
}drawUnderTabBar
Draw the screen behind the BottomTabs, Useful when toggling BottomTabs or when the BottomTabs are translucent.
bottomTabs: {
drawBehind: true
}tabBarHidden
BottomTabs visibility.
bottomTabs: {
visible: false
}The BottomTab's visibility can be toggled only on Android using mergeOptions:
Navigation.mergeOptions(this.props.componentId, {
bottomTabs: {
visible: false
}
});On iOS, BottomTab visibility can be changed only when pushing screens. This means that if you'd like to hide BottomTabs when pushing a screen, You'll need to set the property to false in the options passed to the push command or via the static options(passProps) {} api.
statusBarHidden
StatusBar visibility. For android, also set drawBehind: true.
statusBar: {
visible: false
}statusBarTextColorScheme
Theme of text and icons displayed in the StatusBar
statusBar: {
style: 'light' | 'dark'
}navBarSubtitleColor
Subtitle color
topBar: {
subtitle: {
color: 'red'
}
}navBarSubtitleFontFamily
Subtitle font
topBar: {
subtitle: {
fontFamily: 'Helvetica'
}
}navBarSubtitleFontSize
Subtitle font size
topBar: {
subtitle: {
fontSize: 14
}
}screenBackgroundColor
Screen color, visible before the actual React view is rendered
layout: {
backgroundColor: 'red'
}orientation
layout: {
orientation: ['portrait', 'landscape'] // An array of supported orientations
}disabledButtonColor
Button color when enabled: false is used
topBar: {
rightButtons: [
{
disabledColor: 'grey'
}
]
}navBarButtonFontSize
Button font size
topBar: {
rightButtons: [
{
fontSize: 13
}
],
leftButtons: [
{
fontSize: 13
}
]
}navBarLeftButtonFontSize
Left button font size
{
topBar: {
leftButtons: [
{
fontSize: 13
}
]
}
}navBarLeftButtonColor
Left button color
{
topBar: {
leftButtons: [
{
color: 'red'
}
]
}
}navBarLeftButtonFontWeight
Left button font weight
{
topBar: {
leftButtons: [
{
weight: '300'
}
]
}
}navBarRightButtonFontSize
Right button font size
topBar: {
leftButtons: [
{
fontSize: 13
}
]
}navBarRightButtonColor
Right button color
topBar: {
rightButtons: [
{
color: 'red'
}
]
}navBarRightButtonFontWeight
Right button font weight
topBar: {
rightButtons: [
{
weight: '400'
}
]
}modalPresentationStyle
Controls the behavior of screens displayed modally.
Options supported on iOS
overCurrentContext- Content is displayed over the previous screen. Useful for transparent modalsformSheet- Content is centered in the screenpageSheet-Content partially covers the underlying contentoverFullScreen- Content covers the screen, without detaching previous content.fullScreen- Content covers the screen, previous content is detached.popover- Content is displayed in a popover view.
More information on the different styles for iOS can be found on https://developer.apple.com/documentation/uikit/uimodalpresentationstyle
Options supported on Android
overCurrentContext- Content is displayed over the previous screen. Useful for transparent modalsnone- Previous content is detached when the Modal's show animation ends
{
modalPresentationStyle: 'overCurrentContext'
}navBarButtonFontFamily
Button font family
topBar: {
rightButtons: [
{
fontFamily: 'Helvetica'
}
]
}iOS only
navBarHideOnScroll
Hide TopBar when list is scrolled
topBar: {
hideOnScroll: true
}navBarTranslucent
Translucent TopBar, Setting drawBehind: true is required for this property to work as expected.
topBar: {
drawBehind: true,
background: {
translucent: true
}
}navBarNoBorder
Remove TopBar border (hair line)
topBar: {
noBorder: true
}navBarBlur
Blur the area behind the TopBar, Setting drawBehind: true and topBar background transparent: true is required for this property to work as expected.
topBar: {
drawBehind: true,
background: {
blur: true,
transparent: true
}
}rootBackgroundImageName
- iOS: name of image in Images.xcassets
- Android: name of drawable
{
rootBackgroundImage: require('rootBackground.png')
}screenBackgroundImageName
name of image in Images.xcassets
{
backgroundImage: require('background.png')
}statusBarHideWithNavBar
Hide the StatusBar if the TopBar is also hidden
statusBar: {
hideWithTopBar: true
}statusBarBlur
Blur the area behind the StatusBar
statusBar: {
blur: true
}statusBarAnimate
Animate between style changes of the StatusBar
statusBar: {
animate: true
}disabledBackGesture
Disable the back (swipe) gesture used to pop screens
{
popGesture: false
}largeTitle
Use iOS 11 large title
topBar: {
largeTitle: {
visible: true,
fontSize: 30,
color: 'red',
fontFamily: 'Helvetica'
}
}Android Options
topBarElevationShadowEnabled
TopBar elevation in dp. Set this value to 0 to disable the TopBar's shadow.
topBar: {
elevation: 0
}navBarTitleTextCentered
Title alignment
topBar: {
title: {
alignment: 'center'|'fill'
}
}statusBarColor
StatusBar color
statusBar: {
backgroundColor: 'red'
}navigationBarColor
NavigationBar color
navigationBar: {
backgroundColor: 'red'
}drawUnderStatusBar
Draw content behind the StatusBar
statusBar: {
drawBehind: true
}navBarHeight
TopBar height in dp
topBar: {
height: 70
}navBarTopPadding
Content top margin
layout: {
topMargin: 26
}topTabsHeight
TopTabs height
topTabs: {
height: 70
}topBarBorderColor
TopBar border color
topBar: {
borderColor: 'red'
}topBarBorderWidth
TopBar border height
topBar: {
borderHeight: 1.3
}Unsupported options
- disabledSimultaneousGesture
- statusBarTextColorSchemeSingleScreen
- navBarButtonFontWeight
- topBarShadowColor
- topBarShadowOpacity
- topBarShadowOffset
- topBarShadowRadius
- preferredContentSize
- navBarSubTitleTextCentered
- collapsingToolBarImage
- collapsingToolBarCollapsedColor
- navBarTextFontBold
Api Options Modal (`website/docs/api/options-modal.mdx`)
id: options-modal title: Modal Options sidebar_label: Modal
const options = {
swipeToDismiss
};`swipeToDismiss`
Control wether this modal should be dismiss using swipe gesture when the modalPresentationStyle is pageSheet
| Type | Required | Platform | Default |
|---|---|---|---|
| boolean | No | Both | true |
Api Options NavigationBar (`website/docs/api/options-navigationBar.mdx`)
id: options-navigationBar title: Navigation Bar Options sidebar_label: Navigation Bar
import useBaseUrl from '@docusaurus/useBaseUrl';
The Navigation Bar is the area at the bottom of the screen containing Android's three navigation buttons: Back, Home and Recents.
:::info Edge-to-Edge
Starting with Android 15 (API 35), the system enforces edge-to-edge by default. React Native Navigation supports this via a theme opt-in. See the Edge-to-Edge guide for setup instructions. Navigation bar options below work in both edge-to-edge and non-edge-to-edge modes.
:::
An example of a dark navigation bar:
const options = {
navigationBar: {}
};`visible`
Set the navigation bar visibility.
| Type | Required | Platform |
|---|---|---|
| boolean | No | Android |
`backgroundColor`
Set the navigation bar color. When a light background color is used, the color of the navigation bar icons will adapt to a dark color.
| Type | Required | Platform | Default |
|---|---|---|---|
| Color | No | Android | 'black' |
`drawBehind`
Draw screen content behind the system navigation bar while keeping it visible (gesture pill / 3-button bar remain on screen). Use with edge-to-edge enabled in your activity. With backgroundColor: 'transparent', content shows through the nav bar area; with an opaque color, RNN paints a scrim overlay without reserving bottom layout inset.
| Type | Required | Platform |
|---|---|---|
| boolean | No | Android |
Api Options Overlay (`website/docs/api/options-overlay.mdx`)
id: options-overlay title: Overlay sidebar_label: Overlay
Controls overlay options
const options = {
overlay: {
}
}`interceptTouchOutside`
Controls whether touch events outside the bounds of the overlay are to be handled by content behind the overlay. When set to true, touch events will not pass through to the underlying content.
| Type | Required | Platform |
|---|---|---|
| boolean | No | Both |
`handleKeyboardEvents`
Overlays on iOS don't handle keyboard events by default. If your Overlay contains a TextInput component, you'll want to enable this option so that TextInput responds to keyboard events.
| Type | Required | Platform |
|---|---|---|
| boolean | No | iOS |
Api Options Preview (`website/docs/api/options-preview.mdx`)
id: options-preview title: Preview (iOS) sidebar_label: Preview (iOS)
Api Options Root (`website/docs/api/options-root.mdx`)
id: options-root title: The options object sidebar_label: Options object
const options = {
bottomTab,
bottomTabs,
topBar,
layout,
sideMenu,
overlay,
modal,
preview,
navigationBar,
scrollEdgeEffect,
};`bottomTab`
Controls the bottom tab icon, font, color, and more.
| Type | Required | Platform |
|---|---|---|
| BottomTabOptions | No | Both |
`bottomTabs`
Controls the bottom tabs container.
| Type | Required | Platform |
|---|---|---|
| BottomTabsOptions | No | Both |
`topBar`
Controls the Stack top bar styling.
| Type | Required | Platform |
|---|---|---|
| TopBarOptions | No |
`statusBar`
Controls the system status bar styling.
| Type | Required | Platform |
|---|---|---|
| StatusBarOptions | No | Both |
`layout`
| Type | Required | Platform |
|---|---|---|
| LayoutOptions | No | Both |
`sideMenu`
| Type | Required | Platform |
|---|---|---|
| SideMenuOptions | No | Both |
`overlay`
| Type | Required | Platform |
|---|---|---|
| OverlayOptions | No | Both |
`animations`
| Type | Required | Platform |
|---|---|---|
| AnimationsOptions | No | Both |
`modal`
| Type | Required | Platform |
|---|---|---|
| ModalOptions | No | Both |
`preview`
| Type | Required | Platform |
|---|---|---|
| PreviewOptions | No | iOS |
`splitView`
| Type | Required | Platform |
|---|---|---|
| SplitViewOptions | No | iOS |
`fab`
| Type | Required | Platform |
|---|---|---|
| Fab | No | Android |
`modalPresentationStyle`
Configure the presentation style of the modal.
| Type | Required | Platform |
|---|---|---|
| enum('formSheet', 'pageSheet', 'fullScreen', 'overFullScreen', 'overCurrentContext', 'popOver', 'none') | No | Both |
Styles supported on both platforms
- overCurrentContext - Display the modal and do not remove previous content when the show animation ends.
- none - default system presentation style
Styles supported only on iOS
- fullScreen - The view covers the underlying content completely.
- pageSheet - partially cover the underlying content.
- formSheet - display content centered in the screen.
- overFullScreen - display the modal in full screen mode and do not remove previous content when the show animation ends.
- popOver - Center content on screen and dim the content behind it.
The default presentation style is different on each platform.
| iOS | Android |
|---|---|
fullScreenpageSheet |
fullScreen |
`modalTransitionStyle`
Configure the transition style of the modal.
| Type | Required | Platform |
|---|---|---|
| enum('coverVertical', 'crossDissolve', 'flipHorizontal', 'partialCurl') | No | Both |
`popGesture`
Enable or disable swipe back to pop gesture.
| Type | Required | Platform |
|---|---|---|
| boolean | No | iOS |
`navigationButtonEventOnSwipeBack`
Enable NavigationButtonPressed event sending on Swipe-back on pushed VC.
| Type | Required | Platform | Default |
|---|---|---|---|
| boolean | No | iOS | false |
`backgroundImage`
Background image of the screen.
| Type | Required | Platform |
|---|---|---|
| Image | No | iOS |
`rootBackgroundImage`
Background image for the Navigation View.
| Type | Required | Platform |
|---|---|---|
| Image | No | Android |
`blurOnUnmount`
Enable or disable automaticall blur of the focused input, dismissing keyboard on unmount.
| Type | Required | Platform | Default |
|---|---|---|---|
| boolean | No | Android | false |
`scrollEdgeEffect`
Show / hide or change the style of the iOS 26 scroll edge effect on every UIScrollView contained in the screen.
| Type | Required | Platform |
|---|---|---|
| ScrollEdgeEffectOptions | No | iOS 26+ |
`navigationBar`
Enable or disable automaticall blur of the focused input, dismissing keyboard on unmount.
| Type | Required | Platform |
|---|---|---|
| boolean | No | Android |
Api Options ScrollEdgeAppearance (`website/docs/api/options-scrollEdgeAppearance.mdx`)
id: options-scrollEdgeAppearance title: Scroll Edge Appearance Options sidebar_label: Scroll Edge Appearance
Controls the top bar background styling.
const options = {
topBar: {
scrollEdgeAppearance: {
background: {},
active: true,
},
},
};`active`
Since this might be considered as a breaking change (meaning that previous configs might behave different), you'll need to
pass active true/false to activate this option.
| Type | Required | Platform |
|---|---|---|
| boolean | No | iOS |
`color`
Set the background color.
| Type | Required | Platform |
|---|---|---|
| Color | No | iOS |
`translucent`
Allows the Scroll Edge Appearance to be translucent (blurred).
| Type | Required | Platform |
|---|---|---|
| boolean | No | iOS |
`borderColor`
Change the topBar border color.
| Type | Required | Platform |
|---|---|---|
| Color | No | iOS |
`noBorder`
Disables border at the bottom of the TopBar.
| Type | Required | Platform |
|---|---|---|
| boolean | No | iOS |
Api Options ScrollEdgeEffect (`website/docs/api/options-scrollEdgeEffect.mdx`)
id: options-scrollEdgeEffect title: Scroll Edge Effect Options sidebar_label: Scroll Edge Effect
Controls the iOS 26+ UIScrollEdgeEffect (Liquid Glass fade) shown at the edges of scroll views when content scrolls under bars.
Credits: Contributed by @manuhook (PR #8281).
const options = {
scrollEdgeEffect: {
hidden: true,
},
};`hidden`
Hide the scroll edge effect.
| Type | Required | Platform |
|---|---|---|
| boolean | No | iOS 26+ |
`style`
Set the scroll edge effect style.
| Type | Required | Platform | Default |
|---|---|---|---|
| enum('automatic', 'soft', 'hard') | No | iOS 26+ | 'automatic' |
`top` / `bottom` / `left` / `right`
Per-edge override. Values fall back to the global hidden / style. Per-edge values take precedence.
| Type | Required | Platform |
|---|---|---|
{ hidden?: boolean; style?: 'automatic' | 'soft' | 'hard' } |
No | iOS 26+ |
Examples
Hide all edges:
MyScreen.options = {
scrollEdgeEffect: { hidden: true },
};Top visible, bottom hidden:
MyScreen.options = {
scrollEdgeEffect: {
top: { hidden: false },
bottom: { hidden: true },
},
};Dynamic update:
Navigation.mergeOptions(componentId, {
scrollEdgeEffect: { bottom: { hidden: true } },
});Api Options SideMenu (`website/docs/api/options-sideMenu.mdx`)
id: options-sideMenu title: Side Menu Options sidebar_label: Side Menu
const options = {
sideMenu: {
left: {},
right: {},
openGestureMode: 'entireScreen',
},
};`left`
Configure the left side menu.
| Type | Required | Platform |
|---|---|---|
| SideMenuSide | No | Both |
`right`
Configure the right side menu.
| Type | Required | Platform |
|---|---|---|
| SideMenuSide | No | Both |
`openGestureMode`
Configure how a user is allowed to open a drawer using gestures.
| Type | Required | Platform | Default |
|---|---|---|---|
| enum('entireScreen', 'bezel') | No | iOS | 'bezel' |
Api Options SideMenuSide (`website/docs/api/options-sideMenuSide.mdx`)
id: options-sideMenuSide title: Side Menu Side Options sidebar_label: Side Menu Side
const options = {
sideMenu: {
left: {
visible: false,
enabled: true
}
}
};`visible`
Show or hide the side menu.
| Type | Required | Platform |
|---|---|---|
| boolean | No | Both |
`enabled`
Enable or disable the side menu.
| Type | Required | Platform |
|---|---|---|
| boolean | No | Both |
`width`
Set the width of the side menu.
| Type | Required | Platform |
|---|---|---|
| number | No | Both |
`height`
Set the height of the side menu.
| Type | Required | Platform |
|---|---|---|
| number | No | Both |
Api Options SplitView (`website/docs/api/options-splitView.mdx`)
id: options-splitView title: SplitView Options sidebar_label: SplitView
`displayMode`
Master view display mode.
| Type | Required | Default | Platform |
|---|---|---|---|
| enum('auto', 'visible', 'hidden', 'overlay') | No | 'auto' | iOS |
`primaryEdge`
Master view side. Leading is left. Trailing is right.
| Type | Required | Default | Platform |
|---|---|---|---|
| enum('leading', 'trailing') | No | 'leading' | iOS |
`minWidth`
Set the minimum width of master view.
| Type | Required | Platform |
|---|---|---|
| number | No | iOS |
`maxWidth`
Set the maximum width of master view.
| Type | Required | Platform |
|---|---|---|
| number | No | iOS |
`primaryBackgroundStyle`
Set background style of sidebar. Currently works for Mac Catalyst apps only.
See: Optimizing Your iPad App for Mac
| Type | Required | Default | Platform |
|---|---|---|---|
| enum('none', 'sidebar') | No | 'none' | iOS |
Api Options Stack (`website/docs/api/options-stack.mdx`)
id: options-stack title: Top Bar Options sidebar_label: Top Bar
TopBar
const options = {
topBar: {
animate: true,
title: {},
subtitle: {},
backButton: {},
background: {},
},
};`visible`
Determines if TopBar is visible or not.
| Type | Required | Platform |
|---|---|---|
| boolean | No | Both |
`animate`
Determines if changing the TopBar visibility will be animated or not.
| Type | Required | Platform |
|---|---|---|
| boolean | No | Both |
`animateLeftButtons`
Determines if changing the left buttons will be animated.
| Type | Required | Platform | Default |
|---|---|---|---|
| boolean | No | Both | False |
`animateRightButtons`
Determines if changing the right buttons will be animated.
| Type | Required | Platform | Default |
|---|---|---|---|
| boolean | No | Both | False |
`title`
Controls the top bar title.
| Type | Required | Platform |
|---|---|---|
| Title | No | Both |
`subtitle`
Controls the top bar subtitle.
| Type | Required | Platform |
|---|---|---|
| Subitle | No | Both |
`backButton`
Controls the top bar back button.
| Type | Required | Platform |
|---|---|---|
| BackButton | No | Both |
`background`
Controls the top bar background.
| Type | Required | Platform |
|---|---|---|
| Background | No | Both |
`scrollEdgeAppearance`
Controls the appearance settings when the scrollable content reaches the matching edge of the navigation bar.
| Type | Required | Platform |
|---|---|---|
| Scroll Edge Background | No | iOS 13+ |
`barStyle`
Control the TopBar blur style. Requires translucent: true.
| Type | Required | Platform |
|---|---|---|
| enum('default','black') | No | iOS |
`height`
Set TopBar height, in dp units, on Android only. For iOS use largeTitle see Options.
| Type | Required | Platform |
|---|---|---|
| number | No | Android |
`borderColor`
Change the topBar border color.
| Type | Required | Platform |
|---|---|---|
| Color | No | Both |
`borderHeight`
Set the border height of the navbar in dp.
| Type | Required | Platform |
|---|---|---|
| number | No | Android |
`drawBehind`
Controls if child should be drawn behind the TopBar or below it.
| Type | Required | Platform |
|---|---|---|
| boolean | No | Both |
`elevation`
Set the elevation of the TopBar in dp. This option changes how the shadow under the TopBar looks. Setting this value to 0 will remove the shadow completely.
| Type | Required | Platform |
|---|---|---|
| number | No | Android |
`hideOnScroll`
Hide the TopBar when a scrolling layout is scrolled.
| Type | Required | Platform |
|---|---|---|
| boolean | No | Both |
:::warning Deprecation warning
This option is currently deprecated and will be removed in a future release. Please use the OptionsSearchBar option instead.
:::
`hideNavBarOnFocusSearchBar`
Indicates whether the navigation bar should be hidden when searching. True by default.
| Type | Required | Platform |
|---|---|---|
| boolean | No | iOS 11+ |
`leftButtons`
An array of buttons to be displayed at the right-side of the TopBar. Button layout is from left to right. See the Buttons section for more details.
:::info Android support
Android currently only supports a single left button and does not support custom left Buttons.
:::
| Type | Required | Platform |
|---|---|---|
| [Button] | No | Both |
`leftButtonColor`
Default color for left buttons.
| Type | Required | Platform |
|---|---|---|
| Color | No | Both |
`noBorder`
Disables border at the bottom of the TopBar.
:::important
For disabling the border completely, you also need to disable scrollEdgeAppearance.noBorder
:::
| Type | Required | Platform |
|---|---|---|
| boolean | No | iOS |
`rightButtons`
An array of buttons to be displayed at the right side of the TopBar. Button layout is from right to left. See the Buttons section for more details.
| Type | Required | Platform |
|---|---|---|
| [Button] | No | Both |
`rightButtonColor`
Default color for the right button.
| Type | Required | Platform |
|---|---|---|
| Color | No | Both |
`searchBar`
Controls the SearchBar.
| Type | Required | Platform |
|---|---|---|
| OptionsSearchBar | No | iOS 11+ |
:::warning Deprecation warning
This option is currently deprecated and will be removed in a future release. Please use the OptionsSearchBar option instead.
:::
`searchBarHiddenWhenScrolling`
Hides the UISearchBar when scrolling.
| Type | Required | Platform |
|---|---|---|
| boolean | No | iOS 11+ |
:::warning Deprecation warning
This option is currently deprecated and will be removed in a future release. Please use the OptionsSearchBar option instead.
:::
`searchBarPlaceholder`
The placeholder value in the UISearchBar.
| Type | Required | Platform |
|---|---|---|
| string | No | iOS 11+ |
:::warning Deprecation warning
This option is currently deprecated and will be removed in a future release. Please use the OptionsSearchBar option instead.
:::
`searchBarBackgroundColor`
The background color of the UISearchBar's TextField.
| Type | Required | Platform |
|---|---|---|
| Color | No | iOS 13+ |
:::warning Deprecation warning
This option is currently deprecated and will be removed in a future release. Please use the OptionsSearchBar option instead.
:::
`searchBarTintColor`
The tint color of the UISearchBar. Affects text selection color, as well as "Cancel" button color.
| Type | Required | Platform |
|---|---|---|
| Color | No | iOS 13+ |
Api Options StatusBar (`website/docs/api/options-statusBar.mdx`)
id: options-statusBar title: Status Bar Options sidebar_label: Status Bar
const options = {
statusBar: {},
};`visible`
Set the status bar visibility.
| Type | Required | Platform |
|---|---|---|
| boolean | No | Both |
`style`
Set the text color of the status bar.
| Type | Required | Platform | Default |
|---|---|---|---|
| enum('light', 'dark') | No | Both | 'light' |
`backgroundColor`
Set the background color of the status bar.
| Type | Required | Platform |
|---|---|---|
| Color | No | Android |
`drawBehind`
Draw screen behind the status bar.
| Type | Required | Platform |
|---|---|---|
| boolean | No | Android |
`translucent`
Set the status bar to be translucent.
| Type | Required | Platform |
|---|---|---|
| boolean | No | Android |
`animate`
Animate StatusBar style changes.
| Type | Required | Platform |
|---|---|---|
| boolean | No | iOS |
`blur`
Blur content beneath the StatusBar.
| Type | Required | Platform |
|---|---|---|
| boolean | No | iOS |
`hideWithTopBar`
Automatically hide the StatusBar when the TopBar hides.
| Type | Required | Platform |
|---|---|---|
| boolean | No | iOS |
Api Options Subtitle (`website/docs/api/options-subtitle.mdx`)
id: options-subtitle title: Subtitle Options sidebar_label: Subtitle
Controls the top bar subtitle.
const options = {
topBar: {
subtitle: {},
},
};`text`
Subtitle text.
| Type | Required | Platform |
|---|---|---|
| string | No | Both |
`fontSize`
Subtitle fontSize. On Android this value is sp.
| Type | Required | Platform |
|---|---|---|
| number | No | Both |
`color`
Subtitle color.
| Type | Required | Platform |
|---|---|---|
| Color | No | Both |
`fontFamily`
Subtitle FontFamily.
| Type | Required | Platform |
|---|---|---|
| FontFamily | No | Both |
`fontStyle`
| Type | Required | Platform |
|---|---|---|
| enum('normal', 'italic') | No | Both |
`fontWeight`
| Type | Required | Platform |
|---|---|---|
| enum('normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900') | No | Both |
`alignment`
fill will make the subtitle stretch and consume all available space in the TopBar while center will center the subtitle in the middle of the TopBar.
| Type | Required | Platform |
|---|---|---|
| enum('center', 'fill') | No | Both |
`testID`
Used to interact with the TopBar in e2e tests.
| Type | Required | Platform |
|---|---|---|
| string | No | Both |
Api Options Title (`website/docs/api/options-title.mdx`)
id: options-title title: Title Options sidebar_label: Title
Controls the top bar title.
const options = {
topBar: {
title: {},
},
};`text`
Set the title for the TopBar.
| Type | Required | Platform |
|---|---|---|
| string | No | Both |
`fontSize`
Set the title font size. On Android this value is in sp.
| Type | Required | Platform |
|---|---|---|
| number | No | Both |
`color`
Set the title color.
| Type | Required | Platform |
|---|---|---|
| Color | No | Both |
`fontFamily`
Set the title font family.
| Type | Required | Platform |
|---|---|---|
| FontFamily | No | Both |
`fontStyle`
| Type | Required | Platform |
|---|---|---|
| enum('normal', 'italic') | No | Both |
`fontWeight`
| Type | Required | Platform |
|---|---|---|
| enum('normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900') | No | Both |
`alignment`
fill will make the title stretch and consume all available space in the TopBar while center will center the title in the middle of the TopBar.
:::info defaultscenter is the default option on iOS while fill is the default for Android.
:::
| Type | Required | Platform |
|---|---|---|
| enum('center','fill') | No | Both |
`component`
Set a react component as the title. If this option is specified then text is ignored.
| Type | Required | Platform |
|---|---|---|
| Component | No | Both |
`topMargin`
Change to TopBar's top margin.
| Type | Required | Platform |
|---|---|---|
| number | No | Android |
`visible`
Determines whether the TopBar is visible or not.
| Type | Required | Platform |
|---|---|---|
| boolean | No | Both |
Api TopBar SearchBar (`website/docs/api/topBar-searchBar.mdx`)
id: topBar-searchBar title: SearchBar sidebar_label: Layout
`visible`
| Type | Required | Platform | Description |
|---|---|---|---|
| boolean | No | iOS | Determines if SearchBar is visible or not |
`focus`
| Type | Required | Platform | Description |
|---|---|---|---|
| boolean | No | iOS | Auto focuses search bar |
`hideOnScroll`
| Type | Required | Platform | Description |
|---|---|---|---|
| boolean | No | iOS | Hides the UISearchBar when scrolling. |
`hideTopBarOnFocus`
| Type | Required | Platform | Description |
|---|---|---|---|
| boolean | No | iOS | Indicates whether the navigation bar should be hidden when searching. True by default. |
`obscuresBackgroundDuringPresentation`
| Type | Required | Platform | Description |
|---|---|---|---|
| boolean | No | iOS | A Boolean indicating whether the underlying content is obscured during a search. |
`backgroundColor`
| Type | Required | Platform | Description |
|---|---|---|---|
| Color | No | iOS | The background color of the UISearchBar's TextField. |
`tintColor`
| Type | Required | Platform | Description |
|---|---|---|---|
| Color | No | iOS | The tint color of the UISearchBar. Affects text selection color, as well as "Cancel" button color. |
`searchBarPlaceholder`
| Type | Required | Platform | Description |
|---|---|---|---|
| string | No | iOS | The placeholder value in the UISearchBar. |
`cancelText`
| Type | Required | Platform | Description |
|---|---|---|---|
| string | No | iOS | The text value of "Cancel" button in the UISearchBar. |
`placement`
| Type | Required | Platform | Description |
|---|---|---|---|
| string | No | iOS 26 | 'stacked' (default) or 'integrated' where 'integrated' is the new iOS 26 look and feel. |
Docs Community Libraries (`website/docs/docs/community-libraries.mdx`)
id: community-libraries title: Community libraries sidebar_label: Community libraries
React Native Navigation has an active community with a bunch of support libraries.
Utilities
React Native Navigation Hooks :
A set of React hooks for React Native Navigation.
React Native Navigation Register Screens :
Utility function to register array of screens instead of calling Navigation.registerComponent multiple times.
Extensions
React Native Navigation Drawer Extension :
Drawer API built on top of React Native Navigation for iOS and Android.
React Native Navigation Search Bar :
React Native Elements search bar with collapsible header built for React Native Navigation.
React Native Navigation Bottom Sheet Extension :
A performant customizable bottom sheet component built on top of React Native Navigation.
Docs Docs Installing (`website/docs/docs/docs-Installing.mdx`)
id: installing title: Installation sidebar_label: Installation
Requirements
- node ≥ 18.18
- react-native ≥ 0.77
- new architecture enabled
npm or yarn
npm:
npm install --save react-native-navigationyarn:
yarn add react-native-navigationInstalling with `npx rnn-link` (automatic installation)
You can benefit from autolinking for some of the necessary installation steps. But unlike most other libraries, react-native-navigation requires you to make a few changes to native files.
We've simplified the process through a set of scripts. So to make all the necessary changes automatically, in your project's root folder run:
npx rnn-linkMake sure to commit the changes introduced by the rnn-link script.
The automatic linking is optimized for new applications created via the
npx @react-native-community/cli@latest initcommand. If you are migrating from a version of react-native-navigation older than v7, it's recommended to check the steps manually after the script runs.
In iOS, from React-native 0.79 onwards the rnn-link will make the necessary changes only to the AppDelegate.swift file. The Objective-C version of the AppDelegate will need to be changed manually (see below).
If one of the steps failed or you can't run (or are not comfortable with) the automatic scripts, you'll need to complete the relevant steps in the manual installation steps below, for both platforms.
CocoaPods
After the the automatic scripts completed successfully, run pod install:
pod install --project-directory=iosUpdate index.js file
index.js is typically used as an entry point into the app. It's first parsed and executed by the JS engine, therefore we'll want to show our UI from there.
The following diff demonstrates changes needed to be made to index.js, initialized by react-native init.
+import { Navigation } from "react-native-navigation";
-import {AppRegistry} from 'react-native';
import App from "./App";
-import {name as appName} from './app.json';
-AppRegistry.registerComponent(appName, () => App);
+Navigation.registerComponent('com.myApp.WelcomeScreen', () => App);
+Navigation.events().registerAppLaunchedListener(() => {
+ Navigation.setRoot({
+ root: {
+ stack: {
+ children: [
+ {
+ component: {
+ name: 'com.myApp.WelcomeScreen'
+ }
+ }
+ ]
+ }
+ }
+ });
+});Finish
If you followed the steps successfully up to this point, then rebuild your application and you are good to go:
react-native run-ios
react-native run-android:::tip
This is a good moment to build your application in both platforms, validate that everything is working properly and commit your changes. If you're coming from a fresh npx @react-native-community/cli@latest init project, then you should be seeing the Welcome screen as usual, but under the hood your application is using react-native-navigation!
::::
Manual Installation
If installation with npx rnn-link did not work, follow the manual installation steps below.
iOS
Make sure your Xcode is updated. We recommend editing
.h,.mmand.swiftfiles in Xcode as the IDE will usually point out common errors.
Installation with CocoaPods
cd ios && pod installNative Installation
Choose your implementation language: Depending on whether your project uses Swift or Objective-C, follow the appropriate section below. Most new React Native projects use Swift by default.
React-Native 0.77 - 0.78:
For Swift projects
In Xcode, you will need to update this file: AppDelegate.swift
import UIKit
import React
- import React_RCTAppDelegate
+ import ReactNativeNavigation
import ReactAppDependencyProvider
@main
- class AppDelegate: RCTAppDelegate {
+ class AppDelegate: RNNAppDelegate {
...For Objective-C projects
In Xcode, you will need to edit this file: AppDelegate.h. Its content should look like this:
#import <UIKit/UIKit.h>
- #import <RCTAppDelegate.h>
+ #import "RNNAppDelegate.h"
- @interface AppDelegate : RCTAppDelegate
+ @interface AppDelegate : RNNAppDelegate
@endReact-Native 0.79:
For Swift projects
-import React_RCTAppDelegate
+import ReactNativeNavigation
-class AppDelegate: UIResponder, UIApplicationDelegate {
+class AppDelegate: RNNAppDelegate {
-func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool {
+override func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool {
+self.reactNativeDelegate = ReactNativeDelegate()
+super.application(application, didFinishLaunchingWithOptions: launchOptions)
-let delegate = ReactNativeDelegate()
-let factory = RCTReactNativeFactory(delegate: delegate)
-delegate.dependencyProvider = RCTAppDependencyProvider()
-reactNativeDelegate = delegate
-reactNativeFactory = factory
-window = UIWindow(frame: UIScreen.main.bounds)
-factory.startReactNative(
- withModuleName: "RN79",
- in: window,
- launchOptions: launchOptions
-)For Objective-C projects
In Xcode, you will need to edit this file: AppDelegate.h. Its content should look like this:
-@interface AppDelegate: UIResponder, UIApplicationDelegate
+@interface AppDelegate : RNNAppDelegate
-@property (nonatomic, strong) UIWindow *window;
-@property (nonatomic, strong) ReactNativeDelegate *reactNativeDelegate;
-@property (nonatomic, strong) RCTReactNativeFactory *reactNativeFactory;AppDelegate.m
- ReactNativeDelegate *delegate = [[ReactNativeDelegate alloc] init];
- RCTReactNativeFactory *factory = [[RCTReactNativeFactory alloc] initWithDelegate:delegate];
- delegate.dependencyProvider = [[RCTAppDependencyProvider alloc] init];
- self.reactNativeDelegate = delegate;
- self.reactNativeFactory = factory;
- self.window = [[UIWindow alloc] initWithFrame:UIScreen.mainScreen.bounds];
- [factory startReactNativeWithModuleName:@"RN79"
- in:self.window
- launchOptions:launchOptions];
+ self.reactNativeDelegate = [ReactNativeDelegate new];
+ [super application:application didFinishLaunchingWithOptions:launchOptions];Firebase integration on React-Native 0.79:
For Firebase integration with static frameworks the following change needs to be added to your podfile:
podfile
linkage = ENV['USE_FRAMEWORKS']
if linkage != nil
Pod::UI.puts "Configuring Pod with #{linkage}ally linked Frameworks".green
use_frameworks! :linkage => linkage.to_sym
end
+ static_frameworks = [
+ 'FirebaseCore',
+ 'FirebaseCoreExtension',
+ 'GoogleUtilities',
+ 'FirebaseCoreInternal',
+ 'FirebaseInstallations',
+ 'GoogleDataTransport',
+ 'FirebaseFirestoreInternal',
+ 'FirebaseMessagingInternal',
+ 'FirebaseAuth',
+ ]
+ pre_install do |installer|
+ installer.pod_targets.each do |pod|
+ if static_frameworks.include?(pod.name)
+ puts "Configuring #{pod.name} as static framework"
+ def pod.build_type
+ Pod::BuildType.new(:linkage => :static, :packaging => :framework)
+ end
+ end
+ end
+ end
target <ProjectName> do
config = use_native_modules!
+ $RNFirebaseAsStaticFramework = true
+ pod 'FirebaseAuthInterop', :modular_headers => true
+ pod 'FirebaseAppCheckInterop', :modular_headers => true
+ pod 'RecaptchaInterop', :modular_headers => true
...Android
Make sure your Android Studio installation is up to date. We recommend editing
gradleandkotlinfiles in Android Studio as the IDE will suggest fixes and point out errors, this way you avoid most common pitfalls.
1. Update `android/build.gradle`:
buildscript {
ext {
kotlinVersion = "2.0.21"
+ RNNKotlinVersion = kotlinVersion
compileSdkVersion = 35
buildToolsVersion = "35.0.0"
minSdkVersion = 24
targetSdkVersion = 34
ndkVersion = "27.1.12297006"
}
repositories {
google()
mavenCentral()
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
classpath("com.facebook.react:react-native-gradle-plugin")
classpath 'com.android.tools.build:gradle'
}
}
allprojects {
repositories {
google()
maven {
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
url "$rootDir/../node_modules/react-native/android"
}
}
}2. Update `MainActivity.kt`
-import com.facebook.react.ReactActivity
+import com.reactnativenavigation.NavigationActivity
-class MainActivity : ReactActivity() {
- override fun getMainComponentName(): String {
- return "yourproject"
- }
-}
+class MainActivity : NavigationActivity()If you have any react-native related methods, you can safely delete them.
3. Update `MainApplication.kt`
This file is located in android/app/src/main/java/com/<yourproject>/MainApplication.java.
/* Detailed source-code truncated for AI context efficiency. */App root
Now that you're done, don't forget to update the index.js file, as shown above.
Troubleshooting
Build app with gradle command
prefered solution - The RNN flavor you would like to build is specified in app/build.gradle. Therefore in order to compile only that flavor, instead of building your entire project using ./gradlew assembleDebug, you should instruct gradle to build the app module: ./gradlew app:assembleDebug. The easiest way is to add a package.json command to build and install your debug Android APK .
"scripts": {
// ...
"android": "cd ./android && ./gradlew app:assembleDebug && ./gradlew installDebug"
}Now run npm run android to build your application
Force the same support library version across all dependencies
Some of your dependencies might require a different version of one of Google's support library packages. This results in compilation errors similar to this:
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':app:preDebugBuild'.
> Android dependency 'com.android.support:design' has different version for the compile (25.4.0) and runtime (26.1.0) classpath. You should manually set the same version via DependencyResolutionTo resolve these conflicts, add the following to your app/build.gradle:
android {
...
}
configurations.all {
resolutionStrategy.eachDependency { DependencyResolveDetails details ->
def requested = details.requested
if (requested.group == 'com.android.support' && requested.name != 'multidex') {
details.useVersion "${rootProject.ext.supportLibVersion}"
}
}
}
dependencies {
...
implementation 'com.android.support:design:25.4.0'
implementation "com.android.support:appcompat-v7:${rootProject.ext.supportLibVersion}"
}Docs Docs Advanced Navigation (`website/docs/docs/docs-advanced-navigation.mdx`)
id: advanced-navigation title: Advanced navigation sidebar_label: Advanced navigation
import useBaseUrl from '@docusaurus/useBaseUrl';
Tab navigation
As mobile applications become bigger and more complex, they usually end up containing multiple primary high-level destinations, which are logically independent from one another. The BottomTabs layout is often used when an app contains three to five top-level destinations which should be accessible from anywhere in the app.
Lets return to the example code from the previous section (Basic navigation) and see how we can convert it to a tab-based application. We'll use the BottomTabs layout to display tabs at the bottom of the screen. Similarly to Stack layout, The BottomTabs layout also contains a children property where each child will be displayed in a tab.
Lets change our code to the following and reload the app.
/* Detailed source-code truncated for AI context efficiency. */Now our app has two tabs at the bottom of the screen and our users can easily navigate between them.
Replacing the root
Up until now, we've discussed how to navigate within the same layout structure. We'll now learn how to replace the entire layout structure to display a new Root. A real life example for this use case would be, for instance, if you need to switch from a login screen to the app's main root. Replacing the root fits this use case since we'd like to discard the previous root (login root) entirely when switching to the main root.
To demonstrate this, we'll make the following changes to our code:
- Add a login screen with a login button.
- When the button is clicked, we will switch to the main root.
- Replace our current root with the login screen. We'll also extract both roots to variables to improve code readability.
/* Detailed source-code truncated for AI context efficiency. */Conditional roots
Now that our initial root is the Login root, we're facing a new problem - how do we show the Login root only to users that are not logged in? Since our initial root is determined in the registerAppLaunchedListener callback, we'll check if a user is logged in there and set the appropriate root accordingly.
Navigation.events().registerAppLaunchedListener(async () => {
Navigation.setRoot(isLoggedIn() ? mainRoot : loginRoot);
});
function isLoggedIn() {
// TODO: your business logic goes here
}Docs Docs App Launch (`website/docs/docs/docs-app-launch.mdx`)
id: app-launch title: Launching the app sidebar_label: Launching the app
When your app is launched for the first time, the bundle is parsed and executed. At this point you need to display your UI. To do so, listen to the appLaunched event and call Navigation.setRoot when that event is received.
Navigation.events().registerAppLaunchedListener(() => {
// Each time the event is received you should call Navigation.setRoot
});:::important
Register the app launched listener as soon as possible - this should be one of the first lines in your index.js file.
:::
If you're observing a "white screen" or a hanging splash screen after relaunching your app, it probably means Navigation.setRoot isn't called as soon as the app is launched. Perhaps the listener was registered too late.
Difference between the platforms
When your app is launched, RN makes sure JS context (which is what enables you to execute JavaScript code) is running. There are quite a few differences between iOS and Android in this regard.
iOS
Whenever an app is put into the background, the app process can potentially be destroyed by the system. If this destruction takes place, the JS context will be destroyed alongside with the app process.
Android
An Android application is typically bound to two contexts:
- Application context - bound to the app process
- Activity context - bound to app UI
React's JS Context is executed on and bound to the Application context. This means that even when the Activity context (and thus the UI) is destroyed, React's JS Context remains active until the Application (and your process) are destroyed by the system.
:::caution
Because of this, when your app returns to foreground, JS Context might still exist on Android, even when the Activity and UI contexts have been destroyed - meaning you might not need to initialise all of your app logic; instead, you'll only need to call Navigation.setRoot to repopulate the UI context. This circumstance can easily be determined by setting a flag on your app's first appLaunched event, and checking the value of that flag on subsequent appLaunched events -- if the flag is true in your appLaunched callback, you need to call Navigation.setRoot to repopulate the UI.
:::
Docs Docs Basic Navigation (`website/docs/docs/docs-basic-navigation.mdx`)
id: basic-navigation title: Basic navigation sidebar_label: Basic navigation
import useBaseUrl from '@docusaurus/useBaseUrl';
Generally, any mobile app consists of various destinations which display some content to the user. And in vast majority of cases using an app means navigating between these destinations. React-native-navigation provides ways for you to layout your content on user screen in a logical and performant manner.
React Native Navigation's stack layout lets you push screens, and also navigate back to previous screens. Screens pushed into the stack hide the previous screen in the stack, making the user focus on a single screen at a time. Let's look at a stack layout that provides basic navigation for an app.
Creating a stack
// In index.js of a new project
import React from 'react';
import { View, Text, Button, StyleSheet } from 'react-native';
import { Navigation } from 'react-native-navigation';
const HomeScreen = (props) => {
return (
<View style={styles.root}>
<Text>Home Screen</Text>
</View>
);
};
Navigation.registerComponent('Home', () => HomeScreen);
Navigation.events().registerAppLaunchedListener(async () => {
Navigation.setRoot({
root: {
stack: {
children: [
{
component: {
name: 'Home'
}
}
]
}
}
});
});
const styles = StyleSheet.create({
root: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: 'whitesmoke'
}
});Running this code will render a screen with an empty TopBar and your HomeScreen component (shown below). The TopBar is part of the stack layout, we'll work on configuring it later.
Specifying options
You can specify options of each layout (Stack, component pushed into a stack, etc.) to configure various parameters like the TopBar title or color. Lets spice things up by changing the TopBar color and while we're at it, also move the title to the TopBar.
Lets change the Home screen declaration to the following and reload the app to see the changes.
const HomeScreen = (props) => {
return (
<View style={styles.root}>
<Text>Hello React Native Navigation 👋</Text>
</View>
);
};
HomeScreen.options = {
topBar: {
title: {
text: 'Home',
color: 'white'
},
background: {
color: '#4d089a'
}
}
}Our app should now look like this:
Navigating in a stack
In the previous section we created a stack and initialized it with a single child. We'll now learn how to add another child to the stack. From now on, we'll call the action of adding children to the stack 'push', and removing children - 'pop'.
In order to push another screen to the stack, we will add a button to the home screen and call Navigation.push. The 'push' command accepts two parameters, the first is the id used to indicate into which stack to push the screen and the second is the screen we're pushing. After pushing a screen, a back button is added automatically to the TopBar so the users can navigate easily back to the previous screen.
You can read more about the stack layout here or dive right into the API here.
:::info componentId
Each React component registered with Navigation.registerComponent is assigned a unique componentId by Navigation. This unique id is used with Navigation commands (like the push command) to indicate into which stack we'd like to push. In this case, by using the componentId of the Home screen, we are telling Navigation to push into the stack containing the Home screen.
:::
Navigation.push(props.componentId, {
component: {
name: 'Settings', // Push the screen registered with the 'Settings' key
options: { // Optional options object to configure the screen
topBar: {
title: {
text: 'Settings' // Set the TopBar title of the new Screen
}
}
}
}
});Lets change our code to the following snippet below and reload the app. Now our Home should contain a button which when pressed, pushes a new screen into the stack. We've successfully navigated to a new screen! 👏
/* Detailed source-code truncated for AI context efficiency. */App theme
Our app is growing and already contains two screens. This introduces a new problem - while the home screen has a nice purple TopBar, our settings screen seems pretty dull as it still uses the default system look and feel.
Currently, our style declaration applies only to the Home screen, so lets apply it to all of our screens by using Navigation.setDefaultOptions.
Navigation.setDefaultOptions({
statusBar: {
backgroundColor: '#4d089a'
},
topBar: {
title: {
color: 'white'
},
backButton: {
color: 'white'
},
background: {
color: '#4d089a'
}
}
});We need to add this snippet before registering the registerAppLaunchedListener. This way we ensure our theme is applied when our root is set. Our code should now look like this:
/* Detailed source-code truncated for AI context efficiency. */Lets run our code again - now our design is consistent across both screens.
Summary
We've learned the basics of navigation with React Native Navigation by implementing a stack and pushing screens into it.
We've also learned a few methods of applying styles to our screens and layouts with the Options mechanism.
- Navigation.setRoot() sets the root layout of our app. The initial root has to be set from an
AppLaunchedEventcallback. - Options can be applied directly to components.
- Screens can be added to a stack with the
Navigation.push()command. - Components registered with
Navigation.registerComponent()are injected with acomponentIdwhich is used when navigating. - Themes are applied via the
Navigation.setDefaultOptions()command.
In the next section we'll explore a more advance navigation patterns using BottomTabs layout and also see how, and why, multiple roots are set.
Docs Docs Before You Start (`website/docs/docs/docs-before-you-start.mdx`)
id: before-you-start title: Before you start sidebar_label: Before you start
React Native Navigation is a module, dependent on and intended to be used alongside React Native, so some experience with it and knowledge of core concepts is required. If you feel the need, you can start with their getting started tutorial, and then come back here when you're ready.
We also assume you are working on a Mac with XCode and Android Studio installed and setup. You can also make it work in a Linux distribution, of course, but in that case bear in mind that some sections of the docs that deal with iOS might not be relevant to you.
If you want to dig right into it, start with installing the library. If you're just looking around, we suggest checking out basic navigation first.
Docs Docs BottomTabs (`website/docs/docs/docs-bottomTabs.mdx`)
id: bottomTabs title: Bottom tabs sidebar_label: Bottom tabs
import useBaseUrl from '@docusaurus/useBaseUrl';
Bottom tabs refers to a row of links, displayed at the bottom of the screen, and is referred to as "Tab Bar" in iOS and as "Bottom Navigation Bar" on Android. Usually these are intended to be used to switch between top-level content views with a single tap.
Creating bottom tabs
BottomTabs provide flat navigation and access to up to five equally important top-level root destinations. While any type of layout can be displayed in a tab, typically, Stacks are used, since they allow for vertical navigation within a tab.
Example
Lets see how to create a simple BottomTabs layout. There are a few things to notice here:
- Each child represents a tab on the screen.
- The root layout for each tab is a stack to allow for vertical navigation within the tab.
- Each stack declares
bottomTaboptions which are used to configure the tab's text, icon, color etc.
bottomTabs: {
id: 'BOTTOM_TABS_LAYOUT',
children: [
{
stack: {
id: 'HOME_TAB',
children: [
{
component: {
id: 'HOME_SCREEN',
name: 'HomeScreen'
}
}
],
options: {
bottomTab: {
icon: require('./home.png')
}
}
}
},
{
stack: {
id: 'PROFILE_TAB',
children: [
{
component: {
id: 'PROFILE_SCREEN',
name: 'ProfileScreen'
}
}
],
options: {
bottomTab: {
icon: require('./profile.png')
}
}
}
}
]
}Once we run this code, our BottomTabs should look like this:
Selecting Tabs Programmatically
Tabs can be selected programmatically by updating the currentTabIndex or currentTabId options.
We'll use the BottomTabs layout showcased above to demonstrate programmatic tab selection.
Selecting a tab by index
The following mergeOptions command will select the second tab. We're passing the id of our BottomTabs layout, but we could also use the id of any of the child components, for example PROFILE_TAB or PROFILE_SCREEN.
Navigation.mergeOptions('BOTTOM_TABS_LAYOUT', {
bottomTabs: {
currentTabIndex: 1
}
});Selecting a tab by id
Tabs can also be selected by id (componentId). This is particularly useful in cases where you don't know in which tab a screen is contained.
For example, if invoked from one of the child components, HOME_SCREEN or SETTINGS_SCREEN, the following merge command will select the tab containing the child.
Navigation.mergeOptions(this.props.componentId, {
bottomTabs: {
currentTabId: this.props.componentId
}
});Changing BottomTabs visibility
The visible property is used to control the BottomTab visibility. Visibility can be toggled dynamically using the mergeOptions command.
Navigation.mergeOptions(componentId, {
bottomTabs: {
visible: false
},
});Visibility can also be changed when pushing screens.
Navigation.push(componentId, {
component: {
name: 'pushedScreen',
options: {
bottomTabs: {
visible: false
}
}
}
});Updating tab options dynamically
To update a tab option, like an icon, text or color, simply call mergeOptions with new options using the id of a component or layout contained in the tab you wish to update.
For instance, in the example below we update the color of a specific tab:
Navigation.mergeOptions(componentId, {
bottomTab: {
iconColor: 'red',
textColor: 'red'
},
});Styling the tabs
The bottom-tabs feature support for an enhanced, floating-like layout that offers a modern and visually appealing alternative to the default design.
This layout creates the illusion of the tabs hovering above the screen content, adding depth and sophistication to the user interface.
To achieve this effect (and fully customize it), you can use the following bottom-tabs styling options:
Navigation.setRoot({ // An example; push() also support this
root: {
bottomTabs: {
options: {
layoutStyle: 'compact',
translucent: true,
drawBehind: true,
bottomMargin: 15,
// Cosmetics:
cornerRadius: 15,
blurRadius: 2.0, // Actual radius would be 2.0 * 4 = 8.0
backgroundColor: 'rgba(50, 50, 50, 0.4)', // A subtle semi-transparent (40%) hue
elevation: 1, // Soft surrounding shadow effect (i.e. the elevation)
},
children: [
// ...
],
},
},
});- Setting
layoutStyle: compactkeeps the tabs container from taking up the entire screen width, thus leaving room for the screen content to appear on both its sides. - The
translucentproperty adds a semi-transparent background, whiledrawBehindandbottomMarginallow the screen content to flow beneath the hovering tabs. - The
cornerRadiusrounds the edges of the tabs, giving them a soft, card-like appearance.
Note: Settings some of the options via
mergeOptionsis discouraged, as they'd only apply for the currently visible tab.
Custom React tab items
To render arbitrary React components as tab items (Lottie, SVG, custom badges, full custom bar design), use bottomTab.component on every tab and optionally bottomTabs.customRow for the floating row chrome.
See the Custom bottom tabs guide for setup, props, platform differences, and examples.
Controlling tab loading
By default, all tabs are mounted at the same time. This can have a negative impact on performance since screens which are not visible to the user are mounted.
The order in which tabs are mounted can be configured with the tabsAttachMode option:
together- all tabs are mounted at the same time, this is the default behavior.afterInitialTab- after initial tab is mounted, other tabs are mounted.onSwitchToTab- initial tab is mounted, other tabs are mounted when the user navigates to them for the first time.
Docs Docs CustomBackNavigation (`website/docs/docs/docs-customBackNavigation.mdx`)
id: customBackNavigation title: Custom back navigation sidebar_label: Custom back navigation
Back navigation lets users move backwards through the screens they previously visited.
The most common form of back navigation is done through the back button. The stack layout contains a back button by default, also known as the software back button.
RNN handles the back navigation for you, but there are use cases where you might need to override the default behavior of the back navigation. For example, when editing a blog post, you might want to prompt the user about unsaved changes instead of navigating to the previous destination.
Additional back navigation methods
- Android devices include a hardware back button, located at the bottom of the device.
- Android 11 introduced an opt-in gesture navigation system that hides the NavigationBar. When enabled, the hardware back button is replaced with a back gesture performed with a horizontal swipe across the screen.
- The stack layout on iOS supports a back gesture by default. Like the android back gesture, it's performed by swiping across the screen.
Handling the software back button
The software back button is located in the TopBar and is responsible for popping the top most screen in the stack. It's configured in the TopBar options.
To handle the software back button, we must first disable the default behavior which is to pop the top most screen:
options: {
topBar: {
backButton: {
popStackOnPress: false;
}
}
}Now, when the software back button is pressed, instead of popping the screen RNN will emit a navigationButtonPress event which can be handled in JS.
navigationButtonPressed({ buttonId }) {
if (buttonId === 'RNN.back') {
console.log('The software back button was pressed!');
}
}Handling the hardware back button
Unlike the software back button which affects the stack it's part of, the hardware back button isn't bound to a specific layout. Instead, The hardware back button affects the top most layout that responds to back navigation.
When a stack containing more than one child is focused, pressing the hardware back button will pop the stack. When a modal is displayed and the current stack has exactly one child, pressing the hardware back button will dismiss the modal.
To handle the hardware back button, we must first disable the default behavior for the desired action (pop stack or dismiss modal) through options:
options: {
hardwareBackButton: {
dismissModalOnPress: false,
popStackOnPress: false,
}
}Once the default behaviors are disabled, RNN will emit a navigationButtonPress with the RNN.hardwareBackButton id.
navigationButtonPressed({ buttonId }) {
if (buttonId === 'RNN.hardwareBackButton') {
console.log('The hardware back button was pressed!');
}
}:::info Note
The hardware back button is available on Android only.
:::
:::info Note 2
When gesture navigation is enabled on Android, the back gesture is handled as a hardware back press.
:::
Disabling the iOS pop gesture
The iOS stack layout can be popped with a swipe gesture. If desired, the swipe gesture can be disabled via options:
options: {
popGesture: false;
}Docs Docs CustomBottomTabs (`website/docs/docs/docs-customBottomTabs.mdx`)
id: customBottomTabs title: Custom bottom tab items sidebar_label: Custom bottom tabs
Custom bottom tabs let you render a React component for each tab item instead of the built-in icon + label. Use this when you need Lottie animations, SVG icons, custom badge layouts, or a fully custom tab bar design.
The same JavaScript API works on iOS and Android. Both platforms use a floating custom row when every tab declares bottomTab.component.
Requirements
- Every tab in a
bottomTabslayout must setbottomTab.component. If only some tabs use a custom component, RNN logs a warning and falls back to native rendering for all tabs in that bar. - Register your tab item component with
Navigation.registerComponent(same as any other screen). - Optionally set
bottomTabs.customRowon the layout to style the floating row chrome (margins, height, background, corner radius).
Basic example
// 1. Register the tab item component (e.g. in index.js)
Navigation.registerComponent('MyTabItem', () => require('./MyTabItem'));
// 2. Use it on every tab
Navigation.setRoot({
root: {
bottomTabs: {
options: {
bottomTabs: {
customRow: {
backgroundEffect: 'glass',
cornerRadius: 28,
horizontalMargin: 16,
bottomMargin: 0,
height: 67,
},
},
},
children: [
{
stack: {
children: [{ component: { name: 'HomeScreen' } }],
options: {
bottomTab: {
component: { name: 'MyTabItem' },
badge: '3',
},
},
},
},
{
stack: {
children: [{ component: { name: 'SearchScreen' } }],
options: {
bottomTab: {
component: { name: 'MyTabItem' },
},
},
},
},
{
stack: {
children: [{ component: { name: 'ProfileScreen' } }],
options: {
bottomTab: {
component: { name: 'MyTabItem' },
},
},
},
},
],
},
},
});See the playground: Layouts → BottomTabs Custom Component (LayoutsScreen.bottomTabsWithCustomComponent).
Tab item component props
RNN creates one React surface per tab and pushes prop updates when selection or badge changes.
| Prop | Type | Description |
|---|---|---|
componentId |
string |
Stable id for this tab item instance |
tabIndex |
number |
Zero-based index of the tab |
selected |
boolean |
Whether this tab is currently selected |
badge |
string | null |
Mirrors bottomTab.badge |
passProps from bottomTab.component.passProps are merged at creation time only. Use tabIndex inside your component to pick icon/label per tab.
Example tab item
function MyTabItem({ tabIndex, selected, badge }) {
const labels = ['Home', 'Search', 'Profile'];
return (
<View style={{ alignItems: 'center' }}>
<MyIcon index={tabIndex} selected={selected} />
<Text style={{ color: selected ? '#007aff' : '#9aa0a6' }}>
{labels[tabIndex]}
</Text>
{badge ? <Badge>{badge}</Badge> : null}
</View>
);
}What is ignored when `component` is set
For tabs that use bottomTab.component, these options are ignored for that tab's visual content:
text, icon, selectedIcon, sfSymbol, sfSelectedSymbol, iconColor, selectedIconColor, iconWidth, iconHeight, iconInsets, fontFamily, fontWeight, fontSize, selectedFontSize, textColor, selectedTextColor
Native still handles tab selection, visibility, drawBehind, animations, and dotIndicator. Switch tabs from JS with:
Navigation.mergeOptions('BOTTOM_TABS_LAYOUT_ID', {
bottomTabs: { currentTabIndex: 1 },
});Events
Taps on custom tab items use the same native selection pipeline as the built-in tab bar. Use the standard Navigation events listeners:
| Listener | Custom row |
|---|---|
registerBottomTabPressedListener |
Yes — every tap (iOS and Android) |
registerBottomTabSelectedListener |
Yes — when the selected tab changes |
registerBottomTabLongPressedListener |
No |
bottomTab.selectTabOnPress: when false, pressed still fires; selected does not until you change the tab programmatically (for example via bottomTabs.currentTabIndex).
bottomTab.popToRoot and Android hardware-back tab history behave the same as with native tab items.
registerBottomTabLongPressedListener is iOS-only and is wired to the native UITabBar long-press gesture. With custom tabs the native bar is hidden and touches go to the custom row, so this listener does not fire for custom tab items.
Custom row (`bottomTabs.customRow`)
When all tabs use custom components, RNN replaces the visible tab bar chrome with a floating row that hosts your React tab cells. Configure it with bottomTabs.customRow on the bottomTabs layout options.
bottomTabs: {
options: {
bottomTabs: {
customRow: {
height: 67,
backgroundEffect: 'glass', // 'glass' | 'blur' | 'none'
cornerRadius: 28,
horizontalMargin: 16,
bottomMargin: 0,
// backgroundColor: '#ffffff', // optional; overrides backgroundEffect
},
},
},
children: [/* ... */],
}Options apply on setRoot, showModal, mergeOptions, and setDefaultOptions (same as other bottom-tabs options).
`customRow` fields
| Option | Type | Default (when omitted) | Description |
|---|---|---|---|
height |
number |
Native tab content height (+18 on iOS 26+) | Content height of the row in points/dp, excluding safe-area inset |
backgroundColor |
color | — | Solid background; overrides backgroundEffect |
backgroundEffect |
'glass' | 'blur' | 'none' |
'glass' on iOS 26+, 'blur' below |
Row background style |
cornerRadius |
number |
28 on iOS 26+, 0 below |
Corner radius of the row background |
horizontalMargin |
number |
16 on iOS 26+, 0 below |
Inset from left/right screen edges |
bottomMargin |
number |
0 |
Gap between row bottom and safe-area bottom |
Total row height = height + safe-area bottom inset + bottomMargin (same formula on both platforms).
Platform behavior
| Topic | iOS | Android |
|---|---|---|
| Custom tab cells | React component in custom floating row | Same |
| Native tab bar visuals | Hidden; blank UITabBarItems keep selection |
Native bar hidden (alpha = 0); BottomTabs still drives selection |
backgroundEffect: 'glass' |
UIGlassEffect (iOS 26+), blur fallback |
Semi-opaque material chrome (no system glass API) |
backgroundEffect: 'blur' |
UIBlurEffect systemChromeMaterial |
Semi-opaque material chrome |
| Row shadow | From glass/blur material | Subtle elevation shadow (~3dp, low alpha) to approximate iOS lift |
| Options delivery | Native options parser | JS forwards customRow to RNNBottomTabsCustomRowModule before layout commands |
| Min tabs | 2 (UIKit) | 3 (AHBottomNavigation) |
iOS implementation notes
- Custom row:
RNNBottomTabsCustomRow— hosts cells, appliescustomRowoptions, handles layout and taps. - iOS 26+: defaults match the floating tab bar (glass, 28pt radius, 16pt horizontal margin).
tabBarMinimizeBehavioris forced toneverwhen custom item views are active.
Android implementation notes
- Custom row lives in
com.reactnativenavigation.customrowand attaches when aBottomTabsview with custom items appears. BottomTabs.setExternalCustomItemViewHost(true)prevents the legacy overlay path from re-parenting React views into native cells.- No changes required in app
MainApplication— the module registers viaNavigationPackage.
Updating `customRow` at runtime
Navigation.mergeOptions('BOTTOM_TABS_LAYOUT_ID', {
bottomTabs: {
customRow: {
backgroundColor: '#1c1c1e',
cornerRadius: 20,
},
},
});On Android, merged customRow is picked up on the next layout pass via BottomTabsCustomRowConfigStore.
Related API
Docs Docs Deep Linking (`website/docs/docs/docs-deep-linking.mdx`)
id: deep-linking title: Deep Linking sidebar_label: Deep Linking
Deep linking opens a specific screen in your app from a URL — whether from a custom scheme (myapp://user/42) or a universal link (https://myapp.com/user/42). React Native Navigation maps URLs to screens and presents the matched content as a modal on top of whatever the user is currently doing, so their navigation state is preserved when they dismiss the modal.
Setting up linking
Call Navigation.setLinking() once during app startup, alongside your Navigation.registerComponent calls:
import { Navigation } from 'react-native-navigation';
Navigation.setLinking({
prefixes: ['myapp://', 'https://myapp.com'],
config: {
screens: {
Home: 'home',
Profile: 'user/:id',
Settings: {
path: 'settings',
screens: {
Notifications: 'notifications',
},
},
},
},
});When a deep link arrives:
- RNN parses the URL, strips the longest matching prefix, and matches the path against the
screenstree. - The matched chain is wrapped in a
stackand presented viashowModal. The user can dismiss the modal to return to their previous flow. - If no route matches, the optional
fallbackcallback is invoked.
Path parameters (:id) and query parameters are extracted and passed as passProps to each component in the modal.
Configuration
`prefixes`
Array of URL prefixes your app handles. Custom schemes and universal-link hosts are both supported. The longest matching prefix wins.
prefixes: ['myapp://', 'https://myapp.com', 'https://myapp.com/v2'],`config.screens`
Map of screen names (must match the names you registered with Navigation.registerComponent) to URL path patterns.
String value — a leaf route:
screens: {
Home: 'home', // matches myapp://home
Profile: 'user/:id', // matches myapp://user/42, extracts { id: '42' }
}Object value — a route with nested children:
screens: {
Settings: {
path: 'settings',
screens: {
Notifications: 'notifications',
Account: 'account/:section',
},
},
}
// myapp://settings/notifications → stack: [Settings, Notifications]
// myapp://settings/account/billing → stack: [Settings, Account] with { section: 'billing' }Object value with no path — a grouping node that contributes a screen to the chain but consumes no URL segments:
screens: {
Main: {
screens: {
Feed: 'feed', // matches myapp://feed → stack: [Main, Feed]
Search: 'search',
},
},
}Path parameters
Use :paramName in a pattern segment to capture it. Path params are merged with query params and handed to that segment's component as passProps. Path params win on key collision.
screens: { Post: 'user/:userId/post/:postId' }
// myapp://user/5/post/99
// → Post screen receives passProps: { userId: '5', postId: '99' }Query parameters
Query string values are parsed automatically and merged into every segment's passProps:
myapp://search?q=hello&page=2
→ Search screen receives passProps: { q: 'hello', page: '2' }Reserved keys
The query/path keys ref and key are not forwarded as passProps. React reserves these names (ref is consumed by React itself; passing a string ref to a component crashes under React 19's stricter validation). RNN silently drops them and logs a dev-mode warning. Rename any conflicting URL parameters before they reach your links:
myapp://user/42?ref=push → passProps: { id: '42' } (ref dropped + warning)
myapp://user/42?source=push → passProps: { id: '42', source: 'push' }Customizing presentation
By default, every matched link becomes a modal containing a stack with the matched chain pushed in order. If you need to customize either the layout or the navigation command itself, the config exposes two hooks.
`getModal` — customize the modal layout
Provide a function that returns the Layout to present. Useful for adding default options, wrapping in a different layout, or skipping certain matches conditionally.
Navigation.setLinking({
// ...
getModal: (match) => ({
stack: {
children: match.path.map((segment) => ({
component: {
name: segment.screen,
passProps: { ...match.queryParams, ...segment.params },
options: {
topBar: {
leftButtons: [{ id: 'close', text: 'Close' }],
},
},
},
})),
},
}),
});Return undefined from getModal to skip presenting a modal for a particular match (e.g. ignore certain paths).
`onLink` — full escape hatch
For complete control — pushing onto an existing stack instead of showing a modal, dismissing existing modals first, or composing multiple commands — provide onLink. When supplied, RNN does not present anything itself; you call navigation commands yourself.
Navigation.setLinking({
// ...
onLink: (match) => {
const leaf = match.path[match.path.length - 1];
if (leaf.screen === 'Profile') {
Navigation.push('mainStack', {
component: {
name: 'Profile',
passProps: { id: leaf.params.id },
},
});
return;
}
Navigation.showModal({
stack: {
children: match.path.map((segment) => ({
component: { name: segment.screen, passProps: segment.params },
})),
},
});
},
});When onLink is set, getModal is ignored.
Match payload
Both getModal and onLink receive the same RouteMatch object:
interface RouteMatch {
url: string;
path: RouteMatchSegment[];
queryParams: Record<string, string>;
}
interface RouteMatchSegment {
screen: string;
params: Record<string, string>;
}Fallback for unmatched URLs
If a received URL has no matching prefix or doesn't match any configured route, the optional fallback callback is invoked. Use it for logging, analytics, or routing to a "not found" screen.
Navigation.setLinking({
// ...
fallback: (url) => {
console.warn(`Unhandled deep link: ${url}`);
},
});Deferred deep links
Two readiness gates control when links are processed; both must be open before a link is dispatched.
Automatic root gate
RNN automatically defers link processing until your first Navigation.setRoot() resolves. This handles the common case where the OS delivers an initial URL before your app has a root window — the link is queued and replayed once setRoot mounts the root.
You don't need to opt into this behavior; it always applies.
Optional `isReady` gate
For application-level gates such as authentication, supply an isReady predicate. The predicate is consulted on every incoming link; when it returns false, the link is queued. Once the gate opens, call setLinkingReady(true) to flush queued links in order.
let isAuthenticated = false;
Navigation.setLinking({
prefixes: ['myapp://'],
config: { screens: { Profile: 'user/:id' } },
isReady: () => isAuthenticated,
});
async function onLoginSuccess() {
isAuthenticated = true;
Navigation.setLinkingReady(true);
}setLinkingReady(true) overrides isReady once called, so you can also drive the gate purely with explicit calls if you prefer.
Manual handling
Some links don't arrive via the system URL handler — push-notification payloads, branch.io tokens, App Clips. Feed them into the same pipeline with Navigation.handleDeepLink(url):
Navigation.handleDeepLink('myapp://user/42?source=notification');This runs the URL through parse → match → present exactly like a system-delivered link, including readiness gates.
If you'd rather hand the URL off natively (e.g. from a UNUserNotificationCenterDelegate you already maintain), call [self dispatchDeepLinkURL:url] inside your RNNAppDelegate subclass — same behavior, with cold-start queueing built in. See Native setup → iOS → Notification taps for an example.
Complete example
import { Navigation } from 'react-native-navigation';
Navigation.registerComponent('Home', () => require('./screens/Home').default);
Navigation.registerComponent('Profile', () => require('./screens/Profile').default);
Navigation.registerComponent('Settings', () => require('./screens/Settings').default);
Navigation.registerComponent('Notifications', () => require('./screens/Notifications').default);
let isLoggedIn = false;
Navigation.setLinking({
prefixes: ['myapp://', 'https://myapp.com'],
config: {
screens: {
Home: 'home',
Profile: 'user/:id',
Settings: {
path: 'settings',
screens: { Notifications: 'notifications' },
},
},
},
isReady: () => isLoggedIn,
fallback: (url) => console.warn(`Unhandled: ${url}`),
});
Navigation.events().registerAppLaunchedListener(() => {
Navigation.setRoot({
root: { stack: { children: [{ component: { name: 'Home' } }] } },
});
});
export function onLoginComplete() {
isLoggedIn = true;
Navigation.setLinkingReady(true);
}With this configuration:
myapp://homeopens the Home screen as a modal on top of the current screen.myapp://user/42opens Profile as a modal with{ id: '42' }as props.myapp://settings/notificationsopens a modal containing a stack withSettings→Notificationspushed in order.- Links arriving before
setRootresolves are queued automatically and replayed. - Links arriving before login are queued and replayed when
onLoginComplete()runs.
Native setup
The framework consumes URLs from React Native's Linking API. You still configure the URL schemes and universal links at the platform level — but, unlike most React Native apps, you don't need to hand-write application:openURL: or application:continueUserActivity: glue. As long as your AppDelegate subclasses RNNAppDelegate, custom-scheme openings and universal links are forwarded to JS automatically, including cold-start URLs that arrive before the React runtime is ready.
iOS
1. Register your URL scheme in Info.plist:
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>myapp</string>
</array>
</dict>
</array>2. (Optional) Universal links. Add the Associated Domains entitlement and host an apple-app-site-association file — see Apple's docs. RNNAppDelegate already implements application:continueUserActivity:restorationHandler: and forwards browser-activity URLs through the same pipeline.
3. (Optional) Notification taps. Notifications aren't routed automatically because most apps already own UNUserNotificationCenter.current.delegate via Firebase / OneSignal / etc. From your existing notification handler, hand the URL off to RNN:
#import "AppDelegate.h" // your subclass of RNNAppDelegate
#import <UserNotifications/UserNotifications.h>
@interface AppDelegate () <UNUserNotificationCenterDelegate>
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[super application:application didFinishLaunchingWithOptions:launchOptions];
[UNUserNotificationCenter currentNotificationCenter].delegate = self;
return YES;
}
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
willPresentNotification:(UNNotification *)notification
withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler {
completionHandler(UNNotificationPresentationOptionBanner |
UNNotificationPresentationOptionList |
UNNotificationPresentationOptionSound);
}
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
didReceiveNotificationResponse:(UNNotificationResponse *)response
withCompletionHandler:(void (^)(void))completionHandler {
NSString *urlString = response.notification.request.content.userInfo[@"url"];
if ([urlString isKindOfClass:[NSString class]]) {
[self dispatchDeepLinkURL:[NSURL URLWithString:urlString]];
}
completionHandler();
}
@enddispatchDeepLinkURL: is inherited from RNNAppDelegate. It safely handles cold-start (URLs arriving before the React runtime is ready are queued and flushed automatically once content first renders) so you can call it from anywhere — push handlers, deferred-deep-link SDKs, branch.io callbacks, etc.
The JS equivalent is Navigation.handleDeepLink(url); use whichever fits the layer you're working at.
Android
Add an intent filter to your MainActivity in AndroidManifest.xml:
<activity
android:name=".MainActivity"
android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="myapp" />
</intent-filter>
</activity>For App Links, add android:autoVerify="true" and host an assetlinks.json. See Android's documentation.
NavigationActivity already forwards both cold-start (getIntent()) and warm (onNewIntent) URLs to React Native's Linking module — no extra Java/Kotlin needed. Notification taps that carry a deep-link URI (via PendingIntent with ACTION_VIEW) flow through the same onNewIntent path automatically.
API reference
`Navigation.setLinking(config)`
Configure deep link handling. Subsequent calls reconfigure cleanly (the previous subscription is torn down).
| Field | Type | Required | Description |
|---|---|---|---|
prefixes |
string[] |
Yes | URL prefixes to match. Longest wins. |
config.screens |
ScreensConfig |
Yes | Screen-to-path mapping. |
getModal |
(match) => Layout | undefined |
No | Build a custom modal layout for the match. |
onLink |
(match) => void |
No | Full escape hatch. Overrides default and getModal. |
fallback |
(url) => void |
No | Called when a URL doesn't match any prefix or route. |
isReady |
() => boolean |
No | Predicate gating link processing. |
`Navigation.handleDeepLink(url)`
Feed a URL into the pipeline as if it had been delivered by the OS.
`Navigation.setLinkingReady(ready)`
Set the user-controlled readiness gate. Passing true flushes queued links; passing false blocks future links until called again with true.
Docs Docs ExternalComponent (`website/docs/docs/docs-externalComponent.mdx`)
id: externalComponent title: External Component sidebar_label: External Component
The ExternalComponent layout allows you to display any native view as a screen. To use the External Component we'll need to register it with a string name. This name is then used when declaring layouts in JS.
Android
Implementing the native component
When it comes to implementing an external component on Android, you have two choices. We recommend you use a base class extending View. If you're required to use Fragments, you'll find an basic example below.
ViewGroup
public class ViewGroupComponent implements ExternalComponent {
private final FrameLayout content;
ViewGroupComponent(FragmentActivity activity, JSONObject props) {
content = new FrameLayout(activity);
}
@Override
public View asView() {
return content;
}
}Fragment
Using a Fragment as an external component is done by attaching the Fragment to a FrameLayout, and returning the FrameLayout from the asView() method of the ExternalComponent.
public class FragmentComponent implements ExternalComponent {
private final FrameLayout content;
FragmentComponent(FragmentActivity activity, JSONObject props) {
// Create the FrameLayout to which we'll attach our Fragment to
content = new FrameLayout(activity);
content.setId(R.id.fragment_screen_content);
// Attach the Fragment to the FrameLayout
activity.getSupportFragmentManager()
.beginTransaction()
.add(R.id.fragment_screen_content, createFragment(props))
.commitAllowingStateLoss();
}
private FragmentScreen createFragment(JSONObject props) {
FragmentScreen fragment = new FragmentScreen();
// Pass the props sent from Js in a bundle
Bundle args = new Bundle();
args.putString("text", props.optString("text", ""));
fragment.setArguments(args);
return fragment;
}
@Override
public View asView() {
return content;
}
}Registering the component
public class MainApplication extends NavigationApplication {
@Override
public void onCreate() {
super.onCreate();
registerExternalComponent("MyExternalComponent", new FragmentCreator());
}
}iOS
ViewController registration
[ReactNativeNavigation registerExternalComponent:@"MyExternalComponent" callback:^UIViewController *(NSDictionary *props, RCTBridge *bridge) {
return [[ExternalViewController alloc] initWithProps:props];
}];Using the component from JS
Once you've registered the external component in native, you can use it in your layout declarations.
For example, to show an external component modally:
Navigation.showModal({
externalComponent: {
name: 'MyExternalComponent',
passProps: {
text: "Text from JS"
}
}
});:::caution
props passed to external components are sent over the bridge and therefore must be serializable.
:::
Docs Docs Functional Components (`website/docs/docs/docs-functional-components.mdx`)
id: functionalComponents title: Using functional components as screens sidebar_label: Using functional components
Both class components and functional components can be used as screens. There is no performance difference between the two. Deciding which type component to use boils down to user preference.
Declaring static options
Static options are a great way to declare options close to where they are used. Declaring static options is done by first declaring the functional component, and then declaring the options on the function component.
const MyScreen = (props) => {
return (
<View>
<Text>Screen Component</Text>
</View>
);
};
MyScreen.options = {
topBar: {
background: {
color: 'red'
}
}
}Listening to navigation events
Navigation events let you react to various events related to a specific component or the app itself. Read more about them here
import { useEffect } from 'react';
import { Navigation } from 'react-native-navigation';
function MyScreen(props) {
useEffect(() => {
const listener = {
componentDidAppear: () => {
console.log('RNN', `componentDidAppear`);
},
componentDidDisappear: () => {
console.log('RNN', `componentDidDisappear`);
}
};
// Register the listener to all events related to our component
const unsubscribe = Navigation.events().registerComponentListener(listener, props.componentId);
return () => {
// Make sure to unregister the listener during cleanup
unsubscribe.remove();
};
}, []);
return (
<View>
<Text>Screen Component</Text>
</View>
);
}In the example above, our listener is registered with the componentId passed in the second argument, and will receive only its related events.
:::tip
underscopeio/react-native-navigation-hooks is a wonderful library which greatly simplifies usage with hooks by introducing dedicated hooks for each event. The following example, which is taken from their docs, shows how to listen to all appear events and a particular screen's appear events:
import { useNavigationComponentDidAppear } from 'react-native-navigation-hooks'
const ScreenComponent = ({ componentId }) => {
// Global listener
useNavigationComponentDidAppear(e => {
console.log(`${e.componentName} (${e.componentId}) appeared`)
})
// Listen events only for this screen (componentId)
useNavigationComponentDidAppear(e => {
console.log(`${e.componentName} appeared`)
}, componentId)
return (
<View>
<Text>Screen Component</Text>
</View>
)
}:::
Docs Docs Modal (`website/docs/docs/docs-modal.mdx`)
id: modal title: Modal sidebar_label: Modal
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import useBaseUrl from '@docusaurus/useBaseUrl';
In human-centered design, when we speak about a modal (or modality), we often refer to a set of techniques, aimed at bringing user attention to a specific event / screen / action / etc. Those often require user input. A pop-up on a website, a file deletion confirmation dialogue on your computer, or an alert asking you to enable location service on your phone - these can all be considered modals.
A modal is a term used in native iOS world (full description here), while on Android, dialogs are often used to create similar or identical behavior, alongside other techniques.
Presenting modals
Modal can be displayed by invoking the Navigation.showModal() command, as shown in the example below:
Navigation.showModal({
stack: {
children: [
{
component: {
name: 'MODAL_SCREEN',
options: {
topBar: {
title: {
text: 'Modal',
},
},
},
},
},
],
},
});Or by declaring modal as a Component inside render function:
:::note
Available for Android only, for iOS please use Modal from react-native.
:::
(Full code can be found in the Playground).
import { NavigationComponent, Modal as RNNModal } from 'react-native-navigation';
.....
toggleModal = () => this.setState({ modalVisible: !this.state.modalVisible });
.....
render() {
return (
.....
<RNNModal
onShow={/* optional callback */}
animationType={/* optional, 'none' | 'fade' | 'slide', default 'slide'*/}
blurOnUnmount={/* optional, default false */}
transparent={/* optional, default false, true behaves as overCurrentContext */}
visible={this.state.modalVisible}
onRequestClose={() => this.setState({ modalVisible: false })}
>
<Button
label="Dismiss declared Modal"
testID={DISMISS_MODAL_BTN}
onPress={this.toggleModal}
/>
....
</RNNModal>
....
);
}Here's how the Modal looks on both platforms.
<Tabs
defaultValue="ios"
values={[
{ label: 'iOS', value: 'ios' },
{ label: 'Android', value: 'android' },
]}
Adding a dismiss button
Modals should always have a dismiss button, on left-to-right devices it's typically placed as a left button in the TopBar.
After we've added our dismiss button, we need to dismiss the modal when the button is pressed.
:::note
For the LeftButton to be visible, the screen must be displayed in a Stack.
:::
class ModalScreen extends React.Component {
// Set a dismiss button in static options
static options() {
return {
topBar: {
leftButtons: {
id: 'dismiss',
icon: require('./dismiss.png'),
},
},
};
}
constructor(props) {
super(props);
// Register to events
Navigation.events().bindComponent(this);
}
// Handle the button press event and dismiss the modal if needed
navigationButtonPressed({ buttonId }) {
if (buttonId === 'dismiss') {
Navigation.dismissModal(this.props.componentId);
}
}
}Transparent modals
Showing transparent modals is a nice technique to increase immersiveness while keeping the user in the same context.
When a modal is displayed the content behind it (either root or modal) is removed from hierarchy. While this behavior improves performance, it's undesired when
showing transparent modals as we need to see the content behind it.
To configure this behaviour, we'll need to change the modalPresentationStyle option to overCurrentContext and change the layout background color to 'transparent'.
{
modalPresentationStyle: 'overCurrentContext',
layout: {
backgroundColor: 'transparent'
}
}Preventing a Modal from being dismissed
Preventing a modal from being dismissed is done differently for each platform. While preventing the user from dismissing the modal is possible,
the modal could still be dismissed programmatically by calling Navigation.dismissModal()
If the modal has a dismiss button, of course you'll need to handle it your self and avoid calling Navigation.dismissModal()
when the button is pressed.
Android
On Android, modals can be dismissed with the hardware back button. You can handle the back press yourself by
disabling the hardware back button dismissModalOnPress option:
{
hardwareBackButton: {
dismissModalOnPress: false;
}
}As a result, pressing the hardware back button will not dismiss the modal and will dispatch navigationButtonPress event with the default hardware back button id RNN.hardwareBackButton.
iOS
While iOS devices don't have a hardware back button, PageSheet modals can be dismissed by swipe gesture
from the top of the screen. To disable it, set swipeToDismiss option to false.
Presentation Style
The presentation style determines the look and feel of a screen displayed as modal.
Docs Docs Overlay (`website/docs/docs/docs-overlay.mdx`)
id: overlay title: Overlay sidebar_label: Overlay
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import useBaseUrl from '@docusaurus/useBaseUrl';
Overlays are used to layout content on top of all other layout hierarchies, while fitting the screen bounds.
The contents displayed in an Overlay can either be non-obtrusive, like Toasts and Snackbars, or an Alert that blocks all interactions with any content behind it. The view contained within it should be aligned either with absolute position, flex, or with margins, all according to your needs.
Handling touch events outside the view
When showing views like Alert or Toast in an Overlay, you would want to configure if you want to allow users to interact with content behind the alert. This is done via the interceptTouchOutside option.
Overlay examples
<Tabs
defaultValue="alert"
values={[
{ label: 'Alert', value: 'alert', },
{ label: 'Toast', value: 'toast', }
]
}>
The example below demonstrates how to create a simple alert dialog using an Overlay. Touch events outside the alert will be blocked and won't pass through to the content behind the alert since we're specifying interceptTouchOutside: true in the static options of the Alert.
const React = require('react');
const { Text, Button, View } = require('react-native');
const { Navigation } = require('react-native-navigation');
function Alert({ componentId, title, message }) {
const dismiss = () => Navigation.dismissOverlay(componentId);
return (
<View style={styles.root}>
<View style={styles.alert}>
<Text style={styles.title}>{title}</Text>
<Text style={styles.message}>{message}</Text>
<Button title="OK" onPress={dismiss} />
</View>
</View>
);
}
const styles = {
root: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#00000050',
},
alert: {
alignItems: 'center',
backgroundColor: 'whitesmoke',
width: 250,
elevation: 4,
padding: 16,
},
title: {
fontSize: 18,
},
message: {
marginVertical: 8,
},
};
Alert.options = (props) => {
return {
overlay: {
interceptTouchOutside: true,
},
};
};
module.exports = Alert;The example below demonstrates how to show a Toast using an Overlay. A user can interact with the content behind the toast since we've declared interceptTouchOutside: false in the static options of the Alert.
const React = require('react');
const { View, Text, StyleSheet, TouchableOpacity } = require('react-native');
const Colors = require('../commons/Colors');
const Navigation = require('../services/Navigation');
const Toast = function ({ componentId }) {
return (
<View style={styles.root}>
<View style={styles.toast}>
<Text style={styles.text}>This a very important message!</Text>
<TouchableOpacity
style={styles.button}
onPress={() => Navigation.dismissOverlay(componentId)}
>
<Text style={styles.buttonText}>OK</Text>
</TouchableOpacity>
</View>
</View>
);
};
const styles = StyleSheet.create({
root: {
flex: 1,
flexDirection: 'column-reverse',
},
toast: {
elevation: 2,
flexDirection: 'row',
height: 40,
margin: 16,
borderRadius: 20,
backgroundColor: Colors.accent,
alignItems: 'center',
justifyContent: 'space-between',
},
text: {
color: 'white',
fontSize: 16,
marginLeft: 16,
},
button: {
marginRight: 16,
},
buttonText: {
color: 'white',
fontSize: 16,
fontWeight: 'bold',
},
});
Toast.options = {
layout: {
componentBackgroundColor: 'transparent',
},
overlay: {
interceptTouchOutside: false,
},
};
module.exports = Toast;Docs Docs Passing Data To Components (`website/docs/docs/docs-passing-data-to-components.mdx`)
id: passing-data-to-components title: Passing data to components sidebar_label: Passing data to components
Initial props
React components use props to receive data when they are created. When displaying the component layout, you can pass initial props to components using the passProps property.
In this example, we push the UserProfile screen and pass two initial props to it:
Navigation.push(this.props.componentId, {
component: {
name: 'UserProfile',
id: 'PROFILE_SCREEN_ID'
passProps: {
name: 'John Doe',
status: 'online'
}
}
});:::tip Serialization
passProps don't need to be serializable. You can use this mechanism to pass lambda functions or even React Components.
:::
Handling passProps in static options
Each component can have a static options property which is used to configure the style properties of elements on the screen when that component is displayed.
Static options can either be a simple object, or a function which accepts passProps as an argument. Sometimes when declaring components, not all style properties are known. For example, a user profile screen will usually have the user's name as the TopBar title text. Therefore the title must be configured dynamically when pushing the screen.
Following the code example above, lets see how to use props passed in the push command to set the TopBar title.
class UserProfileScreen extends React.Component {
static options(props) {
return {
topBar: {
title: {
text: props.name
}
}
};
}
}Updating props
To update a component's props, use the Navigation.updateProps() command. Updating props triggers the component lifecycle methods related to updating.
Notice that we're using the component id in order to update props of this specific instance of the component.
Navigation.updateProps('PROFILE_SCREEN_ID', {
status: 'offline'
});Docs Docs Playground App (`website/docs/docs/docs-playground-app.mdx`)
id: playground-app title: Playground app sidebar_label: Playground app
Running The Project
If you want to have a quick look around and test things out, you can run the playground app, bundled with this repo.
- Install dependencies via
yarn install(if you haven't already) andyarn pod-install(for iOS) - Run the playground project on Android and iOS
yarn startto get the packager running in the terminal, leave it open- iOS: open
./playground/ios/playground.xcworkspacein Xcode and run it - Android: open
./playground/androidin Android Studio and run it
- You can run tests if / when you need to (list of scripts available here). Before
you start changing things, make sure everything works.- To easily run all tests in parallel
yarn test-all
- To easily run all tests in parallel
Docs Docs Root (`website/docs/docs/docs-root.mdx`)
id: root title: Root sidebar_label: Root
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
The root is where the application layout structure is defined. It is typically the first UI element a user interacts with. The root can be changed multiple times during the lifespan of the application. For example, if an app requires users to login, it's common to use a stack-based root and transition to either tabs- or SideMenu-based root if login is successful.
Setting root on app launch
RNN exposes an appLaunched listener which is invoked whenever root needs to be set.
On iOS, the app launched event is usually emitted once in the lifespan of the application - when the app is opened for the first time. On Android things become a little bit more complicated. Like on iOS, the event is emitted when an app is opened for the first time. Since the system can destroy the Activity when the app is in the background to free resources, the event is emitted when the app returns to foreground after the activity has been destroyed.
Navigation.events().registerAppLaunchedListener(() => {
Navigation.setRoot({
root: {
}
});
});:::important
registerAppLaunchedListener() must be called as soon as the bundle is executed. Otherwise the event, which is emitted from native to JS, won't be handled at the correct moment in time.
::::
Conditional initial root
A common use case is to set the initial root according to a condition of some sort. For example:
If a user is logged in, show the application main root; otherwise show a login screen.
To do this, simply set the appropriate root according to your needs.
Navigation.events().registerAppLaunchedListener(() => {
if (isUserLoggedIn()) {
setMainRoot();
} else {
setLoginRoot();
}
});Common root structures
<Tabs
defaultValue='stack'
values={[
{ label: 'Stack root', value: 'stack' },
{ label: 'BottomTabs root', value: 'bottomTabs' },
{ label: 'SideMenu root', value: 'sideMenu' }
]
}>
Navigation.setRoot({
root: {
stack: {
children: [
{
component: {
name: 'LOGIN_SCREEN'
}
}
]
}
}
});Navigation.setRoot({
root: {
bottomTabs: {
children: [
{
stack: {
children: [
{
component: {
name: 'FEED_SCREEN'
}
}
]
}
},
{
stack: {
children: [
{
component: {
name: 'CHAT_LIST'
}
}
]
}
},
{
stack: {
children: [
{
component: {
name: 'PROFILE_SCREEN'
}
}
]
}
}
]
}
}
});Navigation.setRoot({
root: {
sideMenu: {
center: {
stack: {
children: [
{
component: {
name: 'HOME_SCREEN'
}
}
]
}
},
left: {
component: {
name: 'MENU_SCREEN'
}
}
}
}
});Docs Docs Screen Lifecycle (`website/docs/docs/docs-screen-lifecycle.mdx`)
id: screen-lifecycle title: Screen Lifecycle sidebar_label: Screen Lifecycle
Any React Component registered with react-native-navigation is enhanced with two additional lifecycle events:
componentDidAppear- called each time a component is revealed to the usercomponentDidDisappear- called each time a component is hidden from user's view as a result of being detached from hierarchy
These methods compliment React's lifecycle methods:
componentDidMount- called once, when a component is attached to hierarchy for the first timecomponentWillUnmount- called once, when a component is destroyed
Mounting
These methods are called in the following order when a component is created and attached to hierarchy.
- constructor()
- render()
- componentDidMount()
- componentDidAppear()
Unmounting
These methods are called when a component is being removed from hierarchy
- componentDidDisappear()
- componentWillUnmount()
Modal
When a modal is displayed, depending on the modalPresentationStyle, content behind it might be detached from hierarchy. This affects the visibility events which are emitted to the content behind the modal.
When Modals with pageSheet or overCurrentContext modalPresentationStyle are displayed, previous content is still visible to the user. Thus componentDidDisappear event is not emitted.
Same is applied when a modal is dismissed. If it was originally presented with pageSheet or overCurrentContext modalPresentationStyle, when that modal is then dismissed, the previous context won't receive a componentDidAppear event.
Overlay
These methods are called in the following order when a component is displayed as an Overlay:
- constructor()
- render()
- componentDidMount()
- componentDidAppear()
:::note
Content displayed behind an Overlay does not receive the componentDidDisappear, since it's still visible to user and attached to the hierarchy.
:::
Docs Docs Showcases (`website/docs/docs/docs-showcases.mdx`)
id: showcases title: Showcases sidebar_label: Showcases
Apps
Hekla for Hacker News
A hacker news client built from the ground up with v2 of react-native-navigation.
It is a tab-based app and utilizes Split Layout for iOS tablets, 3D touch and more.
Starter Kits
- kanzitelli/expo-rnn-starter - Simple yet practical starter with React Native Navigation and Expo Modules, MobX-State-Tree and Redux (pick one) and more goodies inside such as Typescript, RNN Hooks, RN Vector Icons, RN Gesture Handler. As an example, basic Reddit App implemented in two ways: using MobX-State-Tree in
/srcMobXand using Redux in/srcRedux. - atoami/react-native-navigation-redux-starter-kit - React Native Navigation(v2) Starter Kit with Redux, Saga, ESLint, Babel and Jest 😎
- alexwasner/react-native-mobx-firebase-starter - A React Native starter app with React Native Navigation(v2), MobX, Firebase, Lottie and More 🤓
- hsyndeniz/react-native-quick-start - A React Native starter app with React Native Navigation, Redux, Firebase, Social Auth, Phone Auth, Push Notifications, Google Maps, Google Admob, Lottie and More 🤓
- svbutko/react-native-template-strong - Quick, opinionated, documented and strong beginning for your next project. React Native Navigation, TypeScript, Redux ToolKit are the core, and it also includes many more dependencies (tests, linters are included) 💪
Playground
The playground example is a place where we showcase and test all features of react-native-navigation.
git clone https://github.com/wix/react-native-navigation
cd react-native-navigation
yarn install
# for iOS:
yarn pod-install # Install iOS pods
yarn xcode # Opens XCode
yarn start # Starts the packager
# for Android:
# 1. open Android Studio inside "./playground/android"
# 2. start an emulator
yarn start # Start the packager
# Run the app in Simulator or on Device from within XCode/Android StudioDocs Docs SideMenu (`website/docs/docs/docs-sideMenu.mdx`)
id: sideMenu title: Side Menu sidebar_label: Side Menu
SideMenu provides access to destinations in the app from an easy to access menu which is accessible to the user via horizontal swipe gesture or a menu button.
Opening programmatically
To open the side menu programmatically, you'll need to update the visible property of the side menu you'd like to open.
The following snippet shows how to open the left side menu. The menu is opened by setting its visible option to visible.
Navigation.mergeOptions(componentId, {
sideMenu: {
left: {
visible: true,
},
},
});Opening via the hamburger menu
The most common use case is to open the side menu by pressing the hamburger button at the TopBar. To achieve this, listen to the press event of the burger button, and open the side menu as shown above.
const React = require('react');
const Navigation = require('react-native-navigation');
const { View, Text } = require('react-native');
class SideMenuCenterScreen extends React.Component {
static options() {
return {
topBar: {
leftButtons: {
id: 'sideMenu',
icon: require('./menuIcon.png')
}
}
};
}
constructor(props) {
super(props);
Navigation.events().bindComponent(this);
}
render() {
return (
<View>
<Text>Click the hamburger icon to open the side menu</Text>
</View>
);
}
navigationButtonPressed({ buttonId }) {
if (buttonId === 'sideMenu') {
Navigation.mergeOptions(this.props.componentId, {
sideMenu: {
left: {
visible: true
}
}
});
}
}
}Pushing to the center layout from a menu
A common use case when using SideMenus is to interact with the center layout, e.g. pushing a screen to a stack in the center layout when a user clicks on a button from one of the menus.
In order to interact with the stack, we'll first assign it a predefined id. For example:
sideMenu: {
center: {
stack: {
id: 'CenterStack',
children: []
}
},
left: {
...
}
}Now that we've defined an id for out stack, we can go ahead and push our screen to it. If we're pushing while the SideMenu is open, we'll need to make sure we close it by updating the visibility option of the relevant menu (left or right).
Navigation.push('CenterStack', {
component: {
name: Screens.Pushed,
options: {
sideMenu: {
left: {
visible: false
}
}
}
}
});Docs Docs Stack (`website/docs/docs/docs-stack.mdx`)
id: stack title: Stack sidebar_label: Stack
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
A stack is a container layout promoting a hierarchical navigation. It is used for navigating between screens at consecutive levels of hierarchy, steps in a flow or across an app.
The first child in the stack (represented by the children array) is the root and is displayed at the bottom of the stack. The last child in the children array is the child currently being displayed.
In this layout, only a single child screen is visible at any given time and consecutive screen can be added to the top of the stack using the Navigation.push command. Tapping the back button will pop the stack and remove the top most screen.
The stack manages the TopBar at the top of the stack. The TopBar displays the current screens' title and buttons. It can be hidden with the topBar: { visible: false } option. By default, screens are rendered below the TopBar. This behavior can be changed by setting topBar: { drawBehind: true } in the current screens' options.
Layout Examples
<Tabs
defaultValue="single"
values={[
{ label: 'Single child', value: 'single', },
{ label: 'Multiple Children', value: 'multiple', }
]
}>
A stack declared with a single child.
const stack = {
children: [
{
component: {
name: 'MyComponent',
},
},
],
};A stack can be initialized with more than one child, in which case the last child will be the currently displayed child and the first child will be hidden. In this case the back button will be visible automatically, clicking it will go back in the stack revealing the first (previous) child.
Once the root child becomes visible, the back button is hidden.
const stack = {
children: [
{
component: {
name: 'RootComponent',
},
},
{
component: {
name: 'SecondComponent',
},
},
],
};TopBar Buttons
Buttons can be added to the right and left areas of the TopBar. Buttons can have either an icon or a text. They are declared in the the options object and, as with any other option, can be updated dynamically with the Navigation.mergeOptions command.
:::tip Always assign titles to buttons!
When using an icon button on Android, you should always pass a title as well. The title is used when the button is collapsed to the overflow menu and as a tooltip when the button is long pressed.
:::
Overflow menu
It's common practice to group less important actions in a menu or an action sheet.
To do so on iOS, include a button with a menu icon and open an ActionSheet with the relevant actions when the button is clicked.
On Android, use the showAsAction options to control when the button should appear in the menu.
Using a react component in a button
Sometimes we require more from our buttons. In order to support every product need, React Components can be used as custom views of buttons.
To do so, you'll first need to register the view with Navigation, just like you register your components used as screens:
Navigation.registerComponent('ButtonComponent', () => require('./ButtonComponent'));Now you can create buttons which use the component registered with 'ButtonComponent' as their custom view:
topBar: {
rightButtons: [
{
id: 'custom',
component: {
name: 'ButtonComponent',
passProps: {
// Pass initial props to the button here
},
},
},
];
}Updating props of custom buttons
To update props of a mounted component used as a button, you'll first need to assign it a unique id, then call the Navigation.updateProps() command with the id.
Calling the updateProps command will trigger Reacts component lifecycle methods related to props update
// Declare the button and assign it a unique id
topBar: {
rightButtons: [
{
id: 'SomeUniqueId',
component: {
name: 'ButtonComponent',
passProps: {
count: 0,
},
},
},
];
}
// Update props
Navigation.updateProps('SomeUniqueId', {
count: 1,
});Changing buttons dynamically
As buttons are part of a screen's options, they can be modified like any other styling option using the mergeOptions command.
Setting buttons
The following command will set the screen's right buttons. If the screen already has Right Buttons declared - they will be overridden.
Navigation.mergeOptions(this.props.componentId, {
topBar: {
rightButtons: [
{
id: 'myDynamicButton',
text: 'My Button',
},
],
},
});Removing buttons
Buttons can be removed by setting zero buttons, as shown in the snippet below.
Navigation.mergeOptions(this.props.componentId, {
topBar: {
rightButtons: [],
},
});Back Button
The back button is added automatically when two or more screens are pushed into the stack.
Styling the back button
The back button's style can be customized by declaring a backButton options object. This configuration can be part of a screen's static options, or default options.
backButton: {
color: 'red',
icon: require('../../img/customChevron.png')
}Controlling visibility
The back buttons visibility can be controlled with the visible property.
backButton: {
visible: false;
}Changing visibility programmatically
Back button visibility can be changed dynamically using the mergeOptions command. When using a screen's componentId, the change will affect only that specific screen. But when using the stack's id, the change will affect all screens pushed into the stack.
Navigation.mergeOptions(this.props.componentId, {
backButton: {
visible: false,
},
});Interacting programmatically
The Navigation object provides ways to programmatically manipulate the stack.
Interact with the Stack by componentId
Each layout pushed into the stack has an id. When in the context of a component, The component's componentId can be used to interact with a parent stack.
When using a component's componentId, the native implementation knows to perform the command on the parent Stack of this component.
In this example, we push a screen onto the component's parent stack.
const React = require('react');
const Navigation = require('react-native-navigation');
class MyComponent extends React.Component {
onButtonClick = () => {
Navigation.push(this.props.componentId, {
component: {
name: 'PUSHED_SCREEN',
},
});
};
}Interact with the Stack by a predefined id
Sometimes we're required to interact with a specific stack not from the context of a component pushed into it. To do so, assign the stack a predefined id and use it when invoking any stack command.
Navigation.setRoot({
root: {
stack: {
id: 'MyStack', // This is the id we're going to use when interacting with the stack
children: [
{
component: {
name: 'SomeComponent',
},
},
],
},
},
});
function push() {
Navigation.push('MyStack', {
component: {
name: 'PushedScreen',
},
});
}Docs Meta Contributing (`website/docs/docs/meta-contributing.mdx`)
id: meta-contributing title: Contributing sidebar_label: Contributing
Thank you for showing interest in contributing to React-Native-Navigation! This project is developed and maintained by Wix in collaboration with the community.
There are various ways in which you can contribute to this library, not all require knowledge or expertise in native development. Listed below is all you need to get started with your first contribution.
Stack Overflow
Stack Overflow is used by developers to find answers and troubleshoot code. Users are encouraged to post questions on SO and tag them with the wix-react-native-navigation tag. Answering issues on SO is very helpful and beneficial to the community, only this time, there's a personal angle - you can boost your SO ranking and rack up points quickly.
Discord
Another popular communication channel is our Discord channel where users post questions and consult with each other. You're welcome to join the discussions and answer questions. This is also a great place to meet other community members and project maintainers.
Contributing to React-Native-Navigation
iOS
There are 2 ways to test your contribution to this project on iOS:
You can contribute and test your changes using the playground provided.
You can install React-Native-Navigation in a new or existing project and set your project to use your local clone of React-Native-Navigation in the podfile.
The following steps are required in order to make the project work with your local environment:
2.1
Install React-Native-Navigation as you'll usually do in your project, using yarn add react-native-navigation or npm install --save react-native-navigation.
2.2
Add the following configuration to the react-native.config.js file, at the root of your project.
module.exports = {
project: {
ios: {},
android: {},
},
dependencies: {
'react-native-navigation': {
platforms: {
ios: null,
android: null
},
},
},
};2.3
Clone the project using git clone https://github.com/wix/react-native-navigation at your desired location.
2.4
Add the following line in the Podfile:
pod 'ReactNativeNavigation', :path => '/path/to/cloned/react-native-navigation'You're done. Happy coding!
GitHub Issues
Open an issue
Found a bug? Missing a feature? Go ahead and open an issue! Make sure to add all details mentioned in the issue template. If you're interested in suggesting a big change, please speak to one of the admins on Discord to coordinate your effort.
Respond to issues
Although the issue tracker is used solely for bug reports, issues are frequently opened for questions or to request assistance. As the project grows in popularity, more issues remain unanswered for long periods of time.
Some issues can be trivial and easy to pinpoint - a missing import statement or apostrophe, wrong layout structure, etc. If you're an experienced user, helping out newcomers can be quite satisfying and allows maintainers to focus on features and bug fixes.
Some issues are tagged as "looking for contributors". These issues have been recognized by the team, but aren't prioritized by Wix developers due to lack of time or for some other reason. We leave these issues to our community and you're more than welcome to take a crack at them. If you'd like to submit a PR, see these instructions on how to run the project locally.
Provide reproductions
Reproducing bugs takes time. Lots of time. Usually that's because an issue is lacking important information, which then causes lots of back and forth communication between maintainers and users. Help us address more bugs and issues by providing reproductions.
Providing reproductions improves the chances of an issue being prioritized by maintainers!
If an issue is reproduced with a specific combination of options, posting these options will usually suffice. If a specific layout structure is involved or specific actions need to be performed in a certain order - then we ask that you fork the project and push a branch with a reproduction to the Playground app.
Check out the list of issues requiring reproductions.
Submitting PRs
So you've fixed a bug or added a feature and you're ready to submit a pull request 🎉🎊 Make sure the title is clear and describes the reason for the PR. Please include any information you can which will help us understand the reasons for the PR, what it fixes and what it changes. Please include before/after pictures/gifs when appropriate.
Workflow
This project is driven by tests. Before implementing any feature or fixing any bug, a failing test (e2e or unit or both) should be added, depending on the environment of where the fix should be implemented. For example, for an API change, a failing e2e should be written. For a small bug fix in Android, for example, a unit test in Android should be added.
This will ensure good quality throughout the life of the project and will help avoid unexpected breakages.
No PR will be accepted without adequate test coverage.
If you need help running the project, have a look at the Playground app section. You can run the tests using the scripts below.
Tests and the Playground app
Besides an overview of basic React Native Navigation functionality, the Playground app can (and rather should) be used for e2e tests and reproductions. If your change requires an e2e, add it to the playground app, to the appropriate screen. Again, quick setup instructions available in Playground app section of these docs.
:::tip
If a screen contains too many buttons, e2e tests might fail if the button is positioned out of screen bounds because Detox matchers detect it's invisible.
:::
Enable linter
The project uses ESLint with Prettier to ensure code style consistency across the codebase. Make sure to install these plugins in your IDE.
A pre-commit hook will verify that the linter passes when committing.
Project Folder Structure
| Folder | Description |
|---|---|
src |
TypeScript sources and unit tests |
android |
Android sources and unit tests |
ios |
iOS sources and unit tests |
lib |
Compiled JavaScript and TypeScript definitions (generated by react-native-builder-bob) |
lib/module/index.js |
The entry point for import Navigation from 'react-native-navigation' |
playground |
The playground app workspace. Contains its own src, android, ios, and package.json. All e2e tests run against this app. |
playground/e2e |
detox e2e tests on both Android and iOS |
integration |
Misc JavaScript integration tests, proving integration with other libraries like redux |
scripts |
All build and test scripts |
autolink |
Autolinking scripts for React Native CLI |
Scripts
This project uses Yarn workspaces. The root workspace contains the main library, and the playground workspace contains the playground app for testing.
| Command | Description |
|---|---|
yarn install |
installs dependencies for all workspaces |
yarn prepare |
compiles TypeScript sources ./src into JavaScript ./lib (runs automatically on install) |
yarn clean |
cleans all build directories, stops packager, fixes flakiness by removing watchman cache, etc. |
yarn start |
starts the react-native packager for local debugging |
yarn xcode |
for convenience, opens xcode in this project |
yarn pod-install |
installs iOS pods (runs yarn workspace react-native-navigation-playground pod-install) |
yarn install-android |
builds playground debug/release version and installs on running android devices/emulators. Options: -- --release |
yarn uninstall-android |
uninstalls playground from running android devices/simulators |
yarn test-js |
runs javascript tests and coverage report |
yarn test-autolink |
runs autolink tests |
yarn test-unit-ios |
runs ios unit tests in debug/release Options: -- --release |
yarn test-unit-android |
runs android unit tests in debug/release Options: -- --release |
yarn test-snapshot-ios |
runs ios snapshot tests Options: -- --release |
yarn test-e2e-ios-ci |
runs the ios e2e tests using detox in CI mode (release, multi-device) |
yarn test-e2e-android-ci |
runs the android e2e tests using detox in CI mode (release, multi-device) |
yarn workspace react-native-navigation-playground test-e2e-ios |
runs the ios e2e tests using detox in debug/release Options: -- --release |
yarn workspace react-native-navigation-playground test-e2e-android |
runs the android e2e tests using detox in debug/release Options: -- --release |
yarn test-all |
runs all tests in parallel (js, autolink, unit, snapshot, e2e) |
Docs SideMenu Disable (`website/docs/docs/sideMenu-disable.mdx`)
id: sideMenu-disable title: Disable Open and Close gesture sidebar_label: Open and Close Gesture
To open the side menu programmatically, you'll need to update the visible property of the side menu you'd like to open.
The following snippet shows how to open the left side menu.
Navigation.mergeOptions(componentId, {
sideMenu: {
left: {
visible: true,
},
},
});Open by pressing on the hamburger menu
The most common use case is to open the side menu by pressing the hamburger button in the TopBar. To achieve this, listen to the press event of the burger button, and open the side menu as shown above.
const React = require('react');
const Navigation = require('react-native-navigation');
const { View, Text } = require('react-native');
class SideMenuCenterScreen extends React.Component {
static options() {
return {
topBar: {
leftButtons: {
id: 'sideMenu',
icon: require('./menuIcon.png')
}
}
};
}
constructor(props) {
super(props);
Navigation.events().bindComponent(this);
}
render() {
return (
<View>
<Text>Click the hamburger icon to open the side menu</Text>
</View>
);
}
navigationButtonPressed({ buttonId }) {
if (buttonId === 'sideMenu') {
Navigation.mergeOptions(this, {
sideMenu: {
left: {
visible: true
}
}
});
}
}
}Docs Stack BackButton (`website/docs/docs/stack-backButton.mdx`)
id: stack-backButton title: The Back button sidebar_label: Back button
The back button is added automatically when two or more screens are pushed into the stack.
Styling the back button
The back button's style can be customised by declaring a backButton options object. This configuration can be part of a screen's static options, or default options.
backButton: {
color: 'red',
icon: require('../../img/customChevron.png')
}Controling visibility
The back buttons visbility can be controlled with the visible property.
backButton: {
visible: false
}Changing visbility programmatically
Back button visibility can be changed dynamically using the mergeOptions command. When using a screen's componentId, the change will affect only that specific screen. But when using the stack's id, the change will affect all screens pushed into the stack.
Navigation.mergeOptions(this.props.componentId, {
backButton: {
visible: false
}
});Handling the back button
Handling the back button is not possible. However, you can set a left button with a chevron and handle it like you'd handle any other button and calling Navigation.pop when desired.
Docs Stack BackNavigation (`website/docs/docs/stack-backNavigation.mdx`)
id: stack-backNavigation title: Back Navigation sidebar_label: Back navigation
Disabling back navigation
Handling back navigation
Docs Stack Buttons (`website/docs/docs/stack-buttons.mdx`)
id: stack-buttons title: TopBar Buttons sidebar_label: TopBar Buttons
Buttons can be added to the right and left areas of the TopBar. Buttons can have either an icon or a text. They are declared in the the options object and, as with any other option, can be updated dynamically with the Navigation.mergeOptions command.
When using an icon button on Android, you should always pass a title as well. The title is used when the button is collapsed to the overflow menu and as a tooltip when the button is long pressed.
Overflow menu
It's common practice to group less important actions in a menu or an action sheet.
To do so on iOS, include a button with a menu icon and open an ActionSheet with the relevant actions when the button is clicked.
On Android, use the showAsAction options to control when the button should appear in the menu.
Left button
Left buttons behave like right buttons with two caveats on Android:
- Only a single left button is alowed
- Textual left button isn't supported
Using a react component in a button
⚠️At the moment, Android only supports using custom buttons in
rightButtons.
Sometimes we require more from our buttons. In order to support every product need React Components can be used as custom views of buttons.
To do so, you'll first need to register the view with Navigation, just like you register your components used as screens:
Navigation.registerComponent('ButtonComponent', () => require('./ButtonComponent'));Now you can create buttons which use the component registered with 'ButtonComponent' as thier custom view:
topBar: {
rightButtons: [
{
component: {
name: 'ButtonComponent',
passProps: {
// Pass initial props to the button here
},
},
},
];
}Updating props of custom buttons
To update props of a mounted compoennt used as a button, you'll first need to assign it a unique id, then call the Navigation.updateProps() command with the id.
Calling the updateProps command will trigger React's component lifecycle methods related to props update
// Declare the button and assign it a unique id
topBar: {
rightButtons: [
{
id: 'SomeUniqueId',
component: {
name: 'ButtonComponent',
passProps: {
count: 0
},
},,
},
];
}
// Update props
Navigation.updateProps('SomeUniqueId', {
count: 1,
});Changing buttons dynamically
As buttons are part of a screen's options, they can be modified like any other styling option using the mergeOptions command.
Setting buttons
The following command will set the screen's right buttons. If the screen already has Right Buttons declared - they will be overridden.
Navigation.mergeOptions(this.props.componentId, {
topBar: {
rightButtons: [
{
id: 'myDynamicButton',
text: 'My Button',
},
],
},
});Removing buttons
Buttons can be removed by setting zero buttons, as shown in the snippet below.
Navigation.mergeOptions(this.props.componentId, {
topBar: {
rightButtons: [],
},
});Docs Stack Programmatically (`website/docs/docs/stack-programmatically.mdx`)
id: stack-programmatically title: Interact programmatically with the Stack sidebar_label: Interact programmatically
The Navigation object provides ways to programmatically manipulate the stack.
Interact with the Stack by componentId
Each layout pushed into the stack has an id. When in the context of a component, The component's componentId can be used to interact with a parent stack.
When using a component's componentId, the native implementation knows to perform the command on the parent Stack of this component.
In this example, we push a screen onto the component's parent stack.
const React = require('react');
const Navigation = require('react-native-navigation');
class MyComponent extends React.Component {
onButtonClick = () => {
Navigation.push(this.props.componentId, {
component: {
name: 'PUSHED_SCREEN'
}
});
}
}Interact with the Stack by a predefined id
Sometimes we're required to interact with a specific stack not from the context of a component pushed into it. To do so, assign the stack a predefined id and use it when invoking any stack command.
Navigation.setRoot({
root: {
stack: {
id: 'MyStack', // This is the id we're going to use when interacting with the stack
children: [
{
component: {
name: 'SomeComponent'
}
}
]
}
}
});
function push() {
Navigation.push('MyStack', {
component: {
name: 'PushedScreen'
}
});
}Docs Style Animations (`website/docs/docs/style-animations.mdx`)
id: style-animations title: Animations sidebar_label: Animations
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import useBaseUrl from '@docusaurus/useBaseUrl';
Various UI elements can be animated by declaring animation options.
You can change the default animations for various commands, like push and pop, and even animate elements in
your screens during screen transitions.
Animation properties
The following properties can be animated with our animation framework:
- x - Translate the view to a coordinate along the x axis.
- y - Translate the view to a coordinate along the y axis.
- translationX - Translate the view along the x axis relative to its current x position.
- translationY - Translate the view along the y axis relative to its current y position.
- alpha - Apply alpha animation to the view. A value of 0 means the view is not visible, 1 means it's visible.
- scaleX - Scale the view on the x axis. A value of 1 means that no scaling is applied.
- scaleY - Scale the view on the y axis. A value of 1 means that no scaling is applied.
- rotationX - Applies rotation around the x axis (in degrees, passed as a float).
- rotationY - Applies rotation around the y axis (in degrees, passed as a float).
- rotation - Rotates the view on all axis. Positive values result in clockwise rotation.
Layout animations
Root animations
Animating switches between roots, using the enter/exit animations that grants control on how each root is animated when entering or exiting the screen.
This can be acheived by modifying the default options (setDefaultOptions for example) to be like:
options:{
animations: {
push:{...},
setRoot: {
enter: {
waitForRender: true,
enabled: false,
translationY: {
from: 0,
to: 1,
duration: 3,
},
},
exit:{...}
},
},
};In Playground, you can try it by setting useCustomAnimations in flags.ts file.
Stack animations
When animating screens in and out of a stack, there are three elements you can work with:
- content - screen being pushed or popped
- topBar - stack's TopBar
- bottomTabs - if stack is a child of a bottomTabs layout, we can control the
hideandshowanimations of the bottomTabs.
The following example demonstrates how to replace the default push and pop animations with slide animations.
<Tabs
defaultValue="push"
values={[
{ label: 'Push', value: 'push', },
{ label: 'Pop', value: 'pop', },
]
}>
options: {
animations: {
push: {
content: {
translationX: {
from: require('react-native').Dimensions.get('window').width,
to: 0,
duration: 300
}
}
}
}
}options: {
animations: {
pop: {
content: {
translationX: {
from: 0,
to: -require('react-native').Dimensions.get('window').width,
duration: 300
}
}
}
}
}Modal animations
Modal animations are declared similarly to stack animations, only this time we animate the entire view and not only part of the UI (content).
The appearing modal can play custom enter animation and exit for the disappearing screen when showing/dismissing modal.
<Tabs
defaultValue="show"
values={[
{ label: 'Show', value: 'show', },
{ label: 'Dismiss', value: 'dismiss', },
]
}>
The following example demonstrates how to show a modal with a fade-in animation.
:::warning Deprecation warning
Consider using enter, exit animations instead.
options: {
animations: {
showModal: {
alpha: {
from: 0,
to: 1,
duration: 300
}
}
}
}:::
options: {
animations: {
showModal: {
enter :{
enabled:true,
alpha: {
from: 0,
to: 1,
duration: 300
}
},
exit :{
enabled:true,
alpha: {
from: 1,
to: 0,
duration: 300
}
}
}
}
}The following example demonstrates how to dismiss a modal with a fade-out animation.
:::warning Deprecation warning
Consider using enter, exit animations instead.
options: {
animations: {
dismissModal: {
alpha: {
from: 1,
to: 0,
duration: 300
}
}
}
}:::
options: {
animations: {
dismissModal: {
exit:{
enabled:true
alpha: {
from: 1,
to: 0,
duration: 300
}
},
enter:{
enabled:false
alpha: {
from: 0,
to: 1,
duration: 300
}
}
}
}
}Shared element transitions
Shared element transitions allow us to provide visual continuity when navigating between destinations. This also focuses user attention on a particular significant element, which then also gives such user better context when transitioning to some other destination.
:::caution
At the moment, the transition is available for push and pop commands. We are working on expanding supported commands to show/dismiss modal and change BottomTabs.
:::
Transition breakdown
Let's take a more in-depth look at the following example, which you can find in the playground app:
Source screen and the Destination screen
Four elements are animated in this example. Let's list the elements involved in the transition and note which properties are being animated.
image - the item image is animated to the next screen.
- image scale (resizeMode)
- position on screen
image background - each item has a "shadow" view which transitions to the next screen and turns into a colorful header.
- scale
- position on screen
- color
title - the title of the item is animated to the next screen.
- font size
- font color
- position on screen
Description - the description of the item in the destination screen.
- fade-in
- translation y
Implementing shared element transitions
Step 1 - set a nativeID prop to elements in the source screen
In order for RNN to be able to detect the corresponding native views of the elements,
each element must include a unique nativeID prop.
<Image
source={item.image}
nativeID={`image${item.id}`}
style={styles.image}
resizeMode={'contain'}
/>Step 2 - set a nativeID prop to elements in the destination screen
<Image source={this.props.image} nativeID={`image${this.props.id}Dest`} style={styles.image} />Step 3 - Declare the shared element animation when pushing the screen
Navigation.push(this.props.componentId, {
component: {
name: Screens.CocktailDetailsScreen,
passProps: { ...item },
options: {
animations: {
push: {
sharedElementTransitions: [
{
fromId: `image${item.id}`,
toId: `image${item.id}Dest`,
interpolation: { type: 'linear' }
},
],
},
},
},
},
});The interpolation property is an object which consists of a type property, and optional parameters to further configure the interpolation function. The following types are supported:
'accelerate': Begin building up speed until destination has been reached.factor: The acceleration factor. Higher values look faster.
'decelerate': Start at a high speed and slow down to settle.factor: The acceleration factor. Higher values look slower.
'accelerateDecelerate': Begin building up speed until half of the animation is complete, then begin slowing down to settle.'decelerateAccelerate': Start at a high speed and slow down until half of the animation is complete, then speed up again to settle.'linear': Linearly move towards the destination.'overshoot': Begin building up speed and overshoot the destination, then fling back to settle.tension: The tension of the overshoot animation defines how far the object can overshoot. Default:1
'spring': Acts like an actual spring. Depending on the configuration, this interpolation can overshoot the destination multiple times, or be very stiff and smoothly ease towards the destination.mass: The mass defines how "heavy" the object is. Default:3damping: The damping property specifies how much the moving object is being slowed down. Higher values can lead to overdamping, lower values to underdamping. Default:500stiffness: The stiffness property defines how stiff the animated object is. Default:200
:::caution
Since a Spring interpolation is not a time based animation, you have to manually tweak the configuration to match the duration of the animation, otherwise the remaining time of the Spring interpolation will get skipped.
:::
Element Transitions
Element transitions also allow you to animate elements during shared element transitions.
Recreating
Step 1 - Set a nativeID prop to the element you'd like to animate
An element can either be in the source or destination screen.
<Text nativeID="description" style={styles.description}>
{this.props.description}
</Text>Step 2 - Declare the element animation when pushing the screen
Navigation.push(this.props.componentId, {
component: {
name: Screens.CocktailDetailsScreen,
passProps: { ...item },
options: {
animations: {
push: {
elementTransitions: [
{
id: 'description',
alpha: {
from: 0, // We don't declare 'to' value as that is the element's current alpha value, here we're essentially animating from 0 to 1
duration: SHORT_DURATION,
},
translationY: {
from: 16, // Animate translationY from 16dp to 0dp
duration: SHORT_DURATION,
},
},
],
},
},
},
},
});Peek and Pop (iOS 11.4+)
react-native-navigation supports Peek and pop feature in iOS 11.4 and newer.
This works by passing a ref to a component you want to transform into peek view. We have included a handy component to handle all the touches and ref for you:
const handlePress ({ reactTag }) => {
Navigation.push(this.props.componentId, {
component {
name: 'previewed.screen',
options: {
preview: {
reactTag,
height: 300,
width: 300,
commit: true,
actions: [{
title: "Displayed Name",
id: "actionId",
style: 'default', /* or 'selected', 'destructive'*/
actions: [/*define a submenu of actions with the same options*/]
}]
},
},
},
});
};
const Button = (
<Navigation.TouchablePreview
touchableComponent={TouchableHighlight}
onPress={handlePress}
onPressIn={handlePress}
>
<Text>My button</Text>
</Navigation.TouchablePreview>
);All options except for reactTag are optional. Actions trigger the same event as navigation button presses. To react when a preview is committed, listen to the previewCompleted event.
Docs Style Constants (`website/docs/docs/style-constants.mdx`)
id: style-constants title: Constants sidebar_label: Constants
React Native Navigation exposes a set of constants which can be used to get the dimensions of various navigation elements on the screen: StatusBar, TopBar and BottomTabs.
When are constants evaluated
Some of the values exposed through the constants API are actually evaluated only after the UI is created (setRoot has been called) and therefore are not accessible through static getters.
For example, if you need to get the height of the BottomTabs, you'll first need to have BottomTabs visible on the screen and only then retrieve their height via the constants API.
:::important
We recommend you don't cache the actual constants object returned by await Navigation.constants() or Navigation.constantsSync() as it might not be accurate later on when, for example, a new root is set or orientation changes.
:::
API
As explained above, constants are evaluated in native each time the API is invoked. There are two methods for accessing constants:
Navigation.constants(), which is async and thus returns a promiseNavigation.constantsSync(), which returns the constants synchronously, blocking the main thread
Where possible, it is better to use the async method, since this will not block the main thread.
const { Navigation } = require('react-native-navigation');
const {
statusBarHeight,
topBarHeight,
bottomTabsHeight
} = await Navigation.constants();But, if you need to access the constants synchronously (e.g. within the render() method of a React component), you can do so as follows:
const { Navigation } = require('react-native-navigation');
const {
statusBarHeight,
topBarHeight,
bottomTabsHeight
} = Navigation.constantsSync();Docs Style Edge To Edge (`website/docs/docs/style-edge-to-edge.mdx`)
id: style-edge-to-edge title: Edge-to-Edge (Android) sidebar_label: Edge-to-Edge
Starting with Android 15 (API 35), the system enforces edge-to-edge display by default — app content draws behind the transparent status bar and navigation bar. React Native Navigation provides built-in support for this behavior, disabled by default.
Enabling edge-to-edge
To enable edge-to-edge, set windowOptOutEdgeToEdgeEnforcement to false in your app's theme:
<resources>
<style name="AppTheme" parent="Theme.AppCompat.DayNight.NoActionBar">
<item name="android:windowOptOutEdgeToEdgeEnforcement">false</item>
</style>
</resources>NavigationActivity reads this attribute on startup. When set to false on API 35+, it calls EdgeToEdge.enable() to draw content behind system bars. On older API levels this attribute has no effect.
How it works
When edge-to-edge is active:
- Status bar and navigation bar become transparent. React Native Navigation manages view-based background overlays for both, replacing the deprecated
window.statusBarColor/window.navigationBarColorAPIs. SafeAreaViewreceives correct navigation bar insets, so content is automatically inset from the system navigation bar.- Bottom tabs account for navigation bar insets when positioning.
- Navigation mode detection — the library detects gesture navigation vs 3-button navigation at runtime and applies appropriate default styling for the navigation bar background.
When edge-to-edge is not active (the default), behavior is unchanged from previous versions.
Custom behavior
NavigationActivity.enableEdgeToEdge() is a protected method that controls when edge-to-edge is enabled. Override it in your MainActivity to customize the decision logic, and call activateEdgeToEdge() to enable it:
class MainActivity : NavigationActivity() {
override fun enableEdgeToEdge() {
// Always enable edge-to-edge regardless of theme or API level
activateEdgeToEdge()
}
}activateEdgeToEdge() handles both the Android system call and the internal navigation framework state. Always use it instead of calling EdgeToEdge.enable() directly.
Styling system bars
Status bar and navigation bar colors can still be controlled per-screen via options:
options: {
statusBar: {
backgroundColor: 'white',
style: 'dark'
},
navigationBar: {
backgroundColor: '#000000'
}
}Use navigationBar.drawBehind with edge-to-edge to draw content behind the system navigation bar while keeping it visible (gesture pill or 3-button bar stay on screen). Layout insets skip the navigation bar height; RNN paints an optional scrim via backgroundColor:
// Transparent nav bar — content shows through, pill visible
options: {
navigationBar: {
visible: true,
drawBehind: true,
backgroundColor: 'transparent'
}
}
// Opaque scrim over content behind the nav bar
options: {
navigationBar: {
visible: true,
drawBehind: true,
backgroundColor: '#20303C'
}
}Without drawBehind, an opaque backgroundColor reserves bottom inset so content sits above the navigation bar (traditional behavior). See Navigation Bar options for visible and other fields.
These options work in both edge-to-edge and non-edge-to-edge modes. In edge-to-edge mode, the colors are applied to the view-based overlays rather than the deprecated window APIs.
Docs Style Fonts (`website/docs/docs/style-fonts.mdx`)
id: style-fonts title: Changing fonts sidebar_label: Changing fonts
Before you begin using your own fonts, you'll first need to add them to the app project.
- Android - add the
.ttfor.otffiles tosrc/main/assets/fonts/ - iOS - follow this guide
Once we've added the font files to our project, we can star using them in options.
:::note
When declaring fonts in options, use only the file name without the file type suffix.
:::
BottomTab font
options: {
bottomTab: {
fontFamily: 'helvetica'
}
}Button font
options: {
rightButtons: [
{
id: 'SAVE_BUTTON',
text: 'Save',
fontFamily: 'helvetica'
}
],
leftButtons: [
{
id: 'CANCEL_BUTTON',
text: 'Cancel',
fontFamily: 'helvetica'
}
]
}Title font
options: {
topBar: {
title: {
fontFamily: 'helvetica'
}
}
}Subtitle font
options: {
topBar: {
subtitle: {
fontFamily: 'helvetica'
}
}
}Back button
In iOS the back button will display the title of the previous screen. Use the backButton option to modify its font family.
options: {
topBar: {
backButton: {
fontFamily: 'helvetica'
}
}
}Docs Style Orientation (`website/docs/docs/style-orientation.mdx`)
id: style-orientation title: Orientation sidebar_label: Orientation
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
Locking orientation
Orientation can be locked either in the layout level, or across the app via default options. You can declare orientations you'd like your app to support in default options.
<Tabs
defaultValue="defaultOptions"
values={[
{ label: 'Locking orientation in default options', value: 'defaultOptions', },
{ label: 'Locking root layout orientation', value: 'root', },
{ label: 'Showing landscape modal', value: 'modal', }
]
}>
Setting orientation in default options will affect all screens in all hierarchy levels. It's still possible to override orientation for specific screens.
Navigation.setDefaultOptions({
layout: {
orientation: ['portrait'],
},
});Applying orientation in the root level will affect all screens in the root hierarchy level. It will not apply to modals.
Navigation.setRoot({
root: {
bottomTabs: {
options: {
layout: {
orientation: ['portrait']
}
},
children: [
...
]
}
}
});The following example demonstrates how to show a modal in landscape orientation.
Navigation.showModal({
component: {
name: 'VideoPlayer'
options: {
layout: {
orientation: ['landscape']
}
}
}
});Changing orientation dynamically
Changing orientation dynamically through Navigation.mergeOption() is supported only on Android.
Navigation.mergeOptions(this.props.componentId, {
layout: {
orientation: ['landscape'],
},
});Docs Style Screens (`website/docs/docs/style-screens.mdx`)
id: style-options title: Styling with options sidebar_label: Options
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
A Screen's look and feel is configured with an Options object. Options can be applied to screens in three ways. Each method of applying options has benefits and draw backs which are important be aware of so we can use the right tool for the job.
Command options
Options can be passed to layouts as part of a command. For example when pushing a screen. Options passed in commands are typically dynamic options which are determined at runtime.
In the example below we're pushing a user profile screen and we'd like show the user name in the title. To do so, we set the title in the component's options.
Notice how the title is actually inferred from the user object which is set in passProps. While this works perfectly fine, declaring the title each time a screen is pushed is a bit tedious and error prone. Later on we'll see a more convenient method to declare dynamic options which are inferred from props.
Static options
Static options are options that are declared statically on the component class. Whenever a screen has a known predefined style, static options should be used as they are evaluated immediately when the screen is created.
<Tabs
defaultValue="class"
values={[
{ label: 'Class Component', value: 'class', },
{ label: 'Functional Component', value: 'functional', }
]
}>
Handle props in static options
Sometimes a screen's style includes properties defined in the calling scope where the screen is used. Like in the user profile screen we've seen above where the the user name is used as the TopBar title. As the user name is unique for each profile, it can't be defined explicitly in the static options.
Luckily, we can access props from static options and set the title from static options! Let's see how this is done:
<Tabs
defaultValue="class"
values={[
{ label: 'Class Component', value: 'class', },
{ label: 'Functional Component', value: 'functional', }
]
}>
Following this approach we can determine options that are dependent on other external factors, such as experiments or A/B tests.
Merge options
The mergeOptions command is used to update a layout's style. Keep in mind that the merge happens in native, and not ins JS. This meaning that if mergeOptions is called to update a screen's options, the static options declared on the React Component are not affected by mergeOptions.
The following example shows how to update a TopBar background color to red.
:::warning
Avoid using mergeOptions in a screen's constructor or in componentDidMount!
:::
When a screen first appears, it's constructor and componentDidMount functions are called followed by a componentDidAppear event. (read more about screen lifecycle here).
Developers might be tempted to call mergeOptions in constructor or componentDidMount to update options. Calling mergeOptions before the componentDidAppear event has been received has two very negative effects:
- Updating certain options can trigger additional layout and draw cycles which can have serious impact on performance.
- Options applied dynamically with the
mergeOptionscommand might be handled in native after the screen has already appeared. This will result in noticeable flickering and artifacts as options are updated after the initial options have been applied and are visible to the user.
Docs Style Statusbar (`website/docs/docs/style-statusbar.mdx`)
id: style-statusBar title: StatusBar sidebar_label: StatusBar
The StatusBar appearance is controlled through options.
For example, the following options will change the StatusBar background color to white will use dark colors for the StatusBar content (time, battery information, notification icons etc)
options: {
statusBar: {
backgroundColor: 'white',
style: 'dark'
}
}:::warning Compatibility with StatusBar component
React native's StatusBar component is incompatible with React Native Navigation and you should avoid using it.
:::
Changing StatusBar style dynamically
As the StatusBar is controlled through options, it can be configured dynamically by calling Navigation.mergeOptions with the desired StatusBar options.
For example, here's how you would hide the StatusBar dynamically
Navigation.mergeOptions(this.props.componentId, {
statusBar: {
visible: false
}
});How to keep current StatusBar color when displaying screens
When screens are displayed, the StatusBar color always changes to the color associated with the current screen. If a color isn't specified for a given screen (and thus for the StatusBar), the default (System default or from defaultOptions) color is used. Sometimes you might want to keep the current StatusBar color, for example when displaying an alert or a toast. To keep StatusBar color unchanged when displaying a screen, use null as a StatusBar color.
options: {
statusBar: {
backgroundColor: null
}
}Docs Style Theme (`website/docs/docs/style-theme.mdx`)
id: style-theme title: Themes sidebar_label: Themes
A theme is a type of style which is applied to the entire app. It allows you to declare
a consistent style to all Navigation components such as the TopBar and BottomTabs and also
to system elements like the StatusBar and NavigationBar.
Dark mode
Dark mode support for navigation UI components can be done via colors, perferably via setting a "Theme" in the default options, as in the example below.
The trigger for the change is the system wide Dark Mode toggle.
Navigation.setDefaultOptions({
topBar: {
background: {
color: {
light:'white',
dark:'black'
}
},
title:{
color: {
light:'black',
dark:'red'
}
}
}
});
## Applying a theme
Themes are applied using `Navigation.setDefaultOptions()` which must be called **before** `Navigation.setRoot()` is called.
```js
// Set the default topBar background color to red
Navigation.setDefaultOptions({
topBar: {
background: {
color: 'red'
}
}
});
// That stack's topBar background color will be red, as is set in default options
Navigation.setRoot({
root: {
stack: {
children: [
...
]
}
}
});Conditional themes with Options Processor
Some style requirements can't be facilitated with the defaultOptions api. For example, an app may have a unique theme for screens displayed in a modal or require that modals have a default dismiss button in the TopBar. An Options Processor allow us to mutate the options object of each screen and layout right before they are displayed.
Lets see how we can leverage this API to add a dismiss button to the TopBar when showing a modal.
The Options Processors can also be used to set default options for buttons, here's how:
Conditional themes with Layout Processor
Layout Processor is similar in concept to the Options Processor discussed above. While Options Processor is invoked for each options object, Layout Processor is invoked once on the entire (layout)['../api/layout-layout/'] tree passed to the Navigation command.
Through the Layout Processor we can access the layout and it's children, and mutate both options and passProps before the layout is displayed. In this example we iterate on the stack's children and set a dark TopBar color according to a theme property in the child's props.
Changing theme dynamically
Apps can have multiple themes and sometimes you might need to change theme dynamically. To change current theme, simply call Navigation.setDefaultOptions() with updated theme options, following that with a call to Navigation.setRoot(). The reason we need to setRoot once more is because Navigation.setDefaultOptions() does not apply options to screens which had already been created.
Docs Third Party Mobx (`website/docs/docs/third-party-mobx.mdx`)
id: third-party-mobx title: MobX sidebar_label: MobX
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import useBaseUrl from '@docusaurus/useBaseUrl';
MobX is one of the most popular state management libraries used by applications sized from small to large.
With the introduction of the new React Context API, MobX can now be very easily integrated in React Native Navigation
projects.
:::info Note
With the introduction of the new Context API, there is no need to use Provider pattern with MobX and you
can now just use the React Context API.
Also the example uses mobx-react-lite but you can use the normal mobx-react.
:::
Sharing a store between multiple screens
In the example below we will be creating a small Counter app. We will learn how to integrate Mobx with React-Native-Navigation and demonstrate how updating the store from one component, triggers renders in other components connected to the same store.
Once you finish implementing the example, your screen should look similar to this:
<img width="30%" src={useBaseUrl('/img/mobx_counter.png')} />
Step 1 - Create a Counter store
Let's first create a counter store using MobX. Our store has a single count object and two methods to increment and decrement it.
<Tabs
defaultValue="makeObservable"
values={[
{ label: 'MakeObservable', value: 'makeObservable' },
{ label: 'Decorator', value: 'decorator' },
]}
From [email protected] decorators have become an opt-in feature. To enable decorators for mobx, follow the official guide.
Step 2 - Consuming the store
You can consume the Counter store in any React components using useCounterStore hook or React.useContext
How to use MobX persistent data
Often the app will require a persistent data solution and with MobX you can use mobx-react-persist.
It only takes few extra steps to integrate the library.
Also the integration assumes that you are using the decorator pattern for mobx.
Docs Third Party React Context (`website/docs/docs/third-party-react-context.mdx`)
id: third-party-react-context title: React Context API sidebar_label: React Context API
React Context API
React Context API provides a easy way to pass data through the component tree without having to pass props down
manually at every level. You can find more about the Context API in React documentation.
You can use the React Context API with React Native Navigation with a limitation. In this example, we are going to create a screen which uses the Counter Context.
:::important Limitation
As RNN screens are not part of the same component tree, updating the values in the shared context does not trigger a re-render across all screens.
However you can still use the React.Context per RNN screen component tree.
If you need to trigger a re-render across all screens, there are many popular third party libraries such as
MobX or Redux.
:::
Create a Counter context
Register the screen
When registering the screen that will be using the Counter Context, we need to wrap it
with the Counter Context Provider we created earlier.
Consuming the context
You can consume the Counter Context any way you want such as Provider.Consumer or React.useContext like in the
example below.
Docs Third Party React Native Vector Icons (`website/docs/docs/third-party-react-native-vector-icons.mdx`)
id: third-party-react-native-vector-icons title: react-native-vector-icons sidebar_label: react-native-vector-icons
react-native-vector-icons is a very popular library for using SVG icons in react-native, which keeps large static images
out of your app while providing crisp icons. Using this library with React Native Navigation is not difficult, but does take
a few extra steps..
Using SVG icons in BottomTabs or TopBarButtons
In the example below we will detail a simple app that pre-loads several icons from react-native-vector-icons to be used in our navigation. The example assumes
you have worked through the installation and optional steps to support custom fonts
according to the react-native-vector-icons project.
Step 1 - Choose icons and load image source.
The core issue here is that React Native Navigation wants 'images' for its icons while react-native-vector-icon typically works as a component. However,
there is a way around this, by using the getImageSourceSync method exposed by react-native-vector-icons.
You can use this directly when specifying an icon in an react-native-navigation option, or you can group them all together into a central object like so:
Step 2 - Launching the app.
We now launch our app in the usual fashion.
Step 3 - Using NavIcons in components.
One of the nice features of React Native Navigation is that it is possible to specify the navigation 'options' on each individual component. This still works with NavIcons
framework we have setup:
Docs Third Party Typescript (`website/docs/docs/third-party-typescript.mdx`)
id: third-party-typescript title: TypeScript sidebar_label: TypeScript
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
Strongly typed components
Both functional and class components can be typed to get the benefits of strongly typed options and props.
<Tabs
defaultValue="clazz"
values={[
{ label: 'Class Component', value: 'clazz' },
{ label: 'Functional Component', value: 'functional' },
]}
Typed props in commands
Arguments are passed to components view the passProp. This is a common source for errors as these props tend to change overtime. Luckily we can type the passProps property to avoid these regressions. The example below shows how to declare types for passProps when pushing a screen.
The following commands accept typed passProps:
- push
- setStackRoot
- showModal
- showOverlay
Website Versioned Docs Version 6.12.2 Docs Community Libraries (`website/versioned_docs/version-6.12.2/docs/community-libraries.mdx`)
id: community-libraries title: Community libraries sidebar_label: Community libraries
React Native Navigation has an active community with a bunch of support libraries.
React Native Navigation Hooks :
A set of React hooks for React Native Navigation.
React Native Navigation Drawer Extension :
Drawer API built on top of React Native Navigation for iOS and Android.
React Native Navigation Search Bar :
React Native Elements search bar with collapsible header built for React Native Navigation.
React Native Navigation Register Screens :
Utility function to register array of screens instead of calling Navigation.registerComponent multiple times.
Website Versioned Docs Version 6.12.2 Docs Docs Installing (`website/versioned_docs/version-6.12.2/docs/docs-Installing.mdx`)
id: installing title: Installation sidebar_label: Installation
Requirements
- node >= 8
- react-native >= 0.51
npm or yarn
npm install --save react-native-navigation
...or if you use yarn:
yarn add react-native-navigation
Installing with `npx rnn-link`
If you're using RN 0.60 or higher, you can benefit from autolinking for some of the necessary installation steps. But unlike most other libraries, react-native-navigation requires you to make a few changes to native files.
We've simplified the process through a set of scripts. So to make all the necessary changes automatically, in your project's root folder run:
npx rnn-link
Make sure to commit the changes introduced by the rnn-link script.
The automatic linking is optimized for new applications created via the
react-native initcommand. If you are migrating from a version of react-native-navigation older than v5, it's recommended to check the steps manually after the script runs.
If the link script completed successfully, you're good to go! No more native configuration is required.
If one of the steps failed or you can't run (or are not comfortable with) the automatic scripts, you'll need to complete the relevant steps in the manual installation steps below, for both platforms.
Displaying the app root
Update index.js file
index.js is typically used as an entry point into the app. It's first parsed and executed by the JS engine, therefore we'll want to show our UI from there.
The following diff demonstrates changes needed to be made to index.js, initialized by react-native init.
+import { Navigation } from "react-native-navigation";
-import {AppRegistry} from 'react-native';
import App from "./App";
-import {name as appName} from './app.json';
-AppRegistry.registerComponent(appName, () => App);
+Navigation.registerComponent('com.myApp.WelcomeScreen', () => App);
+Navigation.events().registerAppLaunchedListener(() => {
+ Navigation.setRoot({
+ root: {
+ stack: {
+ children: [
+ {
+ component: {
+ name: 'com.myApp.WelcomeScreen'
+ }
+ }
+ ]
+ }
+ }
+ });
+});If you followed the steps successfully up to this point, then this should be all!
:::tip
This is a good moment to build your application in both platforms, validate that everything is working properly and commit your changes. If you're coming from a fresh react-native init project, then you should be seeing the Welcome screen as usual, but under the hood your application is using react-native-navigation!
::::
Manual Installation
If installation with npx rnn-link did not work, follow the manual installation steps below.
iOS
Make sure your Xcode is updated. We recommend editing
.hand.mfiles in Xcode as the IDE will usually point out common errors.
Installation with CocoaPods
Projects generated using newer versions of react-native use CocoaPods by default. In that case it's easier to install react-native-navigation using CocoaPods.
- Update your
Podfile:
If you're upgrading to v5 from a previous RNN version, make sure to remove manual linking of RNN
/* Detailed source-code truncated for AI context efficiency. */cd ios && pod install
Native Installation
If the React Native version in your project is above 0.60 and you are following the manual installation, skip to step 3.
If autolinking is not available in your project (RN version < 0.60), you can always try and install the hardcore way:
In Xcode, in Project Navigator (left pane), right-click on the
Libraries>Add files to [project name]. Addnode_modules/react-native-navigation/lib/ios/ReactNativeNavigation.xcodeproj(screenshots).In Xcode, in Project Navigator (left pane), click on your project (top), then click on your target row (on the "project and targets list", which is on the left column of the right pane) and select the
Build Phasestab (right pane). In theLink Binary With Librariessection addlibReactNativeNavigation.a(screenshots).
a. If you're seeing an error message in Xcode such as:
'ReactNativeNavigation/ReactNativeNavigation.h' file not found.You may also need to add a Header Search Path: (screenshots).
$(SRCROOT)/../node_modules/react-native-navigation/lib/ios- In Xcode, you will need to edit this file:
AppDelegate.m. This function is the main entry point for your app:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { ... }Its content should look like this:
#import "AppDelegate.h"
#import <React/RCTBundleURLProvider.h>
#import <React/RCTRootView.h>
#import <ReactNativeNavigation/ReactNativeNavigation.h>
@implementation AppDelegate
-(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[ReactNativeNavigation bootstrapWithDelegate:self launchOptions:launchOptions];
return YES;
}
-(NSURL *)sourceURLForBridge:(RCTBridge *)bridge
{
#if DEBUG
return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
#else
return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
#endif
}
@enda. If, in Xcode, you see the following error message in `AppDelegate.m` next to `#import "RCTBundleURLProvider.h"`:
```
! 'RCTBundleURLProvider.h' file not found
```
This is because the `React` scheme is missing from your project. You can verify this by opening the `Product` menu and the `Scheme` submenu.
To make the `React` scheme available to your project, run `npm install react-native-git-upgrade` followed by `react-native-git-upgrade`. Once this is done, you can click back to the menu in Xcode: `Product -> Scheme -> Manage Schemes`, then click '+' to add a new scheme. From the `Target` menu, select "React", and click the checkbox to make the scheme `shared`. This should make the error disappear.Android
Make sure your Android Studio installation is up to date. We recommend editing
gradleandjavafiles in Android Studio as the IDE will suggest fixes and point out errors, this way you avoid most common pitfalls.
1 Update `android/build.gradle`:
buildscript {
ext {
- minSdkVersion = 16
+ minSdkVersion = 19 // Or higher
compileSdkVersion = 26
targetSdkVersion = 26
supportLibVersion = "26.1.0"
+ kotlinVersion = "1.3.72" // Or any version above 1.3.x
+ RNNKotlinVersion = kotlinVersion
}
repositories {
google()
jcenter()
+ mavenLocal()
+ mavenCentral()
}
dependencies {
+ classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion")
+ classpath 'com.android.tools.build:gradle:3.5.3' // Or higher
- classpath 'com.android.tools.build:gradle:2.2.3'
}
}
allprojects {
repositories {
+ google()
mavenLocal()
jcenter()
maven {
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
url "$rootDir/../node_modules/react-native/android"
}
- maven {
- url 'https://maven.google.com/'
- name 'Google'
- }
+ maven { url 'https://jitpack.io' }
}
}2 Update `MainActivity.java`
MainActivity.java should extend com.reactnativenavigation.NavigationActivity instead of ReactActivity.
This file is located in android/app/src/main/java/com/<yourproject>/MainActivity.java.
-import com.facebook.react.ReactActivity;
+import com.reactnativenavigation.NavigationActivity;
-public class MainActivity extends ReactActivity {
+public class MainActivity extends NavigationActivity {
- @Override
- protected String getMainComponentName() {
- return "yourproject";
- }
}If you have any react-native related methods, you can safely delete them.
3 Update `MainApplication.java`
This file is located in android/app/src/main/java/com/<yourproject>/MainApplication.java.
/* Detailed source-code truncated for AI context efficiency. */App root
Now that you're done, don't forget to update the index.js file, as shown above.
Troubleshooting
Build app with gradle command
prefered solution - The RNN flavor you would like to build is specified in app/build.gradle. Therefore in order to compile only that flavor, instead of building your entire project using ./gradlew assembleDebug, you should instruct gradle to build the app module: ./gradlew app:assembleDebug. The easiest way is to add a package.json command to build and install your debug Android APK .
"scripts": {
...
"android": "cd ./android && ./gradlew app:assembleDebug && ./gradlew installDebug"
}Now run npm run android to build your application
Force the same support library version across all dependencies
Some of your dependencies might require a different version of one of Google's support library packages. This results in compilation errors similar to this:
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':app:preDebugBuild'.
> Android dependency 'com.android.support:design' has different version for the compile (25.4.0) and runtime (26.1.0) classpath. You should manually set the same version via DependencyResolutionTo resolve these conflicts, add the following to your app/build.gradle:
android {
...
}
configurations.all {
resolutionStrategy.eachDependency { DependencyResolveDetails details ->
def requested = details.requested
if (requested.group == 'com.android.support' && requested.name != 'multidex') {
details.useVersion "${rootProject.ext.supportLibVersion}"
}
}
}
dependencies {
...
implementation 'com.android.support:design:25.4.0'
implementation "com.android.support:appcompat-v7:${rootProject.ext.supportLibVersion}"
}Website Versioned Docs Version 6.12.2 Docs Docs Advanced Navigation (`website/versioned_docs/version-6.12.2/docs/docs-advanced-navigation.mdx`)
id: advanced-navigation title: Advanced navigation sidebar_label: Advanced navigation
import useBaseUrl from '@docusaurus/useBaseUrl';
Tab navigation
As mobile applications become bigger and more complex, they usually end up containing multiple primary high-level destinations, which are logically independent from one another. The BottomTabs layout is often used when an app contains three to five top-level destinations which should be accessible from anywhere in the app.
Lets return to the example code from the previous section (Basic navigation) and see how we can convert it to a tab-based application. We'll use the BottomTabs layout to display tabs at the bottom of the screen. Similarly to Stack layout, The BottomTabs layout also contains a children property where each child will be displayed in a tab.
Lets change our code to the following and reload the app.
/* Detailed source-code truncated for AI context efficiency. */Now our app has two tabs at the bottom of the screen and our users can easily navigate between them.
<img width="40%" src={useBaseUrl('img/stack4.png')} />
Replacing the root
Up until now, we've discussed how to navigate within the same layout structure. We'll now learn how to replace the entire layout structure to display a new Root. A real life example for this use case would be, for instance, if you need to switch from a login screen to the app's main root. Replacing the root fits this use case since we'd like to discard the previous root (login root) entirely when switching to the main root.
To demonstrate this, we'll make the following changes to our code:
- Add a login screen with a login button.
- When the button is clicked, we will switch to the main root.
- Replace our current root with the login screen. We'll also extract both roots to variables to improve code readability.
/* Detailed source-code truncated for AI context efficiency. */Conditional roots
Now that our initial root is the Login root, we're facing a new problem - how do we show the Login root only to users that are not logged in? Since our initial root is determined in the registerAppLaunchedListener callback, we'll check if a user is logged in there and set the appropriate root accordingly.
Navigation.events().registerAppLaunchedListener(async () => {
Navigation.setRoot(isLoggedIn() ? mainRoot : loginRoot);
});
function isLoggedIn() {
// TODO: your business logic goes here
}Website Versioned Docs Version 6.12.2 Docs Docs App Launch (`website/versioned_docs/version-6.12.2/docs/docs-app-launch.mdx`)
id: app-launch title: Launching the app sidebar_label: Launching the app
When your app is launched for the first time, the bundle is parsed and executed. At this point you need to display your UI. To do so, listen to the appLaunched event and call Navigation.setRoot when that event is received.
Navigation.events().registerAppLaunchedListener(() => {
// Each time the event is received you should call Navigation.setRoot
});:::important
Register the app launched listener as soon as possible - this should be one of the first lines in your index.js file.
:::
If you're observing a "white screen" or a hanging splash screen after relaunching your app, it probably means Navigation.setRoot isn't called as soon as the app is launched. Perhaps the listener was registered too late.
Difference between the platforms
When your app is launched, RN makes sure JS context (which is what enables you to execute JavaScript code) is running. There are quite a few differences between iOS and Android in this regard.
iOS
Whenever an app is put into the background, the app process can potentially be destroyed by the system. If this destruction takes place, the JS context will be destroyed alongside with the app process.
Android
An Android application is typically bound to two contexts:
- Application context - bound to the app process
- Activity context - bound to app UI
React's JS Context is executed on and bound to the Application context. This means that even when the Activity context (and thus the UI) is destroyed, React's JS Context remains active until the Application (and your process) are destroyed by the system.
:::caution
Because of this, when your app returns to foreground, JS Context might still exist on Android, even when the Activity and UI contexts have been destroyed - meaning you might not need to initialise all of your app logic; instead, you'll only need to call Navigation.setRoot to repopulate the UI context. This circumstance can easily be determined by setting a flag on your app's first appLaunched event, and checking the value of that flag on subsequent appLaunched events -- if the flag is true in your appLaunched callback, you need to call Navigation.setRoot to repopulate the UI.
:::
Website Versioned Docs Version 6.12.2 Docs Docs Basic Navigation (`website/versioned_docs/version-6.12.2/docs/docs-basic-navigation.mdx`)
id: basic-navigation title: Basic navigation sidebar_label: Basic navigation
import useBaseUrl from '@docusaurus/useBaseUrl';
Generally, any mobile app consists of various destinations which display some content to the user. And in vast majority of cases using an app means navigating between these destinations. React-native-navigation provides ways for you to layout your content on user screen in a logical and performant manner.
React Native Navigation's stack layout lets you push screens, and also navigate back to previous screens. Screens pushed into the stack hide the previous screen in the stack, making the user focus on a single screen at a time. Let's look at a stack layout that provides basic navigation for an app.
Creating a stack
// In index.js of a new project
const { Navigation } = require('react-native-navigation');
const React = require('react');
const { View, Text, StyleSheet } = require('react-native');
const HomeScreen = (props) => {
return (
<View style={styles.root}>
<Text>Home Screen</Text>
</View>
);
};
Navigation.registerComponent('Home', () => HomeScreen);
Navigation.events().registerAppLaunchedListener(async () => {
Navigation.setRoot({
root: {
stack: {
children: [
{
component: {
name: 'Home'
}
}
]
}
}
});
});
const styles = StyleSheet.create({
root: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: 'whitesmoke'
}
});Running this code will render a screen with an empty TopBar and your HomeScreen component (shown below). The TopBar is part of the stack layout, we'll work on configuring it later.
<img width="40%" src={useBaseUrl('img/stack1.png')} />
Specifying options
You can specify options of each layout (Stack, component pushed into a stack, etc.) to configure various parameters like the TopBar title or color. Lets spice things up by changing the TopBar color and while we're at it, also move the title to the TopBar.
Lets change the Home screen declaration to the following and reload the app to see the changes.
const HomeScreen = (props) => {
return (
<View style={styles.root}>
<Text>Hello React Native Navigation 👋</Text>
</View>
);
};
HomeScreen.options = {
topBar: {
title: {
text: 'Home',
color: 'white'
},
background: {
color: '#4d089a'
}
}
}Our app should now look like this:
<img width="40%" src={useBaseUrl('img/stackOptions.png')} />
Navigating in a stack
In the previous section we created a stack and initialized it with a single child. We'll now learn how to add another child to the stack. From now on, we'll call the action of adding children to the stack 'push', and removing children - 'pop'.
In order to push another screen to the stack, we will add a button to the home screen and call Navigation.push. The 'push' command accepts two parameters, the first is the id used to indicate into which stack to push the screen and the second is the screen we're pushing. After pushing a screen, a back button is added automatically to the TopBar so the users can navigate easily back to the previous screen.
You can read more about the stack layout here or dive right into the API here.
:::info componentId
Each React component registered with Navigation.registerComponent is assigned a unique componentId by Navigation. This unique id is used with Navigation commands (like the push command) to indicate into which stack we'd like to push. In this case, by using the componentId of the Home screen, we are telling Navigation to push into the stack containing the Home screen.
:::
Navigation.push(props.componentId, {
component: {
name: 'Settings', // Push the screen registered with the 'Settings' key
options: { // Optional options object to configure the screen
topBar: {
title: {
text: 'Settings' // Set the TopBar title of the new Screen
}
}
}
}
});Lets change our code to the following snippet below and reload the app. Now our Home should contain a button which when pressed, pushes a new screen into the stack. We've successfully navigated to a new screen! 👏
/* Detailed source-code truncated for AI context efficiency. */<img width="40%" src={useBaseUrl('img/stack2.gif')} />
App theme
Our app is growing and already contains two screens. This introduces a new problem - while the home screen has a nice purple TopBar, our settings screen seems pretty dull as it still uses the default system look and feel.
Currently, our style declaration applies only to the Home screen, so lets apply it to all of our screens by using Navigation.setDefaultOptions.
Navigation.setDefaultOptions({
statusBar: {
backgroundColor: '#4d089a'
},
topBar: {
title: {
color: 'white'
},
backButton: {
color: 'white'
},
background: {
color: '#4d089a'
}
}
});We need to add this snippet before registering the registerAppLaunchedListener. This way we ensure our theme is applied when our root is set. Our code should now look like this:
/* Detailed source-code truncated for AI context efficiency. */Lets run our code again - now our design is consistent across both screens.
<img width="40%" src={useBaseUrl('img/stack3.gif')} />
Summary
We've learned the basics of navigation with React Native Navigation by implementing a stack and pushing screens into it.
We've also learned a few methods of applying styles to our screens and layouts with the Options mechanism.
- Navigation.setRoot() sets the root layout of our app. The initial root has to be set from an
AppLaunchedEventcallback. - Options can be applied directly to components.
- Screens can be added to a stack with the
Navigation.push()command. - Components registered with
Navigation.registerComponent()are injected with acomponentIdwhich is used when navigating. - Themes are applied via the
Navigation.setDefaultOptions()command.
In the next section we'll explore a more advance navigation patterns using BottomTabs layout and also see how, and why, multiple roots are set.
Website Versioned Docs Version 6.12.2 Docs Docs Before You Start (`website/versioned_docs/version-6.12.2/docs/docs-before-you-start.mdx`)
id: before-you-start title: Before you start sidebar_label: Before you start
React Native Navigation is a module, dependent on and intended to be used alongside React Native, so some experience with it and knowledge of core concepts is required. If you feel the need, you can start with their getting started tutorial, and then come back here when you're ready.
We also assume you are working on a Mac with XCode and Android Studio installed and setup. You can also make it work in a Linux distribution, of course, but in that case bear in mind that some sections of the docs that deal with iOS might not be relevant to you.
If you want to dig right into it, start with installing the library. If you're just looking around, we suggest checking out basic navigation first.
Website Versioned Docs Version 6.12.2 Docs Docs BottomTabs (`website/versioned_docs/version-6.12.2/docs/docs-bottomTabs.mdx`)
id: bottomTabs title: Bottom tabs sidebar_label: Bottom tabs
import useBaseUrl from '@docusaurus/useBaseUrl';
Bottom tabs refers to a row of links, displayed at the bottom of the screen, and is referred to as "Tab Bar" in iOS and as "Bottom Navigation Bar" on Android. Usually these are intended to be used to switch between top-level content views with a single tap.
Creating bottom tabs
BottomTabs provide flat navigation and access to up to five equally important top-level root destinations. While any type of layout can be displayed in a tab, typically, Stacks are used, since they allow for vertical navigation within a tab.
Example
Lets see how to create a simple BottomTabs layout. There are a few things to notice here:
- Each child represents a tab on the screen.
- The root layout for each tab is a stack to allow for vertical navigation within the tab.
- Each stack declares
bottomTaboptions which are used to configure the tab's text, icon, color etc.
bottomTabs: {
id: 'BOTTOM_TABS_LAYOUT',
children: [
{
stack: {
id: 'HOME_TAB',
children: [
{
component: {
id: 'HOME_SCREEN',
name: 'HomeScreen'
}
}
],
options: {
bottomTab: {
icon: require('./home.png')
}
}
}
},
{
stack: {
id: 'PROFILE_TAB',
children: [
{
component: {
id: 'PROFILE_SCREEN',
name: 'ProfileScreen'
}
}
],
options: {
bottomTab: {
icon: require('./profile.png')
}
}
}
}
]
}Once we run this code, our BottomTabs should look like this:
<img width="40%" src={useBaseUrl('img/bottomTabs.png')} />
Selecting Tabs Programmatically
Tabs can be selected programmatically by updating the currentTabIndex or currentTabId options.
We'll use the BottomTabs layout showcased above to demonstrate programmatic tab selection.
Selecting a tab by index
The following mergeOptions command will select the second tab. We're passing the id of our BottomTabs layout, but we could also use the id of any of the child components, for example PROFILE_TAB or PROFILE_SCREEN.
Navigation.mergeOptions('BOTTOM_TABS_LAYOUT', {
bottomTabs: {
currentTabIndex: 1
}
});Selecting a tab by id
Tabs can also be selected by id (componentId). This is particularly useful in cases where you don't know in which tab a screen is contained.
For example, if invoked from one of the child components, HOME_SCREEN or SETTINGS_SCREEN, the following merge command will select the tab containing the child.
Navigation.mergeOptions(this.props.componentId, {
bottomTabs: {
currentTabId: this.props.componentId
}
});Changing BottomTabs visibility
The visible property is used to control the BottomTab visibility. Visibility can be toggled dynamically using the mergeOptions command.
Navigation.mergeOptions(componentId, {
bottomTabs: {
visible: false
},
});Visibility can also be changed when pushing screens.
Navigation.push(componentId, {
component: {
name: 'pushedScreen',
options: {
bottomTabs: {
visible: false
}
}
}
});Updating tab options dynamically
To update a tab option, like an icon, text or color, simply call mergeOptions with new options using the id of a component or layout contained in the tab you wish to update.
For instance, in the example below we update the color of a specific tab:
Navigation.mergeOptions(componentId, {
bottomTab: {
iconColor: 'red',
textColor: 'red'
},
});Controlling tab loading
By default, all tabs are mounted at the same time. This can have a negative impact on performance since screens which are not visible to the user are mounted.
The order in which tabs are mounted can be configured with the tabsAttachMode option:
together- all tabs are mounted at the same time, this is the default behavior.afterInitialTab- after initial tab is mounted, other tabs are mounted.onSwitchToTab- initial tab is mounted, other tabs are mounted when the user navigates to them for the first time.
Website Versioned Docs Version 6.12.2 Docs Docs ExternalComponent (`website/versioned_docs/version-6.12.2/docs/docs-externalComponent.mdx`)
id: externalComponent title: External Component sidebar_label: External Component
The ExternalComponent layout allows you to display any native view as a screen. To use the External Component we'll need to register it with a string name. This name is then used when declaring layouts in JS.
Android
Implementing the native component
When it comes to implementing an external component on Android, you have two choices. We recommend you use a base class extending View. If you're required to use Fragments, you'll find an basic example below.
ViewGroup
public class ViewGroupComponent implements ExternalComponent {
private final FrameLayout content;
ViewGroupComponent(FragmentActivity activity, JSONObject props) {
content = new FrameLayout(activity);
}
@Override
public View asView() {
return content;
}
}Fragment
Using a Fragment as an external component is done by attaching the Fragment to a FrameLayout, and returning the FrameLayout from the asView() method of the ExternalComponent.
public class FragmentComponent implements ExternalComponent {
private final FrameLayout content;
FragmentComponent(FragmentActivity activity, JSONObject props) {
// Create the FrameLayout to which we'll attach our Fragment to
content = new FrameLayout(activity);
content.setId(R.id.fragment_screen_content);
// Attach the Fragment to the FrameLayout
activity.getSupportFragmentManager()
.beginTransaction()
.add(R.id.fragment_screen_content, createFragment(props))
.commitAllowingStateLoss();
}
private FragmentScreen createFragment(JSONObject props) {
FragmentScreen fragment = new FragmentScreen();
// Pass the props sent from Js in a bundle
Bundle args = new Bundle();
args.putString("text", props.optString("text", ""));
fragment.setArguments(args);
return fragment;
}
@Override
public View asView() {
return content;
}
}Registering the component
public class MainApplication extends NavigationApplication {
@Override
public void onCreate() {
super.onCreate();
registerExternalComponent("MyExternalComponent", new FragmentCreator());
}
}iOS
ViewController registration
[ReactNativeNavigation registerExternalComponent:@"MyExternalComponent" callback:^UIViewController *(NSDictionary *props, RCTBridge *bridge) {
return [[ExternalViewController alloc] initWithProps:props];
}];Using the component from JS
Once you've registered the external component in native, you can use it in your layout declarations.
For example, to show an external component modally:
Navigation.showModal({
externalComponent: {
name: 'MyExternalComponent',
passProps: {
text: "Text from JS"
}
}
});:::caution
props passed to external components are sent over the bridge and therefore must be serializable.
:::
Website Versioned Docs Version 6.12.2 Docs Docs Functional Components (`website/versioned_docs/version-6.12.2/docs/docs-functional-components.mdx`)
id: functionalComponents title: Using functional components as screens sidebar_label: Using functional components
Both class components and functional components can be used as screens. There is no performance difference between the two. Deciding which type component to use boils down to user preference.
Declaring static options
Static options are a great way to declare options close to where they are used. Declaring static options is done by first declaring the functional component, and then declaring the options on the function component.
const MyScreen = (props) => {
return (
<View>
<Text>Screen Component</Text>
</View>
);
};
MyScreen.options = {
topBar: {
background: {
color: 'red'
}
}
}Listening to navigation events
Navigation events let you react to various events related to a specific component or the app itself. Read more about them here
import { useEffect } from 'react';
import Navigation from 'react-native-navigation';
function MyScreen(props) {
useEffect(() => {
const listener = {
componentDidAppear: () => {
console.log('RNN', `componentDidAppear`);
},
componentDidDisappear: () => {
console.log('RNN', `componentDidDisappear`);
}
};
// Register the listener to all events related to our component
const unsubscribe = Navigation.events().registerComponentListener(listener, props.componentId);
return () => {
// Make sure to unregister the listener during cleanup
unsubscribe.remove();
};
}, []);
return (
<View>
<Text>Screen Component</Text>
</View>
);
}In the example above, our listener is registered with the componentId passed in the second argument, and will receive only its related events.
:::tip
underscopeio/react-native-navigation-hooks is a wonderful library which greatly simplifies usage with hooks by introducing dedicated hooks for each event. The following example, which is taken from their docs, shows how to listen to all appear events and a particular screen's appear events:
import { useNavigationComponentDidAppear } from 'react-native-navigation-hooks'
const ScreenComponent = ({ componentId }) => {
// Global listener
useNavigationComponentDidAppear(e => {
console.log(`${e.componentName} (${e.componentId}) appeared`)
})
// Listen events only for this screen (componentId)
useNavigationComponentDidAppear(e => {
console.log(`${e.componentName} appeared`)
}, componentId)
return (
<View>
<Text>Screen Component</Text>
</View>
)
}:::
Website Versioned Docs Version 6.12.2 Docs Docs Modal (`website/versioned_docs/version-6.12.2/docs/docs-modal.mdx`)
id: modal title: Modal sidebar_label: Modal
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import useBaseUrl from '@docusaurus/useBaseUrl';
In human-centered design, when we speak about a modal (or modality), we often refer to a set of techniques, aimed at bringing user attention to a specific event / screen / action / etc. Those often require user input. A pop-up on a website, a file deletion confirmation dialogue on your computer, or an alert asking you to enable location service on your phone - these can all be considered modals.
A modal is a term used in native iOS world (full description here), while on Android, dialogs are often used to create similar or identical behavior, alongside other techniques.
Presenting modals
Modal can be displayed by invoking the Navigation.showModal() command, as shown in the example below:
Navigation.showModal({
stack: {
children: [
{
component: {
name: 'MODAL_SCREEN',
options: {
topBar: {
title: {
text: 'Modal',
},
},
},
},
},
],
},
});Here's how the Modal looks on both platforms.
<Tabs
defaultValue="ios"
values={[
{ label: 'iOS', value: 'ios' },
{ label: 'Android', value: 'android' },
]}
Adding a dismiss button
Modals should always have a dismiss button, on left-to-right devices it's typically placed as a left button in the TopBar.
After we've added our dismiss button, we need to dismiss the modal when the button is pressed.
:::note
For the LeftButton to be visible, the screen must be displayed in a Stack.
:::
class ModalScreen extends React.Component {
// Set a dismiss button in static options
static options() {
return {
topBar: {
leftButtons: {
id: 'dismiss',
icon: require('./dismiss.png'),
},
},
};
}
constructor(props) {
super(props);
// Register to events
Navigation.events().bindComponent(this);
}
// Handle the button press event and dismiss the modal if needed
navigationButtonPressed({ buttonId }) {
if (buttonId === 'dismiss') {
Navigation.dismissModal(this.props.componentId);
}
}
}Transparent modals
Showing transparent modals is a nice technique to increase immersiveness while keeping the user in the same context.
When a modal is displayed the content behind it (either root or modal) is removed from hierarchy. While this behavior improves performance, it's undesired when
showing transparent modals as we need to see the content behind it.
To configure this behaviour, we'll need to change the modalPresentationStyle option to overCurrentContext and change the layout background color to 'transparent'.
{
modalPresentationStyle: 'overCurrentContext',
layout: {
backgroundColor: 'transparent'
}
}Preventing a Modal from being dismissed
Preventing a modal from being dismissed is done differently for each platform. While preventing the user from dismissing the modal is possible,
the modal could still be dismissed programmatically by calling Navigation.dismissModal()
If the modal has a dismiss button, of course you'll need to handle it your self and avoid calling Navigation.dismissModal()
when the button is pressed.
Android
On Android, modals can be dismissed with the hardware back button. You can handle the back press your self by
adding a BackHandler
iOS
While iOS devices don't have a hardware back button, PageSheet modals can be dismissed by swipe gesture
from the top of the screen. To disable it, set swipeToDismiss option to false.
Presentation Style
The presentation style determines the look and feel of a screen displayed as modal.
Website Versioned Docs Version 6.12.2 Docs Docs Overlay (`website/versioned_docs/version-6.12.2/docs/docs-overlay.mdx`)
id: overlay title: Overlay sidebar_label: Overlay
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import useBaseUrl from '@docusaurus/useBaseUrl';
Overlays are used to layout content on top of all other layout hierarchies, while fitting the screen bounds.
The contents displayed in an Overlay can either be non-obtrusive, like Toasts and Snackbars, or an Alert that blocks all interactions with any content behind it. The view contained within it should be aligned either with absolute position, flex, or with margins, all according to your needs.
Handling touch events outside the view
When showing views like Alert or Toast in an Overlay, you would want to configure if you want to allow users to interact with content behind the alert. This is done via the interceptTouchOutside option.
Overlay examples
<Tabs
defaultValue="alert"
values={[
{ label: 'Alert', value: 'alert', },
{ label: 'Toast', value: 'toast', }
]
}>
The example below demonstrates how to create a simple alert dialog using an Overlay. Touch events outside the alert will be blocked and won't pass through to the content behind the alert since we're specifying interceptTouchOutside: true in the static options of the Alert.
<img width="30%" src={useBaseUrl('/img/alert_android.png')} />
const React = require('react');
const { Text, Button, View } = require('react-native');
const { Navigation } = require('react-native-navigation');
function Alert({ componentId, title, message }) {
const dismiss = () => Navigation.dismissOverlay(componentId);
return (
<View style={styles.root}>
<View style={styles.alert}>
<Text style={styles.title}>{title}</Text>
<Text style={styles.message}>{message}</Text>
</View>
</View>
);
}
const styles = {
root: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#00000050',
},
alert: {
alignItems: 'center',
backgroundColor: 'whitesmoke',
width: 250,
elevation: 4,
padding: 16,
},
title: {
fontSize: 18,
},
message: {
marginVertical: 8,
},
};
Alert.options = (props) => {
return {
overlay: {
interceptTouchOutside: true,
},
};
};
module.exports = Alert;The example below demonstrates how to show a Toast using an Overlay. A user can interact with the content behind the toast since we've declared interceptTouchOutside: false in the static options of the Alert.
<img width="30%" src={useBaseUrl('/img/toast_android.png')} />
const React = require('react');
const { View, Text, StyleSheet, TouchableOpacity } = require('react-native');
const Colors = require('../commons/Colors');
const Navigation = require('../services/Navigation');
const Toast = function ({ componentId }) {
return (
<View style={styles.root}>
<View style={styles.toast}>
<Text style={styles.text}>This a very important message!</Text>
<TouchableOpacity
style={styles.button}
onPress={() => Navigation.dismissOverlay(componentId)}
>
<Text style={styles.buttonText}>OK</Text>
</TouchableOpacity>
</View>
</View>
);
};
const styles = StyleSheet.create({
root: {
flex: 1,
flexDirection: 'column-reverse',
},
toast: {
elevation: 2,
flexDirection: 'row',
height: 40,
margin: 16,
borderRadius: 20,
backgroundColor: Colors.accent,
alignItems: 'center',
justifyContent: 'space-between',
},
text: {
color: 'white',
fontSize: 16,
marginLeft: 16,
},
button: {
marginRight: 16,
},
buttonText: {
color: 'white',
fontSize: 16,
fontWeight: 'bold',
},
});
Toast.options = {
layout: {
componentBackgroundColor: 'transparent',
},
overlay: {
interceptTouchOutside: false,
},
};
module.exports = Toast;Website Versioned Docs Version 6.12.2 Docs Docs Passing Data To Components (`website/versioned_docs/version-6.12.2/docs/docs-passing-data-to-components.mdx`)
id: passing-data-to-components title: Passing data to components sidebar_label: Passing data to components
Initial props
React components use props to receive data when they are created. When displaying the component layout, you can pass initial props to components using the passProps property.
In this example, we push the UserProfile screen and pass two initial props to it:
Navigation.push(this.props.componentId, {
component: {
name: 'UserProfile',
id: 'PROFILE_SCREEN_ID'
passProps: {
name: 'John Doe',
status: 'online'
}
}
});:::tip Serialization
passProps don't need to be serializable. You can use this mechanism to pass lambda functions or even React Components.
:::
Handling passProps in static options
Each component can have a static options property which is used to configure the style properties of elements on the screen when that component is displayed.
Static options can either be a simple object, or a function which accepts passProps as an argument. Sometimes when declaring components, not all style properties are known. For example, a user profile screen will usually have the user's name as the TopBar title text. Therefore the title must be configured dynamically when pushing the screen.
Following the code example above, lets see how to use props passed in the push command to set the TopBar title.
class UserProfileScreen extends React.Component {
static options(props) {
return {
topBar: {
title: {
text: props.name
}
}
};
}
}Updating props
To update a component's props, use the Navigation.updateProps() command. Updating props triggers the component lifecycle methods related to updating.
Notice that we're using the component id in order to update props of this specific instance of the component.
Navigation.updateProps('PROFILE_SCREEN_ID', {
status: 'offline'
});Website Versioned Docs Version 6.12.2 Docs Docs Playground App (`website/versioned_docs/version-6.12.2/docs/docs-playground-app.mdx`)
id: playground-app title: Playground app sidebar_label: Playground app
Running The Project
If you want to have a quick look around and test things out, you can run the playground app, bundled with this repo.
- Install dependencies via
npm install(if you haven't already) andnpm run pod-install(for iOS) - Run the playground project on Android and iOS
npm run startto get the packager running in the terminal, leave it open- iOS: open
./playground/iosin Xcode and run it - Android: open
./playground/androidin Android Studio and run it
- You can run tests if / when you need to (list of scripts available here). Before
you start changing things, make sure everything works.- To easily run all tests in parallel
npm run test-all
- To easily run all tests in parallel
Website Versioned Docs Version 6.12.2 Docs Docs Root (`website/versioned_docs/version-6.12.2/docs/docs-root.mdx`)
id: root title: Root sidebar_label: Root
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
The root is where the application layout structure is defined. It is typically the first UI element a user interacts with. The root can be changed multiple times during the lifespan of the application. For example, if an app requires users to login, it's common to use a stack-based root and transition to either tabs- or SideMenu-based root if login is successful.
Setting root on app launch
RNN exposes an appLaunched listener which is invoked whenever root needs to be set.
On iOS, the app launched event is usually emitted once in the lifespan of the application - when the app is opened for the first time. On Android things become a little bit more complicated. Like on iOS, the event is emitted when an app is opened for the first time. Since the system can destroy the Activity when the app is in the background to free resources, the event is emitted when the app returns to foreground after the activity has been destroyed.
Navigation.events().registerAppLaunchedListener(() => {
Navigation.setRoot({
root: {
}
});
});:::important
registerAppLaunchedListener() must be called as soon as the bundle is executed. Otherwise the event, which is emitted from native to JS, won't be handled at the correct moment in time.
::::
Conditional initial root
A common use case is to set the initial root according to a condition of some sort. For example:
If a user is logged in, show the application main root; otherwise show a login screen.
To do this, simply set the appropriate root according to your needs.
Navigation.events().registerAppLaunchedListener(() => {
if (isUserLoggedIn()) {
setMainRoot();
} else {
setLoginRoot();
}
});Common root structures
<Tabs
defaultValue='stack'
values={[
{ label: 'Stack root', value: 'stack' },
{ label: 'BottomTabs root', value: 'bottomTabs' },
{ label: 'SideMenu root', value: 'sideMenu' }
]
}>
Navigation.setRoot({
root: {
stack: {
children: [
{
component: {
name: 'LOGIN_SCREEN'
}
}
]
}
}
});Navigation.setRoot({
root: {
bottomTabs: {
children: [
{
stack: {
children: [
{
component: {
name: 'FEED_SCREEN'
}
}
]
}
},
{
stack: {
children: [
{
component: {
name: 'CHAT_LIST'
}
}
]
}
},
{
stack: {
children: [
{
component: {
name: 'PROFILE_SCREEN'
}
}
]
}
}
]
}
}
});Navigation.setRoot({
root: {
sideMenu: {
center: {
stack: {
children: [
{
component: {
name: 'HOME_SCREEN'
}
}
]
}
},
left: {
component: {
name: 'MENU_SCREEN'
}
}
}
}
});Website Versioned Docs Version 6.12.2 Docs Docs Screen Lifecycle (`website/versioned_docs/version-6.12.2/docs/docs-screen-lifecycle.mdx`)
id: screen-lifecycle title: Screen Lifecycle sidebar_label: Screen Lifecycle
Any React Component registered with react-native-navigation is enhanced with two additional lifecycle events:
componentDidAppear- called each time a component is revealed to the usercomponentDidDisappear- called each time a component is hidden from user's view as a result of being detached from hierarchy
These methods compliment React's lifecycle methods:
componentDidMount- called once, when a component is attached to hierarchy for the first timecomponentWillUnmount- called once, when a component is destroyed
Mounting
These methods are called in the following order when a component is created and attached to hierarchy.
- constructor()
- render()
- componentDidMount()
- componentDidAppear()
Unmounting
These methods are called when a component is being removed from hierarchy
- componentDidDisappear()
- componentWillUnmount()
Modal
When a modal is displayed, depending on the modalPresentationStyle, content behind it might be detached from hierarchy. This affects the visibility events which are emitted to the content behind the modal.
When Modals with pageSheet or overCurrentContext modalPresentationStyle are displayed, previous content is still visible to the user. Thus componentDidDisappear event is not emitted.
Same is applied when a modal is dismissed. If it was originally presented with pageSheet or overCurrentContext modalPresentationStyle, when that modal is then dismissed, the previous context won't receive a componentDidAppear event.
Overlay
These methods are called in the following order when a component is displayed as an Overlay:
- constructor()
- render()
- componentDidMount()
- componentDidAppear()
:::note
Content displayed behind an Overlay does not receive the componentDidDisappear, since it's still visible to user and attached to the hierarchy.
:::
Website Versioned Docs Version 6.12.2 Docs Docs Showcases (`website/versioned_docs/version-6.12.2/docs/docs-showcases.mdx`)
id: showcases title: Showcases sidebar_label: Showcases
Apps
Hekla for Hacker News
A hacker news client built from the ground up with v2 of react-native-navigation.
It is a tab-based app and utilizes Split Layout for iOS tablets, 3D touch and more.
Starter Kits
kanzitelli/expo-rnn-starter - Simple yet practical starter with React Native Navigation and Expo Modules, MobX-State-Tree and Redux (pick one) and more goodies inside such as Typescript, RNN Hooks, RN Vector Icons, RN Gesture Handler. As an example, basic Reddit App implemented in two ways: using MobX-State-Tree in
/srcMobXand using Redux in/srcRedux.atoami/react-native-navigation-redux-starter-kit - React Native Navigation(v2) Starter Kit with Redux, Saga, ESLint, Babel and Jest 😎
alexwasner/react-native-mobx-firebase-starter - A React Native starter app with React Native Navigation(v2), MobX, Firebase, Lottie and More 🤓
hsyndeniz/react-native-quick-start - A React Native starter app with React Native Navigation, Redux, Firebase, Social Auth, Phone Auth, Push Notifications, Google Maps, Google Admob, Lottie and More 🤓
Playground
The playground example is a place where we showcase and test all features of react-native-navigation.
git clone https://github.com/wix/react-native-navigation
cd react-native-navigation
npm install
# for iOS:
npm run xcode # Opens XCode
npm start # Starts the packager on linux like systems
# for Android:
# 1. open Android Studio inside ".\playground"
# 2. start a emulator
npm run start-windows # Start the packager if you are on a windows system
# Run the app in Simulator or on Device from within XCode/Android StudioWebsite Versioned Docs Version 6.12.2 Docs Docs SideMenu (`website/versioned_docs/version-6.12.2/docs/docs-sideMenu.mdx`)
id: sideMenu title: Side Menu sidebar_label: Side Menu
SideMenu provides access to destinations in the app from an easy to access menu which is accessible to the user via horizontal swipe gesture or a menu button.
Opening programmatically
To open the side menu programmatically, you'll need to update the visible property of the side menu you'd like to open.
The following snippet shows how to open the left side menu. The menu is opened by setting its visible option to visible.
Navigation.mergeOptions(componentId, {
sideMenu: {
left: {
visible: true,
},
},
});Opening via the hamburger menu
The most common use case is to open the side menu by pressing the hamburger button at the TopBar. To achieve this, listen to the press event of the burger button, and open the side menu as shown above.
const React = require('react');
const Navigation = require('react-native-navigation');
const { View, Text } = require('react-native');
class SideMenuCenterScreen extends React.Component {
static options() {
return {
topBar: {
leftButtons: {
id: 'sideMenu',
icon: require('./menuIcon.png')
}
}
};
}
constructor(props) {
super(props);
Navigation.events().bindComponent(this);
}
render() {
return (
<View>
<Text>Click the hamburger icon to open the side menu</Text>
</View>
);
}
navigationButtonPressed({ buttonId }) {
if (buttonId === 'sideMenu') {
Navigation.mergeOptions(this, {
sideMenu: {
left: {
visible: true
}
}
});
}
}
}Pushing to the center layout from a menu
A common use case when using SideMenus is to interact with the center layout, e.g. pushing a screen to a stack in the center layout when a user clicks on a button from one of the menus.
In order to interact with the stack, we'll first assign it a predefined id. For example:
sideMenu: {
center: {
stack: {
id: 'CenterStack',
children: []
}
},
left: {
...
}
}Now that we've defined an id for out stack, we can go ahead and push our screen to it. If we're pushing while the SideMenu is open, we'll need to make sure we close it by updating the visibility option of the relevant menu (left or right).
Navigation.push('CenterStack', {
component: {
name: Screens.Pushed,
options: {
sideMenu: {
left: {
visible: false
}
}
}
}
});Website Versioned Docs Version 6.12.2 Docs Docs Stack (`website/versioned_docs/version-6.12.2/docs/docs-stack.mdx`)
id: stack title: Stack sidebar_label: Stack
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
A stack is a container layout promoting a hierarchical navigation. It is used for navigating between screens at consecutive levels of hierarchy, steps in a flow or across an app.
The first child in the stack (represented by the children array) is the root and is displayed at the bottom of the stack. The last child in the children array is the child currently being displayed.
In this layout, only a single child screen is visible at any given time and consecutive screen can be added to the top of the stack using the Navigation.push command. Tapping the back button will pop the stack and remove the top most screen.
The stack manages the TopBar at the top of the stack. The TopBar displays the current screens' title and buttons. It can be hidden with the topBar: { visible: false } option. By default, screens are rendered below the TopBar. This behavior can be changed by setting topBar: { drawBehind: true } in the current screens' options.
Layout Examples
<Tabs
defaultValue="single"
values={[
{ label: 'Single child', value: 'single', },
{ label: 'Multiple Children', value: 'multiple', }
]
}>
A stack declared with a single child.
const stack = {
children: [
{
component: {
name: 'MyComponent',
},
},
],
};A stack can be initialized with more than one child, in which case the last child will be the currently displayed child and the first child will be hidden. In this case the back button will be visible automatically, clicking it will go back in the stack revealing the first (previous) child.
Once the root child becomes visible, the back button is hidden.
const stack = {
children: [
{
component: {
name: 'RootComponent',
},
},
{
component: {
name: 'SecondComponent',
},
},
],
};TopBar Buttons
Buttons can be added to the right and left areas of the TopBar. Buttons can have either an icon or a text. They are declared in the the options object and, as with any other option, can be updated dynamically with the Navigation.mergeOptions command.
:::tip Always assign titles to buttons!
When using an icon button on Android, you should always pass a title as well. The title is used when the button is collapsed to the overflow menu and as a tooltip when the button is long pressed.
:::
Overflow menu
It's common practice to group less important actions in a menu or an action sheet.
To do so on iOS, include a button with a menu icon and open an ActionSheet with the relevant actions when the button is clicked.
On Android, use the showAsAction options to control when the button should appear in the menu.
Left button
Left buttons behave like right buttons with two caveats on Android:
- Only a single left button is allowed
- Textual left button isn't supported
Using a react component in a button
:::caution
At the moment, custom buttons in leftButtons are supported only on iOS.
:::
Sometimes we require more from our buttons. In order to support every product need React Components can be used as custom views of buttons.
To do so, you'll first need to register the view with Navigation, just like you register your components used as screens:
Navigation.registerComponent('ButtonComponent', () => require('./ButtonComponent'));Now you can create buttons which use the component registered with 'ButtonComponent' as their custom view:
topBar: {
rightButtons: [
{
component: {
name: 'ButtonComponent',
passProps: {
// Pass initial props to the button here
},
},
},
];
}Updating props of custom buttons
To update props of a mounted component used as a button, you'll first need to assign it a unique id, then call the Navigation.updateProps() command with the id.
Calling the updateProps command will trigger Reacts component lifecycle methods related to props update
// Declare the button and assign it a unique id
topBar: {
rightButtons: [
{
id: 'SomeUniqueId',
component: {
name: 'ButtonComponent',
passProps: {
count: 0
},
},,
},
];
}
// Update props
Navigation.updateProps('SomeUniqueId', {
count: 1,
});Changing buttons dynamically
As buttons are part of a screen's options, they can be modified like any other styling option using the mergeOptions command.
Setting buttons
The following command will set the screen's right buttons. If the screen already has Right Buttons declared - they will be overridden.
Navigation.mergeOptions(this.props.componentId, {
topBar: {
rightButtons: [
{
id: 'myDynamicButton',
text: 'My Button',
},
],
},
});Removing buttons
Buttons can be removed by setting zero buttons, as shown in the snippet below.
Navigation.mergeOptions(this.props.componentId, {
topBar: {
rightButtons: [],
},
});Back Button
The back button is added automatically when two or more screens are pushed into the stack.
Styling the back button
The back button's style can be customized by declaring a backButton options object. This configuration can be part of a screen's static options, or default options.
backButton: {
color: 'red',
icon: require('../../img/customChevron.png')
}Controlling visibility
The back buttons visibility can be controlled with the visible property.
backButton: {
visible: false;
}Changing visibility programmatically
Back button visibility can be changed dynamically using the mergeOptions command. When using a screen's componentId, the change will affect only that specific screen. But when using the stack's id, the change will affect all screens pushed into the stack.
Navigation.mergeOptions(this.props.componentId, {
backButton: {
visible: false,
},
});Handling the back button
Handling the back button is not possible. However, you can set a left button with a chevron and handle it like you'd handle any other button and calling Navigation.pop when desired.
Interacting programmatically
The Navigation object provides ways to programmatically manipulate the stack.
Interact with the Stack by componentId
Each layout pushed into the stack has an id. When in the context of a component, The component's componentId can be used to interact with a parent stack.
When using a component's componentId, the native implementation knows to perform the command on the parent Stack of this component.
In this example, we push a screen onto the component's parent stack.
const React = require('react');
const Navigation = require('react-native-navigation');
class MyComponent extends React.Component {
onButtonClick = () => {
Navigation.push(this.props.componentId, {
component: {
name: 'PUSHED_SCREEN',
},
});
};
}Interact with the Stack by a predefined id
Sometimes we're required to interact with a specific stack not from the context of a component pushed into it. To do so, assign the stack a predefined id and use it when invoking any stack command.
Navigation.setRoot({
root: {
stack: {
id: 'MyStack', // This is the id we're going to use when interacting with the stack
children: [
{
component: {
name: 'SomeComponent',
},
},
],
},
},
});
function push() {
Navigation.push('MyStack', {
component: {
name: 'PushedScreen',
},
});
}Disabling back navigation
Handling back navigation
Website Versioned Docs Version 6.12.2 Docs Meta Contributing (`website/versioned_docs/version-6.12.2/docs/meta-contributing.mdx`)
id: meta-contributing title: Contributing sidebar_label: Contributing
Thank you for showing interest in contributing to React-Native-Navigation! This project is developed and maintained by Wix in collaboration with the community.
There are various ways in which you can contribute to this library, not all require knowledge or expertise in native development. Listed below is all you need to get started with your first contribution.
Stack Overflow
Stack Overflow is used by developers to find answers and troubleshoot code. Users are encouraged to post questions on SO and tag them with the wix-react-native-navigation tag. Answering issues on SO is very helpful and beneficial to the community, only this time, there's a personal angle - you can boost your SO ranking and rack up points quickly.
Discord
Another popular communication channel is our Discord channel where users post questions and consult with each other. You're welcome to join the discussions and answer questions. This is also a great place to meet other community members and project maintainers.
GitHub Issues
Open an issue
Found a bug? Missing a feature? Go ahead and open an issue! Make sure to add all details mentioned in the issue template. If you're interested in suggesting a big change, please speak to one of the admins on Discord to coordinate your effort.
Respond to issues
Although the issue tracker is used solely for bug reports, issues are frequently opened for questions or to request assistance. As the project grows in popularity, more issues remain unanswered for long periods of time.
Some issues can be trivial and easy to pinpoint - a missing import statement or apostrophe, wrong layout structure, etc. If you're an experienced user, helping out newcomers can be quite satisfying and allows maintainers to focus on features and bug fixes.
Some issues are tagged as "looking for contributors". These issues have been recognized by the team, but aren't prioritized by Wix developers due to lack of time or for some other reason. We leave these issues to our community and you're more than welcome to take a crack at them. If you'd like to submit a PR, see these instructions on how to run the project locally.
Provide reproductions
Reproducing bugs takes time. Lots of time. Usually that's because an issue is lacking important information, which then causes lots of back and forth communication between maintainers and users. Help us address more bugs and issues by providing reproductions.
Providing reproductions improves the chances of an issue being prioritized by maintainers!
If an issue is reproduced with a specific combination of options, posting these options will usually suffice. If a specific layout structure is involved or specific actions need to be performed in a certain order - then we ask that you fork the project and push a branch with a reproduction to the Playground app.
Check out the list of issues requiring reproductions.
Submitting PRs
So you've fixed a bug or added a feature and you're ready to submit a pull request 🎉🎊 Make sure the title is clear and describes the reason for the PR. Please include any information you can which will help us understand the reasons for the PR, what it fixes and what it changes. Please include before/after pictures/gifs when appropriate.
Workflow
This project is driven by tests. Before implementing any feature or fixing any bug, a failing test (e2e or unit or both) should be added, depending on the environment of where the fix should be implemented. For example, for an API change, a failing e2e should be written. For a small bug fix in Android, for example, a unit test in Android should be added.
This will ensure good quality throughout the life of the project and will help avoid unexpected breakages.
No PR will be accepted without adequate test coverage.
If you need help running the project, have a look at the Playground app section. You can run the tests using the scripts below.
Tests and the Playground app
Besides an overview of basic React Native Navigation functionality, the Playground app can (and rather should) be used for e2e tests and reproductions. If your change requires an e2e, add it to the playground app, to the appropriate screen. Again, quick setup instructions available in Playground app section of these docs.
:::tip
If a screen contains too many buttons, e2e tests might fail if the button is positioned out of screen bounds because Detox matchers detect it's invisible.
:::
Enable linter
The project uses ESLint with Prettier to ensure code style consistency across the codebase. Make sure to install these plugins in your IDE.
A pre-commit hook will verify that the linter passes when committing.
Playground app Folder Structure
| Folder | Description |
|---|---|
lib |
The project itself composed of: |
lib/android |
android sources and unit tests |
lib/ios |
iOS sources and unit tests |
lib/src |
TypeScript sources and unit tests |
lib/dist |
compiled javascript sources and unit tests |
lib/dist/index.js |
the entry point for import Navigation from 'react-native-navigation' |
e2e |
detox e2e tests on both Android and iOS |
playground |
The end-user project all e2e tests run against. Contains its own src, android and ios. Does not have its own package.json, depends on the local <root>/lib for faster local development (no need to npm install locally). |
integration |
misc javascript integration tests, proving integration with other libraries like redux |
scripts |
all scripts |
Scripts
| Command | Description |
|---|---|
npm install |
installs dependencies |
npm run build |
compiles TypeScript sources ./lib/src into javascript ./lib/dist |
npm run clean |
cleans all build directories, stops packager, fixes flakiness by removing watchman cache, etc. |
npm run start |
starts the react-native packager for local debugging |
npm run xcode |
for convenience, opens xcode in this project |
npm run install-android |
builds playground debug/release version and installs on running android devices/emulators. Options: -- --release |
npm run uninstall-android |
uninstalls playground from running android devices/simulators |
npm run test-js |
runs javascript tests and coverage report |
npm run test-unit-ios |
runs ios unit tests in debug/release Options: -- --release |
npm run test-unit-android |
runs android unit tests in debug/release Options: -- --release |
npm run test-e2e-ios |
runs the ios e2e tests using detox in debug/release Options: -- --release |
npm run test-e2e-android |
runs the android e2e tests using detox in debug/release Options: -- --release |
npm run test-all |
runs all tests in parallel |
Website Versioned Docs Version 6.12.2 Docs SideMenu Disable (`website/versioned_docs/version-6.12.2/docs/sideMenu-disable.mdx`)
id: sideMenu-disable title: Disable Open and Close gesture sidebar_label: Open and Close Gesture
To open the side menu programmatically, you'll need to update the visible property of the side menu you'd like to open.
The following snippet shows how to open the left side menu.
Navigation.mergeOptions(componentId, {
sideMenu: {
left: {
visible: true,
},
},
});Open by pressing on the hamburger menu
The most common use case is to open the side menu by pressing the hamburger button in the TopBar. To achieve this, listen to the press event of the burger button, and open the side menu as shown above.
const React = require('react');
const Navigation = require('react-native-navigation');
const { View, Text } = require('react-native');
class SideMenuCenterScreen extends React.Component {
static options() {
return {
topBar: {
leftButtons: {
id: 'sideMenu',
icon: require('./menuIcon.png')
}
}
};
}
constructor(props) {
super(props);
Navigation.events().bindComponent(this);
}
render() {
return (
<View>
<Text>Click the hamburger icon to open the side menu</Text>
</View>
);
}
navigationButtonPressed({ buttonId }) {
if (buttonId === 'sideMenu') {
Navigation.mergeOptions(this, {
sideMenu: {
left: {
visible: true
}
}
});
}
}
}Website Versioned Docs Version 6.12.2 Docs Stack BackButton (`website/versioned_docs/version-6.12.2/docs/stack-backButton.mdx`)
id: stack-backButton title: The Back button sidebar_label: Back button
The back button is added automatically when two or more screens are pushed into the stack.
Styling the back button
The back button's style can be customised by declaring a backButton options object. This configuration can be part of a screen's static options, or default options.
backButton: {
color: 'red',
icon: require('../../img/customChevron.png')
}Controling visibility
The back buttons visbility can be controlled with the visible property.
backButton: {
visible: false
}Changing visbility programmatically
Back button visibility can be changed dynamically using the mergeOptions command. When using a screen's componentId, the change will affect only that specific screen. But when using the stack's id, the change will affect all screens pushed into the stack.
Navigation.mergeOptions(this.props.componentId, {
backButton: {
visible: false
}
});Handling the back button
Handling the back button is not possible. However, you can set a left button with a chevron and handle it like you'd handle any other button and calling Navigation.pop when desired.
Website Versioned Docs Version 6.12.2 Docs Stack BackNavigation (`website/versioned_docs/version-6.12.2/docs/stack-backNavigation.mdx`)
id: stack-backNavigation title: Back Navigation sidebar_label: Back navigation
Disabling back navigation
Handling back navigation
Website Versioned Docs Version 6.12.2 Docs Stack Buttons (`website/versioned_docs/version-6.12.2/docs/stack-buttons.mdx`)
id: stack-buttons title: TopBar Buttons sidebar_label: TopBar Buttons
Buttons can be added to the right and left areas of the TopBar. Buttons can have either an icon or a text. They are declared in the the options object and, as with any other option, can be updated dynamically with the Navigation.mergeOptions command.
When using an icon button on Android, you should always pass a title as well. The title is used when the button is collapsed to the overflow menu and as a tooltip when the button is long pressed.
Overflow menu
It's common practice to group less important actions in a menu or an action sheet.
To do so on iOS, include a button with a menu icon and open an ActionSheet with the relevant actions when the button is clicked.
On Android, use the showAsAction options to control when the button should appear in the menu.
Left button
Left buttons behave like right buttons with two caveats on Android:
- Only a single left button is alowed
- Textual left button isn't supported
Using a react component in a button
⚠️At the moment, Android only supports using custom buttons in
rightButtons.
Sometimes we require more from our buttons. In order to support every product need React Components can be used as custom views of buttons.
To do so, you'll first need to register the view with Navigation, just like you register your components used as screens:
Navigation.registerComponent('ButtonComponent', () => require('./ButtonComponent'));Now you can create buttons which use the component registered with 'ButtonComponent' as thier custom view:
topBar: {
rightButtons: [
{
component: {
name: 'ButtonComponent',
passProps: {
// Pass initial props to the button here
},
},
},
];
}Updating props of custom buttons
To update props of a mounted compoennt used as a button, you'll first need to assign it a unique id, then call the Navigation.updateProps() command with the id.
Calling the updateProps command will trigger React's component lifecycle methods related to props update
// Declare the button and assign it a unique id
topBar: {
rightButtons: [
{
id: 'SomeUniqueId',
component: {
name: 'ButtonComponent',
passProps: {
count: 0
},
},,
},
];
}
// Update props
Navigation.updateProps('SomeUniqueId', {
count: 1,
});Changing buttons dynamically
As buttons are part of a screen's options, they can be modified like any other styling option using the mergeOptions command.
Setting buttons
The following command will set the screen's right buttons. If the screen already has Right Buttons declared - they will be overridden.
Navigation.mergeOptions(this.props.componentId, {
topBar: {
rightButtons: [
{
id: 'myDynamicButton',
text: 'My Button',
},
],
},
});Removing buttons
Buttons can be removed by setting zero buttons, as shown in the snippet below.
Navigation.mergeOptions(this.props.componentId, {
topBar: {
rightButtons: [],
},
});Website Versioned Docs Version 6.12.2 Docs Stack Programmatically (`website/versioned_docs/version-6.12.2/docs/stack-programmatically.mdx`)
id: stack-programmatically title: Interact programmatically with the Stack sidebar_label: Interact programmatically
The Navigation object provides ways to programmatically manipulate the stack.
Interact with the Stack by componentId
Each layout pushed into the stack has an id. When in the context of a component, The component's componentId can be used to interact with a parent stack.
When using a component's componentId, the native implementation knows to perform the command on the parent Stack of this component.
In this example, we push a screen onto the component's parent stack.
const React = require('react');
const Navigation = require('react-native-navigation');
class MyComponent extends React.Component {
onButtonClick = () => {
Navigation.push(this.props.componentId, {
component: {
name: 'PUSHED_SCREEN'
}
});
}
}Interact with the Stack by a predefined id
Sometimes we're required to interact with a specific stack not from the context of a component pushed into it. To do so, assign the stack a predefined id and use it when invoking any stack command.
Navigation.setRoot({
root: {
stack: {
id: 'MyStack', // This is the id we're going to use when interacting with the stack
children: [
{
component: {
name: 'SomeComponent'
}
}
]
}
}
});
function push() {
Navigation.push('MyStack', {
component: {
name: 'PushedScreen'
}
});
}Website Versioned Docs Version 6.12.2 Docs Style Animations (`website/versioned_docs/version-6.12.2/docs/style-animations.mdx`)
id: style-animations title: Animations sidebar_label: Animations
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import useBaseUrl from '@docusaurus/useBaseUrl';
Various UI elements can be animated by declaring animation options.
You can change the default animations for various commands, like push and pop, and even animate elements in
your screens during screen transitions.
Animation properties
The following properties can be animated with our animation framework:
- x - Translate the view to a coordinate along the x axis.
- y - Translate the view to a coordinate along the y axis.
- translationX - Translate the view along the x axis relative to its current x position.
- translationY - Translate the view along the y axis relative to its current y position.
- alpha - Apply alpha animation to the view. A value of 0 means the view is not visible, 1 means it's visible.
- scaleX - Scale the view on the x axis. A value of 1 means that no scaling is applied.
- scaleY - Scale the view on the y axis. A value of 1 means that no scaling is applied.
- rotationX - Applies rotation around the x axis (in degrees, passed as a float).
- rotationY - Applies rotation around the y axis (in degrees, passed as a float).
- rotation - Rotates the view on all axis. Positive values result in clockwise rotation.
Layout animations
Stack animations
When animating screens in and out of a stack, there are three elements you can work with:
- content - screen being pushed or popped
- topBar - stack's TopBar
- bottomTabs - if stack is a child of a bottomTabs layout, we can control the
hideandshowanimations of the bottomTabs.
The following example demonstrates how to replace the default push and pop animations with slide animations.
<Tabs
defaultValue="push"
values={[
{ label: 'Push', value: 'push', },
{ label: 'Pop', value: 'pop', },
]
}>
options: {
animations: {
push: {
content: {
translationX: {
from: require('react-native').Dimensions.get('window').width,
to: 0,
duration: 300
}
}
}
}
}options: {
animations: {
pop: {
content: {
translationX: {
from: 0,
to: -require('react-native').Dimensions.get('window').width,
duration: 300
}
}
}
}
}Modal animations
Modal animations are declared similarly to stack animations, only this time we animate the entire view and not only part of the UI (content).
<Tabs
defaultValue="show"
values={[
{ label: 'Show', value: 'show', },
{ label: 'Dismiss', value: 'dismiss', },
]
}>
The following example demonstrates how to show a modal with a fade-in animation.
options: {
animations: {
showModal: {
alpha: {
from: 0,
to: 1,
duration: 300
}
}
}
}The following example demonstrates how to dismiss a modal with a fade-out animation.
options: {
animations: {
dismissModal: {
alpha: {
from: 1,
to: 0,
duration: 300
}
}
}
}Shared element transitions
Shared element transitions allow us to provide visual continuity when navigating between destinations. This also focuses user attention on a particular significant element, which then also gives such user better context when transitioning to some other destination.
:::caution
At the moment, the transition is available for push and pop commands. We are working on expanding supported commands to show/dismiss modal and change BottomTabs.
:::
Transition breakdown
Let's take a more in-depth look at the following example, which you can find in the playground app:
Source screen and the Destination screen
Four elements are animated in this example. Let's list the elements involved in the transition and note which properties are being animated.
image - the item image is animated to the next screen.
- image scale (resizeMode)
- position on screen
image background - each item has a "shadow" view which transitions to the next screen and turns into a colorful header.
- scale
- position on screen
- color
title - the title of the item is animated to the next screen.
- font size
- font color
- position on screen
Description - the description of the item in the destination screen.
- fade-in
- translation y
Implementing shared element transitions
Step 1 - set a nativeID prop to elements in the source screen
In order for RNN to be able to detect the corresponding native views of the elements,
each element must include a unique nativeID prop.
Step 2 - set a nativeID prop to elements in the destination screen
Step 3 - Declare the shared element animation when pushing the screen
Navigation.push(this.props.componentId, {
component: {
name: Screens.CocktailDetailsScreen,
passProps: { ...item },
options: {
animations: {
push: {
sharedElementTransitions: [
{
fromId: `image${item.id}`,
toId: `image${item.id}Dest`,
},
],
},
},
},
},
});Element Transitions
Element transitions also allow you to animate elements during shared element transitions.
Recreating
Step 1 - Set a nativeID prop to the element you'd like to animate
An element can either be in the source or destination screen.
<Text nativeID="description" style={styles.description}>
{this.props.description}
</Text>Step 2 - Declare the element animation when pushing the screen
Navigation.push(this.props.componentId, {
component: {
name: Screens.CocktailDetailsScreen,
passProps: { ...item },
options: {
animations: {
push: {
elementTransitions: [
{
id: 'description',
alpha: {
from: 0, // We don't declare 'to' value as that is the element's current alpha value, here we're essentially animating from 0 to 1
duration: SHORT_DURATION,
},
translationY: {
from: 16, // Animate translationY from 16dp to 0dp
duration: SHORT_DURATION,
},
},
],
},
},
},
},
});Peek and Pop (iOS 11.4+)
react-native-navigation supports Peek and pop feature in iOS 11.4 and newer.
This works by passing a ref to a component you want to transform into peek view. We have included a handy component to handle all the touches and ref for you:
const handlePress ({ reactTag }) => {
Navigation.push(this.props.componentId, {
component {
name: 'previewed.screen',
options: {
preview: {
reactTag,
height: 300,
width: 300,
commit: true,
actions: [{
title: "Displayed Name",
id: "actionId",
style: 'default', /* or 'selected', 'destructive'*/
actions: [/*define a submenu of actions with the same options*/]
}]
},
},
},
});
};
const Button = (
<Navigation.TouchablePreview
touchableComponent={TouchableHighlight}
onPress={handlePress}
onPressIn={handlePress}
>
<Text>My button</Text>
</Navigation.TouchablePreview>
);All options except for reactTag are optional. Actions trigger the same event as navigation button presses. To react when a preview is committed, listen to the previewCompleted event.
Website Versioned Docs Version 6.12.2 Docs Style Constants (`website/versioned_docs/version-6.12.2/docs/style-constants.mdx`)
id: style-constants title: Constants sidebar_label: Constants
React Native Navigation exposes a set of constants which can be used to get the dimensions of various navigation elements on the screen: StatusBar, TopBar and BottomTabs.
When are constants evaluated
Some of the values exposed through the constants API are actually evaluated only after the UI is created (setRoot has been called) and therefore are not accessible through static getters.
For example, if you need to get the height of the BottomTabs, you'll first need to have BottomTabs visible on the screen and only then retrieve their height via the constants API.
:::important
We recommend you don't cache the actual constants object returned by await Navigation.constants() as it might not be accurate later on when, for example, a new root is set or orientation changes.
:::
API
As explained above, constants are evaluated in native each time the API is invoked. That's why Navigation.constants() returns a promise and the use is as follows:
const { Navigation } = require('react-native-navigation');
const {
statusBarHeight,
topBarHeight,
bottomTabsHeight
} = await Navigation.constants();Website Versioned Docs Version 6.12.2 Docs Style Fonts (`website/versioned_docs/version-6.12.2/docs/style-fonts.mdx`)
id: style-fonts title: Changing fonts sidebar_label: Changing fonts
Before you begin using your own fonts, you'll first need to add them to the app project.
- Android - add the
.ttfor.otffiles tosrc/main/assets/fonts/ - iOS - follow this guide
Once we've added the font files to our project, we can star using them in options.
:::note
When declaring fonts in options, use only the file name without the file type suffix.
:::
BottomTab font
options: {
bottomTab: {
fontFamily: 'helvetica'
}
}Button font
options: {
rightButtons: [
{
id: 'SAVE_BUTTON',
text: 'Save',
fontFamily: 'helvetica'
}
],
leftButtons: [
{
id: 'CANCEL_BUTTON',
text: 'Cancel',
fontFamily: 'helvetica'
}
]
}Title font
options: {
topBar: {
title: {
fontFamily: 'helvetica'
}
}
}Subtitle font
options: {
topBar: {
subtitle: {
fontFamily: 'helvetica'
}
}
}Back button
In iOS the back button will display the title of the previous screen. Use the backButton option to modify its font family.
options: {
topBar: {
backButton: {
fontFamily: 'helvetica'
}
}
}Website Versioned Docs Version 6.12.2 Docs Style Orientation (`website/versioned_docs/version-6.12.2/docs/style-orientation.mdx`)
id: style-orientation title: Orientation sidebar_label: Orientation
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
Locking orientation
Orientation can be locked either in the layout level, or across the app via default options. You can declare orientations you'd like your app to support in default options.
<Tabs
defaultValue="defaultOptions"
values={[
{ label: 'Locking orientation in default options', value: 'defaultOptions', },
{ label: 'Locking root layout orientation', value: 'root', },
{ label: 'Showing landscape modal', value: 'modal', }
]
}>
Setting orientation in default options will affect all screens in all hierarchy levels. It's still possible to override orientation for specific screens.
Navigation.setDefaultOptions({
layout: {
orientation: ['portrait']
}
});The following example demonstrates how to show a modal in landscape orientation.
Navigation.showModal({
component: {
name: 'VideoPlayer'
options: {
layout: {
orientation: ['landscape']
}
}
}
});Applying orientation in the root level will affect all screens in the root hierarchy level. It will not apply to modals.
Navigation.setRoot({
root: {
bottomTabs: {
options: {
layout: {
orientation: ['portrait']
}
},
children: [
...
]
}
}
});Changing orientation dynamically
Changing orientation dynamically through Navigation.mergeOption() is supported only on Android.
Navigation.mergeOptions(this.props.componentId, {
layout: {
orientation: ['landscape']
}
});Website Versioned Docs Version 6.12.2 Docs Style Screens (`website/versioned_docs/version-6.12.2/docs/style-screens.mdx`)
id: style-options title: Styling with options sidebar_label: Options
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
A Screen's look and feel is configured with an Options object. Options can be applied to screens in three ways. Each method of applying options has benefits and draw backs which are important be aware of so we can use the right tool for the job.
Command options
Options can be passed to layouts as part of a command. For example when pushing a screen. Options passed in commands are typically dynamic options which are determined at runtime.
In the example below we're pushing a user profile screen and we'd like show the user name in the title. To do so, we set the title in the component's options.
Notice how the title is actually inferred from the user object which is set in passProps. While this works perfectly fine, declaring the title each time a screen is pushed is a bit tedious and error prone. Later on we'll see a more convenient method to declare dynamic options which are inferred from props.
Static options
Static options are options that are declared statically on the component class. Whenever a screen has a known predefined style, static options should be used as they are evaluated immediately when the screen is created.
<Tabs
defaultValue="class"
values={[
{ label: 'Class Component', value: 'class', },
{ label: 'Functional Component', value: 'functional', }
]
}>
Handle props in static options
Sometimes a screen's style includes properties defined in the calling scope where the screen is used. Like in the user profile screen we've seen above where the the user name is used as the TopBar title. As the user name is unique for each profile, it can't be defined explicitly in the static options.
Luckily, we can access props from static options and set the title from static options! Let's see how this is done:
<Tabs
defaultValue="class"
values={[
{ label: 'Class Component', value: 'class', },
{ label: 'Functional Component', value: 'functional', }
]
}>
Following this approach we can determine options that are dependent on other external factors, such as experiments or A/B tests.
Merge options
The mergeOptions command is used to update a layout's style. Keep in mind that the merge happens in native, and not ins JS. This meaning that if mergeOptions is called to update a screen's options, the static options declared on the React Component are not affected by mergeOptions.
The following example shows how to update a TopBar background color to red.
:::warning
Avoid using mergeOptions in a screen's constructor or in componentDidMount!
:::
When a screen first appears, it's constructor and componentDidMount functions are called followed by a componentDidAppear event. (read more about screen lifecycle here).
Developers might be tempted to call mergeOptions in constructor or componentDidMount to update options. Calling mergeOptions before the componentDidAppear event has been received has two very negative effects:
- Updating certain options can trigger additional layout and draw cycles which can have serious impact on performance.
- Options applied dynamically with the
mergeOptionscommand might be handled in native after the screen has already appeared. This will result in noticeable flickering and artifacts as options are updated after the initial options have been applied and are visible to the user.
Website Versioned Docs Version 6.12.2 Docs Style Statusbar (`website/versioned_docs/version-6.12.2/docs/style-statusbar.mdx`)
id: style-statusBar title: StatusBar sidebar_label: StatusBar
The StatusBar appearance is controlled through options.
For example, the following options will change the StatusBar background color to white will use dark colors for the StatusBar content (time, battery information, notification icons etc)
options: {
statusBar: {
backgroundColor: 'white',
style: 'dark'
}
}:::warning Compatibility with StatusBar component
React native's StatusBar component is incompatible with React Native Navigation and you should avoid using it.
:::
Changing StatusBar style dynamically
As the StatusBar is controlled through options, it can be configured dynamically by calling Navigation.mergeOptions with the desired StatusBar options.
For example, here's how you would hide the StatusBar dynamically
Navigation.mergeOptions(this.props.componentId, {
statusBar: {
visible: false
}
});How keep current StatusBar color when displaying screens
When screens are displayed, the StatusBar color always changes to the color associated with the current screen. If a color isn't specified for a given screen (and thus for the StatusBar), the default (System default or from defaultOptions) color is used. Sometimes you might want to keep the current StatusBar color, for example when displaying an alert or a toast. To keep StatusBar color unchanged when displaying a screen, use null as a StatusBar color.
options: {
statusBar: {
backgroundColor: null
}
}Website Versioned Docs Version 6.12.2 Docs Style Theme (`website/versioned_docs/version-6.12.2/docs/style-theme.mdx`)
id: style-theme title: Themes sidebar_label: Themes
A theme is a type of style which is applied to the entire app. It allows you to declare
a consistent style to all Navigation components such as the TopBar and BottomTabs and also
to system elements like the StatusBar and NavigationBar.
Applying a theme
Themes are applied using Navigation.setDefaultOptions() which must be called before Navigation.setRoot() is called.
// Set the default topBar background color to red
Navigation.setDefaultOptions({
topBar: {
background: {
color: 'red'
}
}
});
// That stack's topBar background color will be red, as is set in default options
Navigation.setRoot({
root: {
stack: {
children: [
...
]
}
}
});Conditional themes with Options Processor
Some style requirements can't be facilitated with the defaultOptions api. For example, an app may have a unique theme for screens displayed in a modal or require that modals have a default dismiss button in the TopBar. An Options Processor allow us to mutate the options object of each screen and layout right before they are displayed.
Lets see how we can leverage this API to add a dismiss button to the TopBar when showing a modal.
The Options Processors can also be used to set default options for buttons, here's how:
Conditional themes with Layout Processor
Layout Processor is similar in concept to the Options Processor discussed above. While Options Processor is invoked for each options object, Layout Processor is invoked once on the entire (layout)['../api/layout-layout/'] tree passed to the Navigation command.
Through the Layout Processor we can access the layout and it's children, and mutate both options and passProps before the layout is displayed. In this example we iterate on the stack's children and set a dark TopBar color according to a theme property in the child's props.
Changing theme dynamically
Apps can have multiple themes and sometimes you might need to change theme dynamically. To change current theme, simply call Navigation.setDefaultOptions() with updated theme options, following that with a call to Navigation.setRoot(). The reason we need to setRoot once more is because Navigation.setDefaultOptions() does not apply options to screens which had already been created.
Website Versioned Docs Version 6.12.2 Docs Third Party Mobx (`website/versioned_docs/version-6.12.2/docs/third-party-mobx.mdx`)
id: third-party-mobx title: MobX sidebar_label: MobX
import useBaseUrl from '@docusaurus/useBaseUrl';
MobX is one of the most popular state management libraries used by applications sized from small to large.
With the introduction of the new React Context API, MobX can now be very easily integrated in React Native Navigation
projects.
:::info Note
With the introduction of the new Context API, there is no need to use Provider pattern with MobX and you
can now just use the React Context API.
Also the example uses mobx-react-lite but you can use the normal mobx-react.
:::
Sharing a store between multiple screens
In the example below we will be creating a small Counter app. We will learn how to integrate Mobx with React-Native-Navigation and demonstrate how updating the store from one component, triggers renders in other components connected to the same store.
Once you finish implementing the example, your screen should look similar to this:
<img width="30%" src={useBaseUrl('/img/mobx_counter.png')} />
Step 1 - Create a Counter store
Let's first create a counter store using MobX. Our store has a single count object and two methods to increment and decrement it.
Step 2 - Consuming the store
You can consume the Counter store in any React components using React.useContext.
How to use MobX persistent data
Often the app will require a persistent data solution and with MobX you can use mobx-react-persist.
It only takes few extra steps to integrate the library.
Website Versioned Docs Version 6.12.2 Docs Third Party React Context (`website/versioned_docs/version-6.12.2/docs/third-party-react-context.mdx`)
id: third-party-react-context title: React Context API sidebar_label: React Context API
React Context API
React Context API provides a easy way to pass data through the component tree without having to pass props down
manually at every level. You can find more about the Context API in React documentation.
You can use the React Context API with React Native Navigation with a limitation. In this example, we are going to create a screen which uses the Counter Context.
:::important Limitation
As RNN screens are not part of the same component tree, updating the values in the shared context does not trigger a re-render across all screens.
However you can still use the React.Context per RNN screen component tree.
If you need to trigger a re-render across all screens, there are many popular third party libraries such as
MobX or Redux.
:::
Create a Counter context
Register the screen
When registering the screen that will be using the Counter Context, we need to wrap it
with the Counter Context Provider we created earlier.
Consuming the context
You can consume the Counter Context any way you want such as Provider.Consumer or React.useContext like in the
example below.
Website Versioned Docs Version 6.12.2 Docs Third Party Typescript (`website/versioned_docs/version-6.12.2/docs/third-party-typescript.mdx`)
id: third-party-typescript title: TypeScript sidebar_label: TypeScript
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
Strongly typed components
Both functional and class components can be typed to get the benefits of strongly typed options and props.
<Tabs
defaultValue="clazz"
values={[
{ label: 'Class Component', value: 'clazz' },
{ label: 'Functional Component', value: 'functional' },
]}
Typed props in commands
Arguments are passed to components view the passProp. This is a common source for errors as these props tend to change overtime. Luckily we can type the passProps property to avoid these regressions. The example below shows how to declare types for passProps when pushing a screen.
The following commands accept typed passProps:
- push
- setStackRoot
- showModal
- showOverlay
Website Versioned Docs Version 7.11.2 Docs Community Libraries (`website/versioned_docs/version-7.11.2/docs/community-libraries.mdx`)
id: community-libraries title: Community libraries sidebar_label: Community libraries
React Native Navigation has an active community with a bunch of support libraries.
React Native Navigation Hooks :
A set of React hooks for React Native Navigation.
React Native Navigation Drawer Extension :
Drawer API built on top of React Native Navigation for iOS and Android.
React Native Navigation Search Bar :
React Native Elements search bar with collapsible header built for React Native Navigation.
React Native Navigation Register Screens :
Utility function to register array of screens instead of calling Navigation.registerComponent multiple times.
Website Versioned Docs Version 7.11.2 Docs Docs Installing (`website/versioned_docs/version-7.11.2/docs/docs-Installing.mdx`)
id: installing title: Installation sidebar_label: Installation
Requirements
- node >= 8
- react-native >= 0.51
npm or yarn
npm:
npm install --save react-native-navigationyarn:
yarn add react-native-navigationInstalling with `npx rnn-link`
If you're using RN 0.60 or higher, you can benefit from autolinking for some of the necessary installation steps. But unlike most other libraries, react-native-navigation requires you to make a few changes to native files.
We've simplified the process through a set of scripts. So to make all the necessary changes automatically, in your project's root folder run:
npx rnn-link
Make sure to commit the changes introduced by the rnn-link script.
The automatic linking is optimized for new applications created via the
react-native initcommand. If you are migrating from a version of react-native-navigation older than v5, it's recommended to check the steps manually after the script runs.
If one of the steps failed or you can't run (or are not comfortable with) the automatic scripts, you'll need to complete the relevant steps in the manual installation steps below, for both platforms.
CocoaPods
After the the automatic scripts completed successfully, run pod install:
pod install --project-directory=iosUpdate index.js file
index.js is typically used as an entry point into the app. It's first parsed and executed by the JS engine, therefore we'll want to show our UI from there.
The following diff demonstrates changes needed to be made to index.js, initialized by react-native init.
+import { Navigation } from "react-native-navigation";
-import {AppRegistry} from 'react-native';
import App from "./App";
-import {name as appName} from './app.json';
-AppRegistry.registerComponent(appName, () => App);
+Navigation.registerComponent('com.myApp.WelcomeScreen', () => App);
+Navigation.events().registerAppLaunchedListener(() => {
+ Navigation.setRoot({
+ root: {
+ stack: {
+ children: [
+ {
+ component: {
+ name: 'com.myApp.WelcomeScreen'
+ }
+ }
+ ]
+ }
+ }
+ });
+});Finish
If you followed the steps successfully up to this point, then rebuild your application and you are good to go:
react-native run-ios
react-native run-android:::tip
This is a good moment to build your application in both platforms, validate that everything is working properly and commit your changes. If you're coming from a fresh react-native init project, then you should be seeing the Welcome screen as usual, but under the hood your application is using react-native-navigation!
::::
Manual Installation
If installation with npx rnn-link did not work, follow the manual installation steps below.
iOS
Make sure your Xcode is updated. We recommend editing
.hand.mfiles in Xcode as the IDE will usually point out common errors.
Installation with CocoaPods
Projects generated using newer versions of react-native use CocoaPods by default. In that case it's easier to install react-native-navigation using CocoaPods.
- Update your
Podfile:
If you're upgrading to v5 from a previous RNN version, make sure to remove manual linking of RNN
/* Detailed source-code truncated for AI context efficiency. */cd ios && pod install
Native Installation
If the React Native version in your project is above 0.60 and you are following the manual installation, skip to step 3.
If autolinking is not available in your project (RN version < 0.60), you can always try and install the hardcore way:
In Xcode, in Project Navigator (left pane), right-click on the
Libraries>Add files to [project name]. Addnode_modules/react-native-navigation/lib/ios/ReactNativeNavigation.xcodeproj(screenshots).In Xcode, in Project Navigator (left pane), click on your project (top), then click on your target row (on the "project and targets list", which is on the left column of the right pane) and select the
Build Phasestab (right pane). In theLink Binary With Librariessection addlibReactNativeNavigation.a(screenshots).
a. If you're seeing an error message in Xcode such as:
'ReactNativeNavigation/ReactNativeNavigation.h' file not found.You may also need to add a Header Search Path: (screenshots).
$(SRCROOT)/../node_modules/react-native-navigation/lib/ios- In Xcode, you will need to edit this file:
AppDelegate.m. This function is the main entry point for your app:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { ... }Its content should look like this:
#import "AppDelegate.h"
#import <React/RCTBundleURLProvider.h>
#import <React/RCTRootView.h>
#import <ReactNativeNavigation/ReactNativeNavigation.h>
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[ReactNativeNavigation bootstrapWithDelegate:self launchOptions:launchOptions];
return YES;
}
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
{
#if DEBUG
return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
#else
return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
#endif
}
- (NSArray> *)extraModulesForBridge:(RCTBridge *)bridge {
return [ReactNativeNavigation extraModulesForBridge:bridge];
}
@enda. If, in Xcode, you see the following error message in `AppDelegate.m` next to `#import "RCTBundleURLProvider.h"`:
```
! 'RCTBundleURLProvider.h' file not found
```
This is because the `React` scheme is missing from your project. You can verify this by opening the `Product` menu and the `Scheme` submenu.
To make the `React` scheme available to your project, run `npm install react-native-git-upgrade` followed by `react-native-git-upgrade`. Once this is done, you can click back to the menu in Xcode: `Product -> Scheme -> Manage Schemes`, then click '+' to add a new scheme. From the `Target` menu, select "React", and click the checkbox to make the scheme `shared`. This should make the error disappear.Android
Make sure your Android Studio installation is up to date. We recommend editing
gradleandjavafiles in Android Studio as the IDE will suggest fixes and point out errors, this way you avoid most common pitfalls.
1 Update `android/build.gradle`:
buildscript {
ext {
- minSdkVersion = 16
+ minSdkVersion = 21 // Or higher
compileSdkVersion = 26
targetSdkVersion = 26
supportLibVersion = "26.1.0"
+ kotlinVersion = "1.3.72" // Or any version above 1.3.x
+ RNNKotlinVersion = kotlinVersion
}
repositories {
google()
jcenter()
+ mavenLocal()
+ mavenCentral()
}
dependencies {
+ classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion")
+ classpath 'com.android.tools.build:gradle:3.5.3' // Or higher
- classpath 'com.android.tools.build:gradle:2.2.3'
}
}
allprojects {
repositories {
+ google()
mavenLocal()
jcenter()
maven {
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
url "$rootDir/../node_modules/react-native/android"
}
- maven {
- url 'https://maven.google.com/'
- name 'Google'
- }
+ maven { url 'https://jitpack.io' }
}
}2 Update `MainActivity.java`
MainActivity.java should extend com.reactnativenavigation.NavigationActivity instead of ReactActivity.
This file is located in android/app/src/main/java/com/<yourproject>/MainActivity.java.
-import com.facebook.react.ReactActivity;
+import com.reactnativenavigation.NavigationActivity;
-public class MainActivity extends ReactActivity {
+public class MainActivity extends NavigationActivity {
- @Override
- protected String getMainComponentName() {
- return "yourproject";
- }
}If you have any react-native related methods, you can safely delete them.
3 Update `MainApplication.java`
This file is located in android/app/src/main/java/com/<yourproject>/MainApplication.java.
/* Detailed source-code truncated for AI context efficiency. */App root
Now that you're done, don't forget to update the index.js file, as shown above.
Troubleshooting
Build app with gradle command
prefered solution - The RNN flavor you would like to build is specified in app/build.gradle. Therefore in order to compile only that flavor, instead of building your entire project using ./gradlew assembleDebug, you should instruct gradle to build the app module: ./gradlew app:assembleDebug. The easiest way is to add a package.json command to build and install your debug Android APK .
"scripts": {
...
"android": "cd ./android && ./gradlew app:assembleDebug && ./gradlew installDebug"
}Now run npm run android to build your application
Force the same support library version across all dependencies
Some of your dependencies might require a different version of one of Google's support library packages. This results in compilation errors similar to this:
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':app:preDebugBuild'.
> Android dependency 'com.android.support:design' has different version for the compile (25.4.0) and runtime (26.1.0) classpath. You should manually set the same version via DependencyResolutionTo resolve these conflicts, add the following to your app/build.gradle:
android {
...
}
configurations.all {
resolutionStrategy.eachDependency { DependencyResolveDetails details ->
def requested = details.requested
if (requested.group == 'com.android.support' && requested.name != 'multidex') {
details.useVersion "${rootProject.ext.supportLibVersion}"
}
}
}
dependencies {
...
implementation 'com.android.support:design:25.4.0'
implementation "com.android.support:appcompat-v7:${rootProject.ext.supportLibVersion}"
}Website Versioned Docs Version 7.11.2 Docs Docs Advanced Navigation (`website/versioned_docs/version-7.11.2/docs/docs-advanced-navigation.mdx`)
id: advanced-navigation title: Advanced navigation sidebar_label: Advanced navigation
import useBaseUrl from '@docusaurus/useBaseUrl';
Tab navigation
As mobile applications become bigger and more complex, they usually end up containing multiple primary high-level destinations, which are logically independent from one another. The BottomTabs layout is often used when an app contains three to five top-level destinations which should be accessible from anywhere in the app.
Lets return to the example code from the previous section (Basic navigation) and see how we can convert it to a tab-based application. We'll use the BottomTabs layout to display tabs at the bottom of the screen. Similarly to Stack layout, The BottomTabs layout also contains a children property where each child will be displayed in a tab.
Lets change our code to the following and reload the app.
/* Detailed source-code truncated for AI context efficiency. */Now our app has two tabs at the bottom of the screen and our users can easily navigate between them.
<img width="40%" src={useBaseUrl('img/stack4.png')} />
Replacing the root
Up until now, we've discussed how to navigate within the same layout structure. We'll now learn how to replace the entire layout structure to display a new Root. A real life example for this use case would be, for instance, if you need to switch from a login screen to the app's main root. Replacing the root fits this use case since we'd like to discard the previous root (login root) entirely when switching to the main root.
To demonstrate this, we'll make the following changes to our code:
- Add a login screen with a login button.
- When the button is clicked, we will switch to the main root.
- Replace our current root with the login screen. We'll also extract both roots to variables to improve code readability.
/* Detailed source-code truncated for AI context efficiency. */Conditional roots
Now that our initial root is the Login root, we're facing a new problem - how do we show the Login root only to users that are not logged in? Since our initial root is determined in the registerAppLaunchedListener callback, we'll check if a user is logged in there and set the appropriate root accordingly.
Navigation.events().registerAppLaunchedListener(async () => {
Navigation.setRoot(isLoggedIn() ? mainRoot : loginRoot);
});
function isLoggedIn() {
// TODO: your business logic goes here
}Website Versioned Docs Version 7.11.2 Docs Docs App Launch (`website/versioned_docs/version-7.11.2/docs/docs-app-launch.mdx`)
id: app-launch title: Launching the app sidebar_label: Launching the app
When your app is launched for the first time, the bundle is parsed and executed. At this point you need to display your UI. To do so, listen to the appLaunched event and call Navigation.setRoot when that event is received.
Navigation.events().registerAppLaunchedListener(() => {
// Each time the event is received you should call Navigation.setRoot
});:::important
Register the app launched listener as soon as possible - this should be one of the first lines in your index.js file.
:::
If you're observing a "white screen" or a hanging splash screen after relaunching your app, it probably means Navigation.setRoot isn't called as soon as the app is launched. Perhaps the listener was registered too late.
Difference between the platforms
When your app is launched, RN makes sure JS context (which is what enables you to execute JavaScript code) is running. There are quite a few differences between iOS and Android in this regard.
iOS
Whenever an app is put into the background, the app process can potentially be destroyed by the system. If this destruction takes place, the JS context will be destroyed alongside with the app process.
Android
An Android application is typically bound to two contexts:
- Application context - bound to the app process
- Activity context - bound to app UI
React's JS Context is executed on and bound to the Application context. This means that even when the Activity context (and thus the UI) is destroyed, React's JS Context remains active until the Application (and your process) are destroyed by the system.
:::caution
Because of this, when your app returns to foreground, JS Context might still exist on Android, even when the Activity and UI contexts have been destroyed - meaning you might not need to initialise all of your app logic; instead, you'll only need to call Navigation.setRoot to repopulate the UI context. This circumstance can easily be determined by setting a flag on your app's first appLaunched event, and checking the value of that flag on subsequent appLaunched events -- if the flag is true in your appLaunched callback, you need to call Navigation.setRoot to repopulate the UI.
:::
Website Versioned Docs Version 7.11.2 Docs Docs Basic Navigation (`website/versioned_docs/version-7.11.2/docs/docs-basic-navigation.mdx`)
id: basic-navigation title: Basic navigation sidebar_label: Basic navigation
import useBaseUrl from '@docusaurus/useBaseUrl';
Generally, any mobile app consists of various destinations which display some content to the user. And in vast majority of cases using an app means navigating between these destinations. React-native-navigation provides ways for you to layout your content on user screen in a logical and performant manner.
React Native Navigation's stack layout lets you push screens, and also navigate back to previous screens. Screens pushed into the stack hide the previous screen in the stack, making the user focus on a single screen at a time. Let's look at a stack layout that provides basic navigation for an app.
Creating a stack
// In index.js of a new project
import React from 'react';
import { View, Text, Button, StyleSheet } from 'react-native';
import { Navigation } from 'react-native-navigation';
const HomeScreen = (props) => {
return (
<View style={styles.root}>
<Text>Home Screen</Text>
</View>
);
};
Navigation.registerComponent('Home', () => HomeScreen);
Navigation.events().registerAppLaunchedListener(async () => {
Navigation.setRoot({
root: {
stack: {
children: [
{
component: {
name: 'Home'
}
}
]
}
}
});
});
const styles = StyleSheet.create({
root: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: 'whitesmoke'
}
});Running this code will render a screen with an empty TopBar and your HomeScreen component (shown below). The TopBar is part of the stack layout, we'll work on configuring it later.
<img width="40%" src={useBaseUrl('img/stack1.png')} />
Specifying options
You can specify options of each layout (Stack, component pushed into a stack, etc.) to configure various parameters like the TopBar title or color. Lets spice things up by changing the TopBar color and while we're at it, also move the title to the TopBar.
Lets change the Home screen declaration to the following and reload the app to see the changes.
const HomeScreen = (props) => {
return (
<View style={styles.root}>
<Text>Hello React Native Navigation 👋</Text>
</View>
);
};
HomeScreen.options = {
topBar: {
title: {
text: 'Home',
color: 'white'
},
background: {
color: '#4d089a'
}
}
}Our app should now look like this:
<img width="40%" src={useBaseUrl('img/stackOptions.png')} />
Navigating in a stack
In the previous section we created a stack and initialized it with a single child. We'll now learn how to add another child to the stack. From now on, we'll call the action of adding children to the stack 'push', and removing children - 'pop'.
In order to push another screen to the stack, we will add a button to the home screen and call Navigation.push. The 'push' command accepts two parameters, the first is the id used to indicate into which stack to push the screen and the second is the screen we're pushing. After pushing a screen, a back button is added automatically to the TopBar so the users can navigate easily back to the previous screen.
You can read more about the stack layout here or dive right into the API here.
:::info componentId
Each React component registered with Navigation.registerComponent is assigned a unique componentId by Navigation. This unique id is used with Navigation commands (like the push command) to indicate into which stack we'd like to push. In this case, by using the componentId of the Home screen, we are telling Navigation to push into the stack containing the Home screen.
:::
Navigation.push(props.componentId, {
component: {
name: 'Settings', // Push the screen registered with the 'Settings' key
options: { // Optional options object to configure the screen
topBar: {
title: {
text: 'Settings' // Set the TopBar title of the new Screen
}
}
}
}
});Lets change our code to the following snippet below and reload the app. Now our Home should contain a button which when pressed, pushes a new screen into the stack. We've successfully navigated to a new screen! 👏
/* Detailed source-code truncated for AI context efficiency. */<img width="40%" src={useBaseUrl('img/stack2.gif')} />
App theme
Our app is growing and already contains two screens. This introduces a new problem - while the home screen has a nice purple TopBar, our settings screen seems pretty dull as it still uses the default system look and feel.
Currently, our style declaration applies only to the Home screen, so lets apply it to all of our screens by using Navigation.setDefaultOptions.
Navigation.setDefaultOptions({
statusBar: {
backgroundColor: '#4d089a'
},
topBar: {
title: {
color: 'white'
},
backButton: {
color: 'white'
},
background: {
color: '#4d089a'
}
}
});We need to add this snippet before registering the registerAppLaunchedListener. This way we ensure our theme is applied when our root is set. Our code should now look like this:
/* Detailed source-code truncated for AI context efficiency. */Lets run our code again - now our design is consistent across both screens.
<img width="40%" src={useBaseUrl('img/stack3.gif')} />
Summary
We've learned the basics of navigation with React Native Navigation by implementing a stack and pushing screens into it.
We've also learned a few methods of applying styles to our screens and layouts with the Options mechanism.
- Navigation.setRoot() sets the root layout of our app. The initial root has to be set from an
AppLaunchedEventcallback. - Options can be applied directly to components.
- Screens can be added to a stack with the
Navigation.push()command. - Components registered with
Navigation.registerComponent()are injected with acomponentIdwhich is used when navigating. - Themes are applied via the
Navigation.setDefaultOptions()command.
In the next section we'll explore a more advance navigation patterns using BottomTabs layout and also see how, and why, multiple roots are set.
Website Versioned Docs Version 7.11.2 Docs Docs Before You Start (`website/versioned_docs/version-7.11.2/docs/docs-before-you-start.mdx`)
id: before-you-start title: Before you start sidebar_label: Before you start
React Native Navigation is a module, dependent on and intended to be used alongside React Native, so some experience with it and knowledge of core concepts is required. If you feel the need, you can start with their getting started tutorial, and then come back here when you're ready.
We also assume you are working on a Mac with XCode and Android Studio installed and setup. You can also make it work in a Linux distribution, of course, but in that case bear in mind that some sections of the docs that deal with iOS might not be relevant to you.
If you want to dig right into it, start with installing the library. If you're just looking around, we suggest checking out basic navigation first.
Website Versioned Docs Version 7.11.2 Docs Docs BottomTabs (`website/versioned_docs/version-7.11.2/docs/docs-bottomTabs.mdx`)
id: bottomTabs title: Bottom tabs sidebar_label: Bottom tabs
import useBaseUrl from '@docusaurus/useBaseUrl';
Bottom tabs refers to a row of links, displayed at the bottom of the screen, and is referred to as "Tab Bar" in iOS and as "Bottom Navigation Bar" on Android. Usually these are intended to be used to switch between top-level content views with a single tap.
Creating bottom tabs
BottomTabs provide flat navigation and access to up to five equally important top-level root destinations. While any type of layout can be displayed in a tab, typically, Stacks are used, since they allow for vertical navigation within a tab.
Example
Lets see how to create a simple BottomTabs layout. There are a few things to notice here:
- Each child represents a tab on the screen.
- The root layout for each tab is a stack to allow for vertical navigation within the tab.
- Each stack declares
bottomTaboptions which are used to configure the tab's text, icon, color etc.
bottomTabs: {
id: 'BOTTOM_TABS_LAYOUT',
children: [
{
stack: {
id: 'HOME_TAB',
children: [
{
component: {
id: 'HOME_SCREEN',
name: 'HomeScreen'
}
}
],
options: {
bottomTab: {
icon: require('./home.png')
}
}
}
},
{
stack: {
id: 'PROFILE_TAB',
children: [
{
component: {
id: 'PROFILE_SCREEN',
name: 'ProfileScreen'
}
}
],
options: {
bottomTab: {
icon: require('./profile.png')
}
}
}
}
]
}Once we run this code, our BottomTabs should look like this:
<img width="40%" src={useBaseUrl('img/bottomTabs.png')} />
Selecting Tabs Programmatically
Tabs can be selected programmatically by updating the currentTabIndex or currentTabId options.
We'll use the BottomTabs layout showcased above to demonstrate programmatic tab selection.
Selecting a tab by index
The following mergeOptions command will select the second tab. We're passing the id of our BottomTabs layout, but we could also use the id of any of the child components, for example PROFILE_TAB or PROFILE_SCREEN.
Navigation.mergeOptions('BOTTOM_TABS_LAYOUT', {
bottomTabs: {
currentTabIndex: 1
}
});Selecting a tab by id
Tabs can also be selected by id (componentId). This is particularly useful in cases where you don't know in which tab a screen is contained.
For example, if invoked from one of the child components, HOME_SCREEN or SETTINGS_SCREEN, the following merge command will select the tab containing the child.
Navigation.mergeOptions(this.props.componentId, {
bottomTabs: {
currentTabId: this.props.componentId
}
});Changing BottomTabs visibility
The visible property is used to control the BottomTab visibility. Visibility can be toggled dynamically using the mergeOptions command.
Navigation.mergeOptions(componentId, {
bottomTabs: {
visible: false
},
});Visibility can also be changed when pushing screens.
Navigation.push(componentId, {
component: {
name: 'pushedScreen',
options: {
bottomTabs: {
visible: false
}
}
}
});Updating tab options dynamically
To update a tab option, like an icon, text or color, simply call mergeOptions with new options using the id of a component or layout contained in the tab you wish to update.
For instance, in the example below we update the color of a specific tab:
Navigation.mergeOptions(componentId, {
bottomTab: {
iconColor: 'red',
textColor: 'red'
},
});Controlling tab loading
By default, all tabs are mounted at the same time. This can have a negative impact on performance since screens which are not visible to the user are mounted.
The order in which tabs are mounted can be configured with the tabsAttachMode option:
together- all tabs are mounted at the same time, this is the default behavior.afterInitialTab- after initial tab is mounted, other tabs are mounted.onSwitchToTab- initial tab is mounted, other tabs are mounted when the user navigates to them for the first time.
Website Versioned Docs Version 7.11.2 Docs Docs ExternalComponent (`website/versioned_docs/version-7.11.2/docs/docs-externalComponent.mdx`)
id: externalComponent title: External Component sidebar_label: External Component
The ExternalComponent layout allows you to display any native view as a screen. To use the External Component we'll need to register it with a string name. This name is then used when declaring layouts in JS.
Android
Implementing the native component
When it comes to implementing an external component on Android, you have two choices. We recommend you use a base class extending View. If you're required to use Fragments, you'll find an basic example below.
ViewGroup
public class ViewGroupComponent implements ExternalComponent {
private final FrameLayout content;
ViewGroupComponent(FragmentActivity activity, JSONObject props) {
content = new FrameLayout(activity);
}
@Override
public View asView() {
return content;
}
}Fragment
Using a Fragment as an external component is done by attaching the Fragment to a FrameLayout, and returning the FrameLayout from the asView() method of the ExternalComponent.
public class FragmentComponent implements ExternalComponent {
private final FrameLayout content;
FragmentComponent(FragmentActivity activity, JSONObject props) {
// Create the FrameLayout to which we'll attach our Fragment to
content = new FrameLayout(activity);
content.setId(R.id.fragment_screen_content);
// Attach the Fragment to the FrameLayout
activity.getSupportFragmentManager()
.beginTransaction()
.add(R.id.fragment_screen_content, createFragment(props))
.commitAllowingStateLoss();
}
private FragmentScreen createFragment(JSONObject props) {
FragmentScreen fragment = new FragmentScreen();
// Pass the props sent from Js in a bundle
Bundle args = new Bundle();
args.putString("text", props.optString("text", ""));
fragment.setArguments(args);
return fragment;
}
@Override
public View asView() {
return content;
}
}Registering the component
public class MainApplication extends NavigationApplication {
@Override
public void onCreate() {
super.onCreate();
registerExternalComponent("MyExternalComponent", new FragmentCreator());
}
}iOS
ViewController registration
[ReactNativeNavigation registerExternalComponent:@"MyExternalComponent" callback:^UIViewController *(NSDictionary *props, RCTBridge *bridge) {
return [[ExternalViewController alloc] initWithProps:props];
}];Using the component from JS
Once you've registered the external component in native, you can use it in your layout declarations.
For example, to show an external component modally:
Navigation.showModal({
externalComponent: {
name: 'MyExternalComponent',
passProps: {
text: "Text from JS"
}
}
});:::caution
props passed to external components are sent over the bridge and therefore must be serializable.
:::
Website Versioned Docs Version 7.11.2 Docs Docs Functional Components (`website/versioned_docs/version-7.11.2/docs/docs-functional-components.mdx`)
id: functionalComponents title: Using functional components as screens sidebar_label: Using functional components
Both class components and functional components can be used as screens. There is no performance difference between the two. Deciding which type component to use boils down to user preference.
Declaring static options
Static options are a great way to declare options close to where they are used. Declaring static options is done by first declaring the functional component, and then declaring the options on the function component.
const MyScreen = (props) => {
return (
<View>
<Text>Screen Component</Text>
</View>
);
};
MyScreen.options = {
topBar: {
background: {
color: 'red'
}
}
}Listening to navigation events
Navigation events let you react to various events related to a specific component or the app itself. Read more about them here
import { useEffect } from 'react';
import Navigation from 'react-native-navigation';
function MyScreen(props) {
useEffect(() => {
const listener = {
componentDidAppear: () => {
console.log('RNN', `componentDidAppear`);
},
componentDidDisappear: () => {
console.log('RNN', `componentDidDisappear`);
}
};
// Register the listener to all events related to our component
const unsubscribe = Navigation.events().registerComponentListener(listener, props.componentId);
return () => {
// Make sure to unregister the listener during cleanup
unsubscribe.remove();
};
}, []);
return (
<View>
<Text>Screen Component</Text>
</View>
);
}In the example above, our listener is registered with the componentId passed in the second argument, and will receive only its related events.
:::tip
underscopeio/react-native-navigation-hooks is a wonderful library which greatly simplifies usage with hooks by introducing dedicated hooks for each event. The following example, which is taken from their docs, shows how to listen to all appear events and a particular screen's appear events:
import { useNavigationComponentDidAppear } from 'react-native-navigation-hooks'
const ScreenComponent = ({ componentId }) => {
// Global listener
useNavigationComponentDidAppear(e => {
console.log(`${e.componentName} (${e.componentId}) appeared`)
})
// Listen events only for this screen (componentId)
useNavigationComponentDidAppear(e => {
console.log(`${e.componentName} appeared`)
}, componentId)
return (
<View>
<Text>Screen Component</Text>
</View>
)
}:::
Website Versioned Docs Version 7.11.2 Docs Docs Modal (`website/versioned_docs/version-7.11.2/docs/docs-modal.mdx`)
id: modal title: Modal sidebar_label: Modal
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import useBaseUrl from '@docusaurus/useBaseUrl';
In human-centered design, when we speak about a modal (or modality), we often refer to a set of techniques, aimed at bringing user attention to a specific event / screen / action / etc. Those often require user input. A pop-up on a website, a file deletion confirmation dialogue on your computer, or an alert asking you to enable location service on your phone - these can all be considered modals.
A modal is a term used in native iOS world (full description here), while on Android, dialogs are often used to create similar or identical behavior, alongside other techniques.
Presenting modals
Modal can be displayed by invoking the Navigation.showModal() command, as shown in the example below:
Navigation.showModal({
stack: {
children: [
{
component: {
name: 'MODAL_SCREEN',
options: {
topBar: {
title: {
text: 'Modal',
},
},
},
},
},
],
},
});Here's how the Modal looks on both platforms.
<Tabs
defaultValue="ios"
values={[
{ label: 'iOS', value: 'ios' },
{ label: 'Android', value: 'android' },
]}
Adding a dismiss button
Modals should always have a dismiss button, on left-to-right devices it's typically placed as a left button in the TopBar.
After we've added our dismiss button, we need to dismiss the modal when the button is pressed.
:::note
For the LeftButton to be visible, the screen must be displayed in a Stack.
:::
class ModalScreen extends React.Component {
// Set a dismiss button in static options
static options() {
return {
topBar: {
leftButtons: {
id: 'dismiss',
icon: require('./dismiss.png'),
},
},
};
}
constructor(props) {
super(props);
// Register to events
Navigation.events().bindComponent(this);
}
// Handle the button press event and dismiss the modal if needed
navigationButtonPressed({ buttonId }) {
if (buttonId === 'dismiss') {
Navigation.dismissModal(this.props.componentId);
}
}
}Transparent modals
Showing transparent modals is a nice technique to increase immersiveness while keeping the user in the same context.
When a modal is displayed the content behind it (either root or modal) is removed from hierarchy. While this behavior improves performance, it's undesired when
showing transparent modals as we need to see the content behind it.
To configure this behaviour, we'll need to change the modalPresentationStyle option to overCurrentContext and change the layout background color to 'transparent'.
{
modalPresentationStyle: 'overCurrentContext',
layout: {
backgroundColor: 'transparent'
}
}Preventing a Modal from being dismissed
Preventing a modal from being dismissed is done differently for each platform. While preventing the user from dismissing the modal is possible,
the modal could still be dismissed programmatically by calling Navigation.dismissModal()
If the modal has a dismiss button, of course you'll need to handle it your self and avoid calling Navigation.dismissModal()
when the button is pressed.
Android
On Android, modals can be dismissed with the hardware back button. You can handle the back press yourself by
disabling the hardware back button dismissModalOnPress option:
{
hardwareBackButton: {
dismissModalOnPress: false;
}
}As a result, pressing the hardware back button will not dismiss the modal and will dispatch navigationButtonPress event with the default hardware back button id RNN.hardwareBackButton.
iOS
While iOS devices don't have a hardware back button, PageSheet modals can be dismissed by swipe gesture
from the top of the screen. To disable it, set swipeToDismiss option to false.
Presentation Style
The presentation style determines the look and feel of a screen displayed as modal.
Website Versioned Docs Version 7.11.2 Docs Docs Overlay (`website/versioned_docs/version-7.11.2/docs/docs-overlay.mdx`)
id: overlay title: Overlay sidebar_label: Overlay
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import useBaseUrl from '@docusaurus/useBaseUrl';
Overlays are used to layout content on top of all other layout hierarchies, while fitting the screen bounds.
The contents displayed in an Overlay can either be non-obtrusive, like Toasts and Snackbars, or an Alert that blocks all interactions with any content behind it. The view contained within it should be aligned either with absolute position, flex, or with margins, all according to your needs.
Handling touch events outside the view
When showing views like Alert or Toast in an Overlay, you would want to configure if you want to allow users to interact with content behind the alert. This is done via the interceptTouchOutside option.
Overlay examples
<Tabs
defaultValue="alert"
values={[
{ label: 'Alert', value: 'alert', },
{ label: 'Toast', value: 'toast', }
]
}>
The example below demonstrates how to create a simple alert dialog using an Overlay. Touch events outside the alert will be blocked and won't pass through to the content behind the alert since we're specifying interceptTouchOutside: true in the static options of the Alert.
<img width="30%" src={useBaseUrl('/img/alert_android.png')} />
const React = require('react');
const { Text, Button, View } = require('react-native');
const { Navigation } = require('react-native-navigation');
function Alert({ componentId, title, message }) {
const dismiss = () => Navigation.dismissOverlay(componentId);
return (
<View style={styles.root}>
<View style={styles.alert}>
<Text style={styles.title}>{title}</Text>
<Text style={styles.message}>{message}</Text>
</View>
</View>
);
}
const styles = {
root: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#00000050',
},
alert: {
alignItems: 'center',
backgroundColor: 'whitesmoke',
width: 250,
elevation: 4,
padding: 16,
},
title: {
fontSize: 18,
},
message: {
marginVertical: 8,
},
};
Alert.options = (props) => {
return {
overlay: {
interceptTouchOutside: true,
},
};
};
module.exports = Alert;The example below demonstrates how to show a Toast using an Overlay. A user can interact with the content behind the toast since we've declared interceptTouchOutside: false in the static options of the Alert.
<img width="30%" src={useBaseUrl('/img/toast_android.png')} />
const React = require('react');
const { View, Text, StyleSheet, TouchableOpacity } = require('react-native');
const Colors = require('../commons/Colors');
const Navigation = require('../services/Navigation');
const Toast = function ({ componentId }) {
return (
<View style={styles.root}>
<View style={styles.toast}>
<Text style={styles.text}>This a very important message!</Text>
<TouchableOpacity
style={styles.button}
onPress={() => Navigation.dismissOverlay(componentId)}
>
<Text style={styles.buttonText}>OK</Text>
</TouchableOpacity>
</View>
</View>
);
};
const styles = StyleSheet.create({
root: {
flex: 1,
flexDirection: 'column-reverse',
},
toast: {
elevation: 2,
flexDirection: 'row',
height: 40,
margin: 16,
borderRadius: 20,
backgroundColor: Colors.accent,
alignItems: 'center',
justifyContent: 'space-between',
},
text: {
color: 'white',
fontSize: 16,
marginLeft: 16,
},
button: {
marginRight: 16,
},
buttonText: {
color: 'white',
fontSize: 16,
fontWeight: 'bold',
},
});
Toast.options = {
layout: {
componentBackgroundColor: 'transparent',
},
overlay: {
interceptTouchOutside: false,
},
};
module.exports = Toast;Website Versioned Docs Version 7.11.2 Docs Docs Passing Data To Components (`website/versioned_docs/version-7.11.2/docs/docs-passing-data-to-components.mdx`)
id: passing-data-to-components title: Passing data to components sidebar_label: Passing data to components
Initial props
React components use props to receive data when they are created. When displaying the component layout, you can pass initial props to components using the passProps property.
In this example, we push the UserProfile screen and pass two initial props to it:
Navigation.push(this.props.componentId, {
component: {
name: 'UserProfile',
id: 'PROFILE_SCREEN_ID'
passProps: {
name: 'John Doe',
status: 'online'
}
}
});:::tip Serialization
passProps don't need to be serializable. You can use this mechanism to pass lambda functions or even React Components.
:::
Handling passProps in static options
Each component can have a static options property which is used to configure the style properties of elements on the screen when that component is displayed.
Static options can either be a simple object, or a function which accepts passProps as an argument. Sometimes when declaring components, not all style properties are known. For example, a user profile screen will usually have the user's name as the TopBar title text. Therefore the title must be configured dynamically when pushing the screen.
Following the code example above, lets see how to use props passed in the push command to set the TopBar title.
class UserProfileScreen extends React.Component {
static options(props) {
return {
topBar: {
title: {
text: props.name
}
}
};
}
}Updating props
To update a component's props, use the Navigation.updateProps() command. Updating props triggers the component lifecycle methods related to updating.
Notice that we're using the component id in order to update props of this specific instance of the component.
Navigation.updateProps('PROFILE_SCREEN_ID', {
status: 'offline'
});Website Versioned Docs Version 7.11.2 Docs Docs Playground App (`website/versioned_docs/version-7.11.2/docs/docs-playground-app.mdx`)
id: playground-app title: Playground app sidebar_label: Playground app
Running The Project
If you want to have a quick look around and test things out, you can run the playground app, bundled with this repo.
- Install dependencies via
npm install(if you haven't already) andnpm run pod-install(for iOS) - Run the playground project on Android and iOS
npm run startto get the packager running in the terminal, leave it open- iOS: open
./playground/iosin Xcode and run it - Android: open
./playground/androidin Android Studio and run it
- You can run tests if / when you need to (list of scripts available here). Before
you start changing things, make sure everything works.- To easily run all tests in parallel
npm run test-all
- To easily run all tests in parallel
Website Versioned Docs Version 7.11.2 Docs Docs Root (`website/versioned_docs/version-7.11.2/docs/docs-root.mdx`)
id: root title: Root sidebar_label: Root
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
The root is where the application layout structure is defined. It is typically the first UI element a user interacts with. The root can be changed multiple times during the lifespan of the application. For example, if an app requires users to login, it's common to use a stack-based root and transition to either tabs- or SideMenu-based root if login is successful.
Setting root on app launch
RNN exposes an appLaunched listener which is invoked whenever root needs to be set.
On iOS, the app launched event is usually emitted once in the lifespan of the application - when the app is opened for the first time. On Android things become a little bit more complicated. Like on iOS, the event is emitted when an app is opened for the first time. Since the system can destroy the Activity when the app is in the background to free resources, the event is emitted when the app returns to foreground after the activity has been destroyed.
Navigation.events().registerAppLaunchedListener(() => {
Navigation.setRoot({
root: {
}
});
});:::important
registerAppLaunchedListener() must be called as soon as the bundle is executed. Otherwise the event, which is emitted from native to JS, won't be handled at the correct moment in time.
::::
Conditional initial root
A common use case is to set the initial root according to a condition of some sort. For example:
If a user is logged in, show the application main root; otherwise show a login screen.
To do this, simply set the appropriate root according to your needs.
Navigation.events().registerAppLaunchedListener(() => {
if (isUserLoggedIn()) {
setMainRoot();
} else {
setLoginRoot();
}
});Common root structures
<Tabs
defaultValue='stack'
values={[
{ label: 'Stack root', value: 'stack' },
{ label: 'BottomTabs root', value: 'bottomTabs' },
{ label: 'SideMenu root', value: 'sideMenu' }
]
}>
Navigation.setRoot({
root: {
stack: {
children: [
{
component: {
name: 'LOGIN_SCREEN'
}
}
]
}
}
});Navigation.setRoot({
root: {
bottomTabs: {
children: [
{
stack: {
children: [
{
component: {
name: 'FEED_SCREEN'
}
}
]
}
},
{
stack: {
children: [
{
component: {
name: 'CHAT_LIST'
}
}
]
}
},
{
stack: {
children: [
{
component: {
name: 'PROFILE_SCREEN'
}
}
]
}
}
]
}
}
});Navigation.setRoot({
root: {
sideMenu: {
center: {
stack: {
children: [
{
component: {
name: 'HOME_SCREEN'
}
}
]
}
},
left: {
component: {
name: 'MENU_SCREEN'
}
}
}
}
});Website Versioned Docs Version 7.11.2 Docs Docs Screen Lifecycle (`website/versioned_docs/version-7.11.2/docs/docs-screen-lifecycle.mdx`)
id: screen-lifecycle title: Screen Lifecycle sidebar_label: Screen Lifecycle
Any React Component registered with react-native-navigation is enhanced with two additional lifecycle events:
componentDidAppear- called each time a component is revealed to the usercomponentDidDisappear- called each time a component is hidden from user's view as a result of being detached from hierarchy
These methods compliment React's lifecycle methods:
componentDidMount- called once, when a component is attached to hierarchy for the first timecomponentWillUnmount- called once, when a component is destroyed
Mounting
These methods are called in the following order when a component is created and attached to hierarchy.
- constructor()
- render()
- componentDidMount()
- componentDidAppear()
Unmounting
These methods are called when a component is being removed from hierarchy
- componentDidDisappear()
- componentWillUnmount()
Modal
When a modal is displayed, depending on the modalPresentationStyle, content behind it might be detached from hierarchy. This affects the visibility events which are emitted to the content behind the modal.
When Modals with pageSheet or overCurrentContext modalPresentationStyle are displayed, previous content is still visible to the user. Thus componentDidDisappear event is not emitted.
Same is applied when a modal is dismissed. If it was originally presented with pageSheet or overCurrentContext modalPresentationStyle, when that modal is then dismissed, the previous context won't receive a componentDidAppear event.
Overlay
These methods are called in the following order when a component is displayed as an Overlay:
- constructor()
- render()
- componentDidMount()
- componentDidAppear()
:::note
Content displayed behind an Overlay does not receive the componentDidDisappear, since it's still visible to user and attached to the hierarchy.
:::
Website Versioned Docs Version 7.11.2 Docs Docs Showcases (`website/versioned_docs/version-7.11.2/docs/docs-showcases.mdx`)
id: showcases title: Showcases sidebar_label: Showcases
Apps
Hekla for Hacker News
A hacker news client built from the ground up with v2 of react-native-navigation.
It is a tab-based app and utilizes Split Layout for iOS tablets, 3D touch and more.
Starter Kits
kanzitelli/expo-rnn-starter - Simple yet practical starter with React Native Navigation and Expo Modules, MobX-State-Tree and Redux (pick one) and more goodies inside such as Typescript, RNN Hooks, RN Vector Icons, RN Gesture Handler. As an example, basic Reddit App implemented in two ways: using MobX-State-Tree in
/srcMobXand using Redux in/srcRedux.atoami/react-native-navigation-redux-starter-kit - React Native Navigation(v2) Starter Kit with Redux, Saga, ESLint, Babel and Jest 😎
alexwasner/react-native-mobx-firebase-starter - A React Native starter app with React Native Navigation(v2), MobX, Firebase, Lottie and More 🤓
hsyndeniz/react-native-quick-start - A React Native starter app with React Native Navigation, Redux, Firebase, Social Auth, Phone Auth, Push Notifications, Google Maps, Google Admob, Lottie and More 🤓
Playground
The playground example is a place where we showcase and test all features of react-native-navigation.
git clone https://github.com/wix/react-native-navigation
cd react-native-navigation
npm install
# for iOS:
npm run xcode # Opens XCode
npm start # Starts the packager on linux like systems
# for Android:
# 1. open Android Studio inside ".\playground"
# 2. start a emulator
npm run start-windows # Start the packager if you are on a windows system
# Run the app in Simulator or on Device from within XCode/Android StudioWebsite Versioned Docs Version 7.11.2 Docs Docs SideMenu (`website/versioned_docs/version-7.11.2/docs/docs-sideMenu.mdx`)
id: sideMenu title: Side Menu sidebar_label: Side Menu
SideMenu provides access to destinations in the app from an easy to access menu which is accessible to the user via horizontal swipe gesture or a menu button.
Opening programmatically
To open the side menu programmatically, you'll need to update the visible property of the side menu you'd like to open.
The following snippet shows how to open the left side menu. The menu is opened by setting its visible option to visible.
Navigation.mergeOptions(componentId, {
sideMenu: {
left: {
visible: true,
},
},
});Opening via the hamburger menu
The most common use case is to open the side menu by pressing the hamburger button at the TopBar. To achieve this, listen to the press event of the burger button, and open the side menu as shown above.
const React = require('react');
const Navigation = require('react-native-navigation');
const { View, Text } = require('react-native');
class SideMenuCenterScreen extends React.Component {
static options() {
return {
topBar: {
leftButtons: {
id: 'sideMenu',
icon: require('./menuIcon.png')
}
}
};
}
constructor(props) {
super(props);
Navigation.events().bindComponent(this);
}
render() {
return (
<View>
<Text>Click the hamburger icon to open the side menu</Text>
</View>
);
}
navigationButtonPressed({ buttonId }) {
if (buttonId === 'sideMenu') {
Navigation.mergeOptions(this, {
sideMenu: {
left: {
visible: true
}
}
});
}
}
}Pushing to the center layout from a menu
A common use case when using SideMenus is to interact with the center layout, e.g. pushing a screen to a stack in the center layout when a user clicks on a button from one of the menus.
In order to interact with the stack, we'll first assign it a predefined id. For example:
sideMenu: {
center: {
stack: {
id: 'CenterStack',
children: []
}
},
left: {
...
}
}Now that we've defined an id for out stack, we can go ahead and push our screen to it. If we're pushing while the SideMenu is open, we'll need to make sure we close it by updating the visibility option of the relevant menu (left or right).
Navigation.push('CenterStack', {
component: {
name: Screens.Pushed,
options: {
sideMenu: {
left: {
visible: false
}
}
}
}
});Website Versioned Docs Version 7.11.2 Docs Docs Stack (`website/versioned_docs/version-7.11.2/docs/docs-stack.mdx`)
id: stack title: Stack sidebar_label: Stack
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
A stack is a container layout promoting a hierarchical navigation. It is used for navigating between screens at consecutive levels of hierarchy, steps in a flow or across an app.
The first child in the stack (represented by the children array) is the root and is displayed at the bottom of the stack. The last child in the children array is the child currently being displayed.
In this layout, only a single child screen is visible at any given time and consecutive screen can be added to the top of the stack using the Navigation.push command. Tapping the back button will pop the stack and remove the top most screen.
The stack manages the TopBar at the top of the stack. The TopBar displays the current screens' title and buttons. It can be hidden with the topBar: { visible: false } option. By default, screens are rendered below the TopBar. This behavior can be changed by setting topBar: { drawBehind: true } in the current screens' options.
Layout Examples
<Tabs
defaultValue="single"
values={[
{ label: 'Single child', value: 'single', },
{ label: 'Multiple Children', value: 'multiple', }
]
}>
A stack declared with a single child.
const stack = {
children: [
{
component: {
name: 'MyComponent',
},
},
],
};A stack can be initialized with more than one child, in which case the last child will be the currently displayed child and the first child will be hidden. In this case the back button will be visible automatically, clicking it will go back in the stack revealing the first (previous) child.
Once the root child becomes visible, the back button is hidden.
const stack = {
children: [
{
component: {
name: 'RootComponent',
},
},
{
component: {
name: 'SecondComponent',
},
},
],
};TopBar Buttons
Buttons can be added to the right and left areas of the TopBar. Buttons can have either an icon or a text. They are declared in the the options object and, as with any other option, can be updated dynamically with the Navigation.mergeOptions command.
:::tip Always assign titles to buttons!
When using an icon button on Android, you should always pass a title as well. The title is used when the button is collapsed to the overflow menu and as a tooltip when the button is long pressed.
:::
Overflow menu
It's common practice to group less important actions in a menu or an action sheet.
To do so on iOS, include a button with a menu icon and open an ActionSheet with the relevant actions when the button is clicked.
On Android, use the showAsAction options to control when the button should appear in the menu.
Using a react component in a button
Sometimes we require more from our buttons. In order to support every product need, React Components can be used as custom views of buttons.
To do so, you'll first need to register the view with Navigation, just like you register your components used as screens:
Navigation.registerComponent('ButtonComponent', () => require('./ButtonComponent'));Now you can create buttons which use the component registered with 'ButtonComponent' as their custom view:
topBar: {
rightButtons: [
{
component: {
name: 'ButtonComponent',
passProps: {
// Pass initial props to the button here
},
},
},
];
}Updating props of custom buttons
To update props of a mounted component used as a button, you'll first need to assign it a unique id, then call the Navigation.updateProps() command with the id.
Calling the updateProps command will trigger Reacts component lifecycle methods related to props update
// Declare the button and assign it a unique id
topBar: {
rightButtons: [
{
id: 'SomeUniqueId',
component: {
name: 'ButtonComponent',
passProps: {
count: 0
},
},,
},
];
}
// Update props
Navigation.updateProps('SomeUniqueId', {
count: 1,
});Changing buttons dynamically
As buttons are part of a screen's options, they can be modified like any other styling option using the mergeOptions command.
Setting buttons
The following command will set the screen's right buttons. If the screen already has Right Buttons declared - they will be overridden.
Navigation.mergeOptions(this.props.componentId, {
topBar: {
rightButtons: [
{
id: 'myDynamicButton',
text: 'My Button',
},
],
},
});Removing buttons
Buttons can be removed by setting zero buttons, as shown in the snippet below.
Navigation.mergeOptions(this.props.componentId, {
topBar: {
rightButtons: [],
},
});Back Button
The back button is added automatically when two or more screens are pushed into the stack.
Styling the back button
The back button's style can be customized by declaring a backButton options object. This configuration can be part of a screen's static options, or default options.
backButton: {
color: 'red',
icon: require('../../img/customChevron.png')
}Controlling visibility
The back buttons visibility can be controlled with the visible property.
backButton: {
visible: false;
}Changing visibility programmatically
Back button visibility can be changed dynamically using the mergeOptions command. When using a screen's componentId, the change will affect only that specific screen. But when using the stack's id, the change will affect all screens pushed into the stack.
Navigation.mergeOptions(this.props.componentId, {
backButton: {
visible: false,
},
});Handling the back button
Handling the back button press event is done by first disabling the default back button behaviour by adding:
topBar: {
backButton: {
popStackOnPress: false,
}
}As a result, pressing the back button will not pop the stack and will dispatch navigationButtonPress event with the default back button id RNN.back.
Handling the hardware back button on Android
Same as handling the default back button, handling the hardware back button is done by disabling popStackOnPress for the hardware button:
hardwareBackButton: {
popStackOnPress: false,
}As a result, pressing the hardware back button will not pop the stack and will dispatch navigationButtonPress event with the default hardware back button id RNN.hardwareBackButton.
Interacting programmatically
The Navigation object provides ways to programmatically manipulate the stack.
Interact with the Stack by componentId
Each layout pushed into the stack has an id. When in the context of a component, The component's componentId can be used to interact with a parent stack.
When using a component's componentId, the native implementation knows to perform the command on the parent Stack of this component.
In this example, we push a screen onto the component's parent stack.
const React = require('react');
const Navigation = require('react-native-navigation');
class MyComponent extends React.Component {
onButtonClick = () => {
Navigation.push(this.props.componentId, {
component: {
name: 'PUSHED_SCREEN',
},
});
};
}Interact with the Stack by a predefined id
Sometimes we're required to interact with a specific stack not from the context of a component pushed into it. To do so, assign the stack a predefined id and use it when invoking any stack command.
Navigation.setRoot({
root: {
stack: {
id: 'MyStack', // This is the id we're going to use when interacting with the stack
children: [
{
component: {
name: 'SomeComponent',
},
},
],
},
},
});
function push() {
Navigation.push('MyStack', {
component: {
name: 'PushedScreen',
},
});
}Website Versioned Docs Version 7.11.2 Docs Meta Contributing (`website/versioned_docs/version-7.11.2/docs/meta-contributing.mdx`)
id: meta-contributing title: Contributing sidebar_label: Contributing
Thank you for showing interest in contributing to React-Native-Navigation! This project is developed and maintained by Wix in collaboration with the community.
There are various ways in which you can contribute to this library, not all require knowledge or expertise in native development. Listed below is all you need to get started with your first contribution.
Stack Overflow
Stack Overflow is used by developers to find answers and troubleshoot code. Users are encouraged to post questions on SO and tag them with the wix-react-native-navigation tag. Answering issues on SO is very helpful and beneficial to the community, only this time, there's a personal angle - you can boost your SO ranking and rack up points quickly.
Discord
Another popular communication channel is our Discord channel where users post questions and consult with each other. You're welcome to join the discussions and answer questions. This is also a great place to meet other community members and project maintainers.
Contributing to React-Native-Navigation
iOS
There are 2 ways to test your contribution to this project on iOS:
You can contribute and test your changes using the playground provided.
You can install React-Native-Navigation in a new or existing project and set your project to use your local clone of React-Native-Navigation in the podfile.
The following steps are required in order to make the project work with your local environment:
2.1
Install React-Native-Navigation as you'll usually do in your project, using npm install --save react-native-navigation.
2.2
Add the following configuration to the react-native.config.js file, at the root of your project.
module.exports = {
project: {
ios: {},
android: {},
},
dependencies: {
'react-native-navigation': {
platforms: {
ios: null,
android: null
},
},
},
};2.3
Clone the project using git clone https://github.com/wix/react-native-navigation at your desired location.
2.4
Add the following line in the Podfile:
pod 'ReactNativeNavigation', :path => '/path/to/cloned/react-native-navigation'You're done. Happy coding!
GitHub Issues
Open an issue
Found a bug? Missing a feature? Go ahead and open an issue! Make sure to add all details mentioned in the issue template. If you're interested in suggesting a big change, please speak to one of the admins on Discord to coordinate your effort.
Respond to issues
Although the issue tracker is used solely for bug reports, issues are frequently opened for questions or to request assistance. As the project grows in popularity, more issues remain unanswered for long periods of time.
Some issues can be trivial and easy to pinpoint - a missing import statement or apostrophe, wrong layout structure, etc. If you're an experienced user, helping out newcomers can be quite satisfying and allows maintainers to focus on features and bug fixes.
Some issues are tagged as "looking for contributors". These issues have been recognized by the team, but aren't prioritized by Wix developers due to lack of time or for some other reason. We leave these issues to our community and you're more than welcome to take a crack at them. If you'd like to submit a PR, see these instructions on how to run the project locally.
Provide reproductions
Reproducing bugs takes time. Lots of time. Usually that's because an issue is lacking important information, which then causes lots of back and forth communication between maintainers and users. Help us address more bugs and issues by providing reproductions.
Providing reproductions improves the chances of an issue being prioritized by maintainers!
If an issue is reproduced with a specific combination of options, posting these options will usually suffice. If a specific layout structure is involved or specific actions need to be performed in a certain order - then we ask that you fork the project and push a branch with a reproduction to the Playground app.
Check out the list of issues requiring reproductions.
Submitting PRs
So you've fixed a bug or added a feature and you're ready to submit a pull request 🎉🎊 Make sure the title is clear and describes the reason for the PR. Please include any information you can which will help us understand the reasons for the PR, what it fixes and what it changes. Please include before/after pictures/gifs when appropriate.
Workflow
This project is driven by tests. Before implementing any feature or fixing any bug, a failing test (e2e or unit or both) should be added, depending on the environment of where the fix should be implemented. For example, for an API change, a failing e2e should be written. For a small bug fix in Android, for example, a unit test in Android should be added.
This will ensure good quality throughout the life of the project and will help avoid unexpected breakages.
No PR will be accepted without adequate test coverage.
If you need help running the project, have a look at the Playground app section. You can run the tests using the scripts below.
Tests and the Playground app
Besides an overview of basic React Native Navigation functionality, the Playground app can (and rather should) be used for e2e tests and reproductions. If your change requires an e2e, add it to the playground app, to the appropriate screen. Again, quick setup instructions available in Playground app section of these docs.
:::tip
If a screen contains too many buttons, e2e tests might fail if the button is positioned out of screen bounds because Detox matchers detect it's invisible.
:::
Enable linter
The project uses ESLint with Prettier to ensure code style consistency across the codebase. Make sure to install these plugins in your IDE.
A pre-commit hook will verify that the linter passes when committing.
Playground app Folder Structure
| Folder | Description |
|---|---|
lib |
The project itself composed of: |
lib/android |
android sources and unit tests |
lib/ios |
iOS sources and unit tests |
lib/src |
TypeScript sources and unit tests |
lib/dist |
compiled javascript sources and unit tests |
lib/dist/index.js |
the entry point for import Navigation from 'react-native-navigation' |
e2e |
detox e2e tests on both Android and iOS |
playground |
The end-user project all e2e tests run against. Contains its own src, android and ios. Does not have its own package.json, depends on the local <root>/lib for faster local development (no need to npm install locally). |
integration |
misc javascript integration tests, proving integration with other libraries like redux |
scripts |
all scripts |
Scripts
| Command | Description |
|---|---|
npm install |
installs dependencies |
npm run build |
compiles TypeScript sources ./lib/src into javascript ./lib/dist |
npm run clean |
cleans all build directories, stops packager, fixes flakiness by removing watchman cache, etc. |
npm run start |
starts the react-native packager for local debugging |
npm run xcode |
for convenience, opens xcode in this project |
npm run install-android |
builds playground debug/release version and installs on running android devices/emulators. Options: -- --release |
npm run uninstall-android |
uninstalls playground from running android devices/simulators |
npm run test-js |
runs javascript tests and coverage report |
npm run test-unit-ios |
runs ios unit tests in debug/release Options: -- --release |
npm run test-unit-android |
runs android unit tests in debug/release Options: -- --release |
npm run test-e2e-ios |
runs the ios e2e tests using detox in debug/release Options: -- --release |
npm run test-e2e-android |
runs the android e2e tests using detox in debug/release Options: -- --release |
npm run test-all |
runs all tests in parallel |
Website Versioned Docs Version 7.11.2 Docs SideMenu Disable (`website/versioned_docs/version-7.11.2/docs/sideMenu-disable.mdx`)
id: sideMenu-disable title: Disable Open and Close gesture sidebar_label: Open and Close Gesture
To open the side menu programmatically, you'll need to update the visible property of the side menu you'd like to open.
The following snippet shows how to open the left side menu.
Navigation.mergeOptions(componentId, {
sideMenu: {
left: {
visible: true,
},
},
});Open by pressing on the hamburger menu
The most common use case is to open the side menu by pressing the hamburger button in the TopBar. To achieve this, listen to the press event of the burger button, and open the side menu as shown above.
const React = require('react');
const Navigation = require('react-native-navigation');
const { View, Text } = require('react-native');
class SideMenuCenterScreen extends React.Component {
static options() {
return {
topBar: {
leftButtons: {
id: 'sideMenu',
icon: require('./menuIcon.png')
}
}
};
}
constructor(props) {
super(props);
Navigation.events().bindComponent(this);
}
render() {
return (
<View>
<Text>Click the hamburger icon to open the side menu</Text>
</View>
);
}
navigationButtonPressed({ buttonId }) {
if (buttonId === 'sideMenu') {
Navigation.mergeOptions(this, {
sideMenu: {
left: {
visible: true
}
}
});
}
}
}Website Versioned Docs Version 7.11.2 Docs Stack BackButton (`website/versioned_docs/version-7.11.2/docs/stack-backButton.mdx`)
id: stack-backButton title: The Back button sidebar_label: Back button
The back button is added automatically when two or more screens are pushed into the stack.
Styling the back button
The back button's style can be customised by declaring a backButton options object. This configuration can be part of a screen's static options, or default options.
backButton: {
color: 'red',
icon: require('../../img/customChevron.png')
}Controling visibility
The back buttons visbility can be controlled with the visible property.
backButton: {
visible: false
}Changing visbility programmatically
Back button visibility can be changed dynamically using the mergeOptions command. When using a screen's componentId, the change will affect only that specific screen. But when using the stack's id, the change will affect all screens pushed into the stack.
Navigation.mergeOptions(this.props.componentId, {
backButton: {
visible: false
}
});Handling the back button
Handling the back button is not possible. However, you can set a left button with a chevron and handle it like you'd handle any other button and calling Navigation.pop when desired.
Website Versioned Docs Version 7.11.2 Docs Stack BackNavigation (`website/versioned_docs/version-7.11.2/docs/stack-backNavigation.mdx`)
id: stack-backNavigation title: Back Navigation sidebar_label: Back navigation
Disabling back navigation
Handling back navigation
Website Versioned Docs Version 7.11.2 Docs Stack Buttons (`website/versioned_docs/version-7.11.2/docs/stack-buttons.mdx`)
id: stack-buttons title: TopBar Buttons sidebar_label: TopBar Buttons
Buttons can be added to the right and left areas of the TopBar. Buttons can have either an icon or a text. They are declared in the the options object and, as with any other option, can be updated dynamically with the Navigation.mergeOptions command.
When using an icon button on Android, you should always pass a title as well. The title is used when the button is collapsed to the overflow menu and as a tooltip when the button is long pressed.
Overflow menu
It's common practice to group less important actions in a menu or an action sheet.
To do so on iOS, include a button with a menu icon and open an ActionSheet with the relevant actions when the button is clicked.
On Android, use the showAsAction options to control when the button should appear in the menu.
Left button
Left buttons behave like right buttons with two caveats on Android:
- Only a single left button is alowed
- Textual left button isn't supported
Using a react component in a button
⚠️At the moment, Android only supports using custom buttons in
rightButtons.
Sometimes we require more from our buttons. In order to support every product need React Components can be used as custom views of buttons.
To do so, you'll first need to register the view with Navigation, just like you register your components used as screens:
Navigation.registerComponent('ButtonComponent', () => require('./ButtonComponent'));Now you can create buttons which use the component registered with 'ButtonComponent' as thier custom view:
topBar: {
rightButtons: [
{
component: {
name: 'ButtonComponent',
passProps: {
// Pass initial props to the button here
},
},
},
];
}Updating props of custom buttons
To update props of a mounted compoennt used as a button, you'll first need to assign it a unique id, then call the Navigation.updateProps() command with the id.
Calling the updateProps command will trigger React's component lifecycle methods related to props update
// Declare the button and assign it a unique id
topBar: {
rightButtons: [
{
id: 'SomeUniqueId',
component: {
name: 'ButtonComponent',
passProps: {
count: 0
},
},,
},
];
}
// Update props
Navigation.updateProps('SomeUniqueId', {
count: 1,
});Changing buttons dynamically
As buttons are part of a screen's options, they can be modified like any other styling option using the mergeOptions command.
Setting buttons
The following command will set the screen's right buttons. If the screen already has Right Buttons declared - they will be overridden.
Navigation.mergeOptions(this.props.componentId, {
topBar: {
rightButtons: [
{
id: 'myDynamicButton',
text: 'My Button',
},
],
},
});Removing buttons
Buttons can be removed by setting zero buttons, as shown in the snippet below.
Navigation.mergeOptions(this.props.componentId, {
topBar: {
rightButtons: [],
},
});Website Versioned Docs Version 7.11.2 Docs Stack Programmatically (`website/versioned_docs/version-7.11.2/docs/stack-programmatically.mdx`)
id: stack-programmatically title: Interact programmatically with the Stack sidebar_label: Interact programmatically
The Navigation object provides ways to programmatically manipulate the stack.
Interact with the Stack by componentId
Each layout pushed into the stack has an id. When in the context of a component, The component's componentId can be used to interact with a parent stack.
When using a component's componentId, the native implementation knows to perform the command on the parent Stack of this component.
In this example, we push a screen onto the component's parent stack.
const React = require('react');
const Navigation = require('react-native-navigation');
class MyComponent extends React.Component {
onButtonClick = () => {
Navigation.push(this.props.componentId, {
component: {
name: 'PUSHED_SCREEN'
}
});
}
}Interact with the Stack by a predefined id
Sometimes we're required to interact with a specific stack not from the context of a component pushed into it. To do so, assign the stack a predefined id and use it when invoking any stack command.
Navigation.setRoot({
root: {
stack: {
id: 'MyStack', // This is the id we're going to use when interacting with the stack
children: [
{
component: {
name: 'SomeComponent'
}
}
]
}
}
});
function push() {
Navigation.push('MyStack', {
component: {
name: 'PushedScreen'
}
});
}Website Versioned Docs Version 7.11.2 Docs Style Animations (`website/versioned_docs/version-7.11.2/docs/style-animations.mdx`)
id: style-animations title: Animations sidebar_label: Animations
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import useBaseUrl from '@docusaurus/useBaseUrl';
Various UI elements can be animated by declaring animation options.
You can change the default animations for various commands, like push and pop, and even animate elements in
your screens during screen transitions.
Animation properties
The following properties can be animated with our animation framework:
- x - Translate the view to a coordinate along the x axis.
- y - Translate the view to a coordinate along the y axis.
- translationX - Translate the view along the x axis relative to its current x position.
- translationY - Translate the view along the y axis relative to its current y position.
- alpha - Apply alpha animation to the view. A value of 0 means the view is not visible, 1 means it's visible.
- scaleX - Scale the view on the x axis. A value of 1 means that no scaling is applied.
- scaleY - Scale the view on the y axis. A value of 1 means that no scaling is applied.
- rotationX - Applies rotation around the x axis (in degrees, passed as a float).
- rotationY - Applies rotation around the y axis (in degrees, passed as a float).
- rotation - Rotates the view on all axis. Positive values result in clockwise rotation.
Layout animations
Stack animations
When animating screens in and out of a stack, there are three elements you can work with:
- content - screen being pushed or popped
- topBar - stack's TopBar
- bottomTabs - if stack is a child of a bottomTabs layout, we can control the
hideandshowanimations of the bottomTabs.
The following example demonstrates how to replace the default push and pop animations with slide animations.
<Tabs
defaultValue="push"
values={[
{ label: 'Push', value: 'push', },
{ label: 'Pop', value: 'pop', },
]
}>
options: {
animations: {
push: {
content: {
translationX: {
from: require('react-native').Dimensions.get('window').width,
to: 0,
duration: 300
}
}
}
}
}options: {
animations: {
pop: {
content: {
translationX: {
from: 0,
to: -require('react-native').Dimensions.get('window').width,
duration: 300
}
}
}
}
}Modal animations
Modal animations are declared similarly to stack animations, only this time we animate the entire view and not only part of the UI (content).
The appearing modal can play custom enter animation and exit for the disappearing screen when showing/dismissing modal.
<Tabs
defaultValue="show"
values={[
{ label: 'Show', value: 'show', },
{ label: 'Dismiss', value: 'dismiss', },
]
}>
The following example demonstrates how to show a modal with a fade-in animation.
:::warning Deprecation warning
options: {
animations: {
showModal: {
alpha: {
from: 0,
to: 1,
duration: 300
}
}
}
}Consider using enter, exit animations instead.
:::
options: {
animations: {
showModal: {
enter :{
enabled:true,
alpha: {
from: 0,
to: 1,
duration: 300
}
},
exit :{
enabled:true,
alpha: {
from: 1,
to: 0,
duration: 300
}
}
}
}
}The following example demonstrates how to dismiss a modal with a fade-out animation.
:::warning Deprecation warning
Consider using enter, exit animations instead.
options: {
animations: {
dismissModal: {
alpha: {
from: 1,
to: 0,
duration: 300
}
}
}
}:::
options: {
animations: {
dismissModal: {
exit:{
enabled:true
alpha: {
from: 1,
to: 0,
duration: 300
}
},
enter:{
enabled:false
alpha: {
from: 0,
to: 1,
duration: 300
}
}
}
}
}Shared element transitions
Shared element transitions allow us to provide visual continuity when navigating between destinations. This also focuses user attention on a particular significant element, which then also gives such user better context when transitioning to some other destination.
:::caution
At the moment, the transition is available for push and pop commands. We are working on expanding supported commands to show/dismiss modal and change BottomTabs.
:::
Transition breakdown
Let's take a more in-depth look at the following example, which you can find in the playground app:
Source screen and the Destination screen
Four elements are animated in this example. Let's list the elements involved in the transition and note which properties are being animated.
image - the item image is animated to the next screen.
- image scale (resizeMode)
- position on screen
image background - each item has a "shadow" view which transitions to the next screen and turns into a colorful header.
- scale
- position on screen
- color
title - the title of the item is animated to the next screen.
- font size
- font color
- position on screen
Description - the description of the item in the destination screen.
- fade-in
- translation y
Implementing shared element transitions
Step 1 - set a nativeID prop to elements in the source screen
In order for RNN to be able to detect the corresponding native views of the elements,
each element must include a unique nativeID prop.
Step 2 - set a nativeID prop to elements in the destination screen
Step 3 - Declare the shared element animation when pushing the screen
Navigation.push(this.props.componentId, {
component: {
name: Screens.CocktailDetailsScreen,
passProps: { ...item },
options: {
animations: {
push: {
sharedElementTransitions: [
{
fromId: `image${item.id}`,
toId: `image${item.id}Dest`,
interpolation: { type: 'linear' }
},
],
},
},
},
},
});The interpolation property is an object which consists of a type property, and optional parameters to further configure the interpolation function. The following types are supported:
'accelerate': Begin building up speed until destination has been reached.factor: The acceleration factor. Higher values look faster.
'decelerate': Start at a high speed and slow down to settle.factor: The acceleration factor. Higher values look slower.
'accelerateDecelerate': Begin building up speed until half of the animation is complete, then begin slowing down to settle.'decelerateAccelerate': Start at a high speed and slow down until half of the animation is complete, then speed up again to settle.'linear': Linearly move towards the destination.'overshoot': Begin building up speed and overshoot the destination, then fling back to settle.tension: The tension of the overshoot animation defines how far the object can overshoot. Default:1
'spring': Acts like an actual spring. Depending on the configuration, this interpolation can overshoot the destination multiple times, or be very stiff and smoothly ease towards the destination.mass: The mass defines how "heavy" the object is. Default:3damping: The damping property specifies how much the moving object is being slowed down. Higher values can lead to overdamping, lower values to underdamping. Default:500stiffness: The stiffness property defines how stiff the animated object is. Default:200
:::caution
Since a Spring interpolation is not a time based animation, you have to manually tweak the configuration to match the duration of the animation, otherwise the remaining time of the Spring interpolation will get skipped.
:::
Element Transitions
Element transitions also allow you to animate elements during shared element transitions.
Recreating
Step 1 - Set a nativeID prop to the element you'd like to animate
An element can either be in the source or destination screen.
<Text nativeID="description" style={styles.description}>
{this.props.description}
</Text>Step 2 - Declare the element animation when pushing the screen
Navigation.push(this.props.componentId, {
component: {
name: Screens.CocktailDetailsScreen,
passProps: { ...item },
options: {
animations: {
push: {
elementTransitions: [
{
id: 'description',
alpha: {
from: 0, // We don't declare 'to' value as that is the element's current alpha value, here we're essentially animating from 0 to 1
duration: SHORT_DURATION,
},
translationY: {
from: 16, // Animate translationY from 16dp to 0dp
duration: SHORT_DURATION,
},
},
],
},
},
},
},
});Peek and Pop (iOS 11.4+)
react-native-navigation supports Peek and pop feature in iOS 11.4 and newer.
This works by passing a ref to a component you want to transform into peek view. We have included a handy component to handle all the touches and ref for you:
const handlePress ({ reactTag }) => {
Navigation.push(this.props.componentId, {
component {
name: 'previewed.screen',
options: {
preview: {
reactTag,
height: 300,
width: 300,
commit: true,
actions: [{
title: "Displayed Name",
id: "actionId",
style: 'default', /* or 'selected', 'destructive'*/
actions: [/*define a submenu of actions with the same options*/]
}]
},
},
},
});
};
const Button = (
<Navigation.TouchablePreview
touchableComponent={TouchableHighlight}
onPress={handlePress}
onPressIn={handlePress}
>
<Text>My button</Text>
</Navigation.TouchablePreview>
);All options except for reactTag are optional. Actions trigger the same event as navigation button presses. To react when a preview is committed, listen to the previewCompleted event.
Website Versioned Docs Version 7.11.2 Docs Style Constants (`website/versioned_docs/version-7.11.2/docs/style-constants.mdx`)
id: style-constants title: Constants sidebar_label: Constants
React Native Navigation exposes a set of constants which can be used to get the dimensions of various navigation elements on the screen: StatusBar, TopBar and BottomTabs.
When are constants evaluated
Some of the values exposed through the constants API are actually evaluated only after the UI is created (setRoot has been called) and therefore are not accessible through static getters.
For example, if you need to get the height of the BottomTabs, you'll first need to have BottomTabs visible on the screen and only then retrieve their height via the constants API.
:::important
We recommend you don't cache the actual constants object returned by await Navigation.constants() as it might not be accurate later on when, for example, a new root is set or orientation changes.
:::
API
As explained above, constants are evaluated in native each time the API is invoked. That's why Navigation.constants() returns a promise and the use is as follows:
const { Navigation } = require('react-native-navigation');
const {
statusBarHeight,
topBarHeight,
bottomTabsHeight
} = await Navigation.constants();Website Versioned Docs Version 7.11.2 Docs Style Fonts (`website/versioned_docs/version-7.11.2/docs/style-fonts.mdx`)
id: style-fonts title: Changing fonts sidebar_label: Changing fonts
Before you begin using your own fonts, you'll first need to add them to the app project.
- Android - add the
.ttfor.otffiles tosrc/main/assets/fonts/ - iOS - follow this guide
Once we've added the font files to our project, we can star using them in options.
:::note
When declaring fonts in options, use only the file name without the file type suffix.
:::
BottomTab font
options: {
bottomTab: {
fontFamily: 'helvetica'
}
}Button font
options: {
rightButtons: [
{
id: 'SAVE_BUTTON',
text: 'Save',
fontFamily: 'helvetica'
}
],
leftButtons: [
{
id: 'CANCEL_BUTTON',
text: 'Cancel',
fontFamily: 'helvetica'
}
]
}Title font
options: {
topBar: {
title: {
fontFamily: 'helvetica'
}
}
}Subtitle font
options: {
topBar: {
subtitle: {
fontFamily: 'helvetica'
}
}
}Back button
In iOS the back button will display the title of the previous screen. Use the backButton option to modify its font family.
options: {
topBar: {
backButton: {
fontFamily: 'helvetica'
}
}
}Website Versioned Docs Version 7.11.2 Docs Style Orientation (`website/versioned_docs/version-7.11.2/docs/style-orientation.mdx`)
id: style-orientation title: Orientation sidebar_label: Orientation
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
Locking orientation
Orientation can be locked either in the layout level, or across the app via default options. You can declare orientations you'd like your app to support in default options.
<Tabs
defaultValue="defaultOptions"
values={[
{ label: 'Locking orientation in default options', value: 'defaultOptions', },
{ label: 'Locking root layout orientation',