diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 2618dc1431f..123190557ce 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,3 +1,5 @@ + diff --git a/.github/workflows/autofix.yml b/.github/workflows/autofix.yml index e890855f305..d8e6daa5fa2 100644 --- a/.github/workflows/autofix.yml +++ b/.github/workflows/autofix.yml @@ -1,9 +1,6 @@ name: autofix.ci on: - push: - branches: - - main pull_request: branches: - main @@ -28,4 +25,4 @@ jobs: run: yarn prettier --write . - name: Autofix - uses: autofix-ci/action@8106fde54b877517c9af2c3d68918ddeaa7bed64 + uses: autofix-ci/action@635ffb0c9798bd160680f18fd73371e355b85f27 diff --git a/blog/2025-01-29-using-react-navigation-with-native-bottom-tabs.md b/blog/2025-01-29-using-react-navigation-with-native-bottom-tabs.md index f21cfbff58d..070c85e91ca 100644 --- a/blog/2025-01-29-using-react-navigation-with-native-bottom-tabs.md +++ b/blog/2025-01-29-using-react-navigation-with-native-bottom-tabs.md @@ -1,13 +1,15 @@ --- title: Bottom Tabs meet Native authors: oskar -tags: [tutorial, react-native-paper] +tags: [tutorial] --- This is a guest post by Oskar Kwaśniewski, creator of `react-native-bottom-tabs`, a library exposing native tab primitives that integrates with React Navigation. If you like this guide, check out the `react-native-bottom-tabs` [documentation](https://callstackincubator.github.io/react-native-bottom-tabs/) for more! This blog post will explain the differences between the JavaScript Bottom Tabs navigator and provide a step-by-step guide for integrating React Navigation with the Native Bottom Tabs Navigator. + + ## Introduction React Navigation comes with many navigators out of the box. We've got Stack, Native Stack, Drawer, and Bottom Tabs, but there were no Native Bottom Tabs until today! diff --git a/package.json b/package.json index 4989ead8672..8c4108eb882 100755 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "deploy": "DEPLOYMENT_BRANCH=gh-pages docusaurus deploy", "crowdin-upload": "crowdin upload sources --auto-update -b main", "crowdin-download": "crowdin download -b main", - "fetch-sponsors": "node scripts/fetch-sponsors.js" + "fetch-sponsors": "node scripts/fetch-sponsors.js && prettier --write src/data/sponsors.js" }, "dependencies": { "@docusaurus/core": "3.6.1", diff --git a/src/css/custom.css b/src/css/custom.css index 19d5ba9ba0e..33d5487453c 100755 --- a/src/css/custom.css +++ b/src/css/custom.css @@ -81,8 +81,12 @@ --ifm-footer-padding-horizontal: var(--ifm-spacing-horizontal); --ifm-footer-padding-vertical: var(--ifm-spacing-vertical); - --ifm-tabs-padding-vertical: 0.5rem; + --ifm-tabs-padding-vertical: 0.375rem; + + --ifm-alert-shadow: none; --ifm-alert-border-left-width: 0; + + --codeblock-background-color: #f6f8fa; } :root[data-theme='dark'] { @@ -133,6 +137,8 @@ --ifm-home-color-border: #f7f7ff; --docusaurus-highlighted-code-line-bg: rgba(255, 255, 255, 0.07); + + --codeblock-background-color: #282a35; } h1 { @@ -388,7 +394,7 @@ p { } .tabs__item { - border-bottom: 1px solid var(--ifm-toc-border-color); + border-bottom: 2px solid var(--ifm-toc-border-color); border-bottom-left-radius: 0; border-bottom-right-radius: 0; } @@ -396,7 +402,29 @@ p { .tabs__item--active, .tabs__item--active:hover { border-bottom-color: var(--ifm-tabs-color-active-border); - background-color: var(--ifm-menu-color-background-active); +} + +.tabs-container:has( + > .margin-top--md > [role='tabpanel'] > .theme-code-block:only-child + ):not(:has(> .margin-top--md > [role='tabpanel'] > :nth-child(2))) { + background-color: var(--codeblock-background-color); + border-radius: var(--ifm-code-border-radius); + + & > .margin-top--md { + margin-top: 0 !important; + } + + & > .tabs { + box-shadow: inset 0 -2px 0 var(--ifm-toc-border-color); + } + + & > .tabs > .tabs__item { + border-top-right-radius: 0; + } + + & > .tabs > .tabs__item:not(:first-child) { + border-top-left-radius: 0; + } } .col:has(.table-of-contents) { @@ -475,6 +503,10 @@ samp { display: none; } +.theme-code-block { + box-shadow: none !important; +} + .theme-code-block:has(+ .snack-sample-link) { margin-bottom: 0; border-bottom-left-radius: 0; @@ -488,17 +520,15 @@ samp { margin-top: 0; margin-bottom: var(--ifm-leading); padding: calc(var(--ifm-pre-padding) / 2) var(--ifm-pre-padding); - background-color: #f6f8fa; + background-color: var(--codeblock-background-color); border-top-width: 1px; border-top-style: solid; border-top-color: var(--ifm-color-gray-200); border-bottom-left-radius: var(--ifm-pre-border-radius); border-bottom-right-radius: var(--ifm-pre-border-radius); - box-shadow: var(--ifm-global-shadow-lw); } [data-theme='dark'] .theme-code-block + .snack-sample-link { - background-color: #282a35; border-top-color: rgba(255, 255, 255, 0.07); } diff --git a/src/data/sponsors.js b/src/data/sponsors.js index ece5c987929..84d0f91f24a 100644 --- a/src/data/sponsors.js +++ b/src/data/sponsors.js @@ -1,37 +1,9 @@ export default [ { avatarUrl: - 'https://avatars.githubusercontent.com/u/9664363?u=a4a9e93dc4305c91ced38b83d4c08186f7254b04&v=4', - username: 'EvanBacon', - name: 'Evan Bacon', - }, - { - avatarUrl: 'https://avatars.githubusercontent.com/u/306134?v=4', - username: 'wcandillon', - name: 'William Candillon', - }, - { - avatarUrl: 'https://avatars.githubusercontent.com/u/476779?v=4', - username: 'Expensify', - name: 'Expensify, Inc', - }, - { - avatarUrl: - 'https://avatars.githubusercontent.com/u/916690?u=66482eb2c5bb755553afbcfa219dcacc42fc487a&v=4', - username: 'benevbright', - name: 'Bright Lee', - }, - { - avatarUrl: - 'https://avatars.githubusercontent.com/u/980234?u=59bb4c6ac0a23b53225bb2235b69c72a960ba83f&v=4', - username: 'simoncar', - name: 'Simoncar', - }, - { - avatarUrl: - 'https://avatars.githubusercontent.com/u/1057756?u=15c3cdff1c715ac27bbc63ccb8f0a1c27eeb3784&v=4', - username: 'zhigang1992', - name: 'Zhigang Fang', + 'https://avatars.githubusercontent.com/u/360412?u=15e7b90eb91a3d2b410f7f47461862cb793398ff&v=4', + username: 'jyc', + name: null, }, { avatarUrl: @@ -41,27 +13,14 @@ export default [ }, { avatarUrl: - 'https://avatars.githubusercontent.com/u/1566403?u=3df07e2ae72e89a3a3509ba6c2f927115b5f38aa&v=4', - username: 'vonovak', - name: 'Vojtech Novak', - }, - { - avatarUrl: - 'https://avatars.githubusercontent.com/u/1629785?u=12eb94da6070d00fc924761ce06e3a428d01b7e9&v=4', + 'https://avatars.githubusercontent.com/u/1629785?u=f91613c118bb1fcf442a71008dff1cd5f9b30411&v=4', username: 'JonnyBurger', name: 'Jonny Burger', }, { - avatarUrl: - 'https://avatars.githubusercontent.com/u/1764217?u=f36737d852ffcc50b87e809474faa27eb2ce130a&v=4', - username: 'markholland', - name: 'Mark Holland', - }, - { - avatarUrl: - 'https://avatars.githubusercontent.com/u/3584560?u=f54bd481e956c6b3fe88a15f466ff9a3973e4b35&v=4', - username: 'hetmann', - name: 'Hetmann W. Iohan', + avatarUrl: 'https://avatars.githubusercontent.com/u/2443340?v=4', + username: 'toyokumo', + name: 'TOYOKUMO', }, { avatarUrl: @@ -69,29 +28,6 @@ export default [ username: 'itsrifat', name: 'Moinul Hossain', }, - { - avatarUrl: - 'https://avatars.githubusercontent.com/u/4376835?u=0cf5324a78dd4140ef71943048dedac328be68b9&v=4', - username: 'lnmunhoz', - name: 'Lucas N. Munhoz', - }, - { - avatarUrl: 'https://avatars.githubusercontent.com/u/5605177?v=4', - username: 'Razorholt', - name: null, - }, - { - avatarUrl: - 'https://avatars.githubusercontent.com/u/5967956?u=f7f5ed6b6b399c2953fd0e3be0512c378e9f76c4&v=4', - username: 'codinsonn', - name: 'Thorr Stevens', - }, - { - avatarUrl: - 'https://avatars.githubusercontent.com/u/6457344?u=47e100289441b7f4681a7809202ff683886e4f5e&v=4', - username: 'ryo-rm', - name: 'ryo kishida', - }, { avatarUrl: 'https://avatars.githubusercontent.com/u/6936373?u=4edd14e6636c45d10ac6a3eecb4b3ffa6cc2bf5c&v=4', @@ -105,89 +41,38 @@ export default [ name: 'Radek Czemerys', }, { - avatarUrl: 'https://avatars.githubusercontent.com/u/12504344?v=4', - username: 'expo', - name: 'Expo', - }, - { - avatarUrl: 'https://avatars.githubusercontent.com/u/13601619?v=4', - username: 'itiden', - name: 'Itiden', + avatarUrl: + 'https://avatars.githubusercontent.com/u/7910545?u=95ae2c2a40b5f6f63f05fce29ee7c01622019c76&v=4', + username: 'UdaySravanK', + name: 'Uday Sravan K', }, { avatarUrl: - 'https://avatars.githubusercontent.com/u/14099522?u=965e74751a9db0f40a30ace9cd0cb1f82eaf1412&v=4', - username: 'aCanalez', - name: 'Alexi Canales', + 'https://avatars.githubusercontent.com/u/9664363?u=46ba6d5fbd29729df2950b845c9ca2cd085a1c2b&v=4', + username: 'EvanBacon', + name: 'Evan Bacon', }, { avatarUrl: - 'https://avatars.githubusercontent.com/u/15199031?u=46da50e88594eb284cf249485f202d5d43d474d1&v=4', + 'https://avatars.githubusercontent.com/u/15199031?u=5a82dcb32237282ff576c0446567a1e2fe49b868&v=4', username: 'mrousavy', name: 'Marc Rousavy', }, { avatarUrl: - 'https://avatars.githubusercontent.com/u/17621507?u=0ee7f26191d430f4fc0672cef92c2759d948bbb5&v=4', - username: 'dsznajder', - name: 'Damian Sznajder', - }, - { - avatarUrl: - 'https://avatars.githubusercontent.com/u/21980965?u=5a571092a83cb71508c60a9c86ab2520fde8a68e&v=4', - username: 'jarvisluong', - name: 'Jarvis Luong', - }, - { - avatarUrl: - 'https://avatars.githubusercontent.com/u/26326015?u=33a1afbe11e8f6b962c6267606d59c2b2ef94716&v=4', - username: 'bang9', - name: 'Hyungu Kang | Airen', - }, - { - avatarUrl: - 'https://avatars.githubusercontent.com/u/27461460?u=b5860875e26d33fd70fd210f4ea74f81cdf9d99b&v=4', - username: 'hyochan', - name: 'Hyo', - }, - { - avatarUrl: 'https://avatars.githubusercontent.com/u/30735054?v=4', - username: 'endearhq', - name: 'Endear', - }, - { - avatarUrl: - 'https://avatars.githubusercontent.com/u/33361399?u=6180514361e35ae42e2401431555c82cc63adda9&v=4', - username: 'oliverloops', - name: 'Oliver Lopez ', - }, - { - avatarUrl: - 'https://avatars.githubusercontent.com/u/34658847?u=17fd3603d012d068c060039bcf009095055290a1&v=4', - username: 'RatebSeirawan', - name: 'Rateb Seirawan', - }, - { - avatarUrl: - 'https://avatars.githubusercontent.com/u/36824170?u=6f56fa2998ffba6b5a3908c79e2ef9331bad502a&v=4', - username: 'luism3861', - name: 'Luis Medina Huerta', - }, - { - avatarUrl: - 'https://avatars.githubusercontent.com/u/45317893?u=253e6eabcb23a5361e93a064914c9b8d136ac888&v=4', - username: 'GandresCoello18', - name: 'Andres Coello', + 'https://avatars.githubusercontent.com/u/23122214?u=7b3231a177e5d661d55b2cee88ea69e1713eb695&v=4', + username: 'sayurimizuguchi', + name: 'Sayuri Mizuguchi', }, { avatarUrl: - 'https://avatars.githubusercontent.com/u/46625943?u=63c9ed9017c34900df8b5ae2ed455ec4c82ef8aa&v=4', - username: 'bowen9284', - name: 'Matt Bowen', + 'https://avatars.githubusercontent.com/u/79333934?u=d18e4c6a4e063534d6fd7e77e0d51c367c91cfa0&v=4', + username: 'finanzguru', + name: 'Finanzguru', }, { - avatarUrl: 'https://avatars.githubusercontent.com/u/49920282?v=4', - username: 'reactrondev', - name: 'Reactron', + avatarUrl: 'https://avatars.githubusercontent.com/u/140319444?v=4', + username: 'jupli-apps', + name: 'Jupli', }, ]; diff --git a/src/pages/home/Sponsors/index.js b/src/pages/home/Sponsors/index.js index 40c3bff4d8d..4bac27a7f4d 100644 --- a/src/pages/home/Sponsors/index.js +++ b/src/pages/home/Sponsors/index.js @@ -1,5 +1,3 @@ -import React from 'react'; - import sponsors from '../../../data/sponsors'; import styles from './styles.module.css'; @@ -8,15 +6,14 @@ export default function Sponsors() {

- React Navigation is built by Expo,{' '} - Software Mansion, and{' '} - Callstack, with contributions - from the{' '} + React Navigation relies on the support from the community. Thanks to{' '} + Software Mansion,{' '} + Callstack,{' '} + Expo, and our amazing{' '} - community + contributors {' '} - and{' '} - sponsors: + & sponsors:

{sponsors.map((sponsor) => ( diff --git a/static/assets/7.x/native-bottom-tabs-android.mp4 b/static/assets/7.x/native-bottom-tabs-android.mp4 new file mode 100644 index 00000000000..4c65cf2105c Binary files /dev/null and b/static/assets/7.x/native-bottom-tabs-android.mp4 differ diff --git a/static/assets/7.x/native-bottom-tabs-ios-minimize.mp4 b/static/assets/7.x/native-bottom-tabs-ios-minimize.mp4 new file mode 100644 index 00000000000..2460652d08b Binary files /dev/null and b/static/assets/7.x/native-bottom-tabs-ios-minimize.mp4 differ diff --git a/static/assets/7.x/native-bottom-tabs-ios-search.mp4 b/static/assets/7.x/native-bottom-tabs-ios-search.mp4 new file mode 100644 index 00000000000..e3892bde2f3 Binary files /dev/null and b/static/assets/7.x/native-bottom-tabs-ios-search.mp4 differ diff --git a/static/assets/7.x/native-bottom-tabs-ios.mp4 b/static/assets/7.x/native-bottom-tabs-ios.mp4 new file mode 100644 index 00000000000..29f928e9779 Binary files /dev/null and b/static/assets/7.x/native-bottom-tabs-ios.mp4 differ diff --git a/static/assets/deep-linking/xcode-linking.png b/static/assets/deep-linking/xcode-linking.png old mode 100755 new mode 100644 index 9f5170bf9a0..2cea12f4854 Binary files a/static/assets/deep-linking/xcode-linking.png and b/static/assets/deep-linking/xcode-linking.png differ diff --git a/static/assets/header-items/header-items-menu.png b/static/assets/header-items/header-items-menu.png new file mode 100644 index 00000000000..c900fdb84f7 Binary files /dev/null and b/static/assets/header-items/header-items-menu.png differ diff --git a/static/assets/header-items/header-items.png b/static/assets/header-items/header-items.png new file mode 100644 index 00000000000..cb7430b9128 Binary files /dev/null and b/static/assets/header-items/header-items.png differ diff --git a/versioned_docs/version-4.x/contributing.md b/versioned_docs/version-4.x/contributing.md index faf347fc790..5472a1c0acd 100644 --- a/versioned_docs/version-4.x/contributing.md +++ b/versioned_docs/version-4.x/contributing.md @@ -32,7 +32,7 @@ And here are a few helpful resources to aid in getting started: You can't write code without writing the occasional bug. Especially as React Navigation is moving quickly, bugs happen. When you think you've found one here's what to do: 1. Search the existing issues for one like what you're seeing. If you see one, add a 👍 reaction (please no +1 comments). Read through the comments and see if you can provide any more valuable information to the thread -2. If there are no other issues like yours then create a new one. Be sure to follow the [issue template](https://github.com/react-navigation/react-navigation/blob/4.x/.github/ISSUE_TEMPLATE.md). +2. If there are no other issues like yours then create a new one. Be sure to follow the [issue template](https://github.com/react-navigation/react-navigation/blob/v4.1.1/.github/ISSUE_TEMPLATE/bug_report.md). Creating a high quality reproduction is critical. Without it we likely can't fix the bug and, in an ideal situation, you'll find out that it's not actually a bug of the library but simply done incorrectly in your project. Instant bug fix! @@ -93,7 +93,7 @@ The libdef (located at `flow/react-navigation.js`) will need to be updated such ### Issue Template -Before submitting an issue, please take a look at the [issue template](https://github.com/react-navigation/react-navigation/blob/4.x/.github/ISSUE_TEMPLATE.md) and follow it. This is in place to help everyone better understand the issue you're having and reduce the back and forth to get the necessary information. +Before submitting an issue, please take a look at the [issue template](https://github.com/react-navigation/react-navigation/blob/v4.1.1/.github/ISSUE_TEMPLATE/bug_report.md) and follow it. This is in place to help everyone better understand the issue you're having and reduce the back and forth to get the necessary information. Yes, it takes time and effort to complete the issue template. But that's the only way to ask high quality questions that actually get responses. @@ -101,7 +101,7 @@ Would you rather take 1 minute to create an incomplete issue report and wait mon ### Pull Request Template -Much like the issue template, the [pull request template](https://github.com/react-navigation/react-navigation/blob/4.x/.github/PULL_REQUEST_TEMPLATE.md) lays out instructions to ensure your pull request gets reviewed in a timely manner and reduces the back and forth. Make sure to look it over before you start writing any code. +Much like the issue template, the [pull request template](https://github.com/react-navigation/react-navigation/blob/v4.1.1/.github/PULL_REQUEST_TEMPLATE.md) lays out instructions to ensure your pull request gets reviewed in a timely manner and reduces the back and forth. Make sure to look it over before you start writing any code. ### Forking the Repository diff --git a/versioned_docs/version-5.x/contributing.md b/versioned_docs/version-5.x/contributing.md index aa31a3cb0b6..f90dd750538 100755 --- a/versioned_docs/version-5.x/contributing.md +++ b/versioned_docs/version-5.x/contributing.md @@ -15,23 +15,9 @@ Here are some of the ways to contribute to the project: - [Bug Fixes](#bug-fixes) - [Suggesting a Feature](#suggesting-a-feature) - [Big Pull Requests](#big-pull-requests) -- [Information](#information) - - [Issue Template](#issue-template) - - [Pull Request Template](#pull-request-template) - - [Forking the Repository](#forking-the-repository) - - [Code Review Guidelines](#code-review-guidelines) - - [Run the Example App](#run-the-example-app) - - [Run Tests](#run-tests) And here are a few helpful resources to aid in getting started: -- [Contributing](#contributing) - - [Reporting Bugs](#reporting-bugs) - - [Improving the Documentation](#improving-the-documentation) - - [Responding to Issues](#responding-to-issues) - - [Bug Fixes](#bug-fixes) - - [Suggesting a Feature](#suggesting-a-feature) - - [Big Pull Requests](#big-pull-requests) - [Information](#information) - [Issue Template](#issue-template) - [Pull Request Template](#pull-request-template) @@ -47,7 +33,7 @@ And here are a few helpful resources to aid in getting started: You can't write code without writing the occasional bug. Especially as React Navigation is moving quickly, bugs happen. When you think you've found one here's what to do: 1. Search the existing issues for one like what you're seeing. If you see one, add a 👍 reaction (please no +1 comments). Read through the comments and see if you can provide any more valuable information to the thread -2. If there are no other issues like yours then create a new one. Be sure to follow the [issue template](https://github.com/react-navigation/react-navigation/blob/main/.github/ISSUE_TEMPLATE.md). +2. If there are no other issues like yours then create a new one. Be sure to follow the [issue template](https://github.com/react-navigation/react-navigation/blob/%40react-navigation/core%405.14.4/.github/ISSUE_TEMPLATE/bug-report.md). Creating a high quality reproduction is critical. Without it we likely can't fix the bug and, in an ideal situation, you'll find out that it's not actually a bug of the library but simply done incorrectly in your project. Instant bug fix! @@ -96,7 +82,7 @@ The reason we want to do this is to save everyone time. Maybe that feature alrea ### Issue Template -Before submitting an issue, please take a look at the [issue template](https://github.com/react-navigation/react-navigation/blob/main/.github/ISSUE_TEMPLATE.md) and follow it. This is in place to help everyone better understand the issue you're having and reduce the back and forth to get the necessary information. +Before submitting an issue, please take a look at the [issue template](https://github.com/react-navigation/react-navigation/blob/%40react-navigation/core%405.14.4/.github/ISSUE_TEMPLATE/bug-report.md) and follow it. This is in place to help everyone better understand the issue you're having and reduce the back and forth to get the necessary information. Yes, it takes time and effort to complete the issue template. But that's the only way to ask high quality questions that actually get responses. @@ -104,7 +90,7 @@ Would you rather take 1 minute to create an incomplete issue report and wait mon ### Pull Request Template -Much like the issue template, the [pull request template](https://github.com/react-navigation/react-navigation/blob/main/.github/PULL_REQUEST.md) lays out instructions to ensure your pull request gets reviewed in a timely manner and reduces the back and forth. Make sure to look it over before you start writing any code. +Much like the issue template, the [pull request template](https://github.com/react-navigation/react-navigation/blob/%40react-navigation/core%405.14.4/.github/PULL_REQUEST.md) lays out instructions to ensure your pull request gets reviewed in a timely manner and reduces the back and forth. Make sure to look it over before you start writing any code. ### Forking the Repository diff --git a/versioned_docs/version-6.x/contributing.md b/versioned_docs/version-6.x/contributing.md index aa31a3cb0b6..461dde2dcc7 100755 --- a/versioned_docs/version-6.x/contributing.md +++ b/versioned_docs/version-6.x/contributing.md @@ -15,23 +15,9 @@ Here are some of the ways to contribute to the project: - [Bug Fixes](#bug-fixes) - [Suggesting a Feature](#suggesting-a-feature) - [Big Pull Requests](#big-pull-requests) -- [Information](#information) - - [Issue Template](#issue-template) - - [Pull Request Template](#pull-request-template) - - [Forking the Repository](#forking-the-repository) - - [Code Review Guidelines](#code-review-guidelines) - - [Run the Example App](#run-the-example-app) - - [Run Tests](#run-tests) And here are a few helpful resources to aid in getting started: -- [Contributing](#contributing) - - [Reporting Bugs](#reporting-bugs) - - [Improving the Documentation](#improving-the-documentation) - - [Responding to Issues](#responding-to-issues) - - [Bug Fixes](#bug-fixes) - - [Suggesting a Feature](#suggesting-a-feature) - - [Big Pull Requests](#big-pull-requests) - [Information](#information) - [Issue Template](#issue-template) - [Pull Request Template](#pull-request-template) @@ -47,7 +33,7 @@ And here are a few helpful resources to aid in getting started: You can't write code without writing the occasional bug. Especially as React Navigation is moving quickly, bugs happen. When you think you've found one here's what to do: 1. Search the existing issues for one like what you're seeing. If you see one, add a 👍 reaction (please no +1 comments). Read through the comments and see if you can provide any more valuable information to the thread -2. If there are no other issues like yours then create a new one. Be sure to follow the [issue template](https://github.com/react-navigation/react-navigation/blob/main/.github/ISSUE_TEMPLATE.md). +2. If there are no other issues like yours then create a new one. Be sure to follow the [issue template](https://github.com/react-navigation/react-navigation/blob/6.x/.github/ISSUE_TEMPLATE/bug-report.yml). Creating a high quality reproduction is critical. Without it we likely can't fix the bug and, in an ideal situation, you'll find out that it's not actually a bug of the library but simply done incorrectly in your project. Instant bug fix! @@ -96,7 +82,7 @@ The reason we want to do this is to save everyone time. Maybe that feature alrea ### Issue Template -Before submitting an issue, please take a look at the [issue template](https://github.com/react-navigation/react-navigation/blob/main/.github/ISSUE_TEMPLATE.md) and follow it. This is in place to help everyone better understand the issue you're having and reduce the back and forth to get the necessary information. +Before submitting an issue, please take a look at the [issue template](https://github.com/react-navigation/react-navigation/blob/6.x/.github/ISSUE_TEMPLATE/bug-report.yml) and follow it. This is in place to help everyone better understand the issue you're having and reduce the back and forth to get the necessary information. Yes, it takes time and effort to complete the issue template. But that's the only way to ask high quality questions that actually get responses. @@ -104,7 +90,7 @@ Would you rather take 1 minute to create an incomplete issue report and wait mon ### Pull Request Template -Much like the issue template, the [pull request template](https://github.com/react-navigation/react-navigation/blob/main/.github/PULL_REQUEST.md) lays out instructions to ensure your pull request gets reviewed in a timely manner and reduces the back and forth. Make sure to look it over before you start writing any code. +Much like the issue template, the [pull request template](https://github.com/react-navigation/react-navigation/blob/6.x/.github/PULL_REQUEST_TEMPLATE.md) lays out instructions to ensure your pull request gets reviewed in a timely manner and reduces the back and forth. Make sure to look it over before you start writing any code. ### Forking the Repository diff --git a/versioned_docs/version-6.x/native-stack-navigator.md b/versioned_docs/version-6.x/native-stack-navigator.md index 3aa0c89c91b..4b286b0ac7c 100755 --- a/versioned_docs/version-6.x/native-stack-navigator.md +++ b/versioned_docs/version-6.x/native-stack-navigator.md @@ -457,15 +457,17 @@ Only supported on Android and iOS. #### `statusBarStyle` -Sets the status bar color (similar to the `StatusBar` component). Defaults to `auto`. +Sets the status bar color (similar to the `StatusBar` component). Supported values: -- `"auto"` +- `"auto"` (iOS only) - `"inverted"` (iOS only) - `"dark"` - `"light"` +Defaults to `auto` on iOS and `light` on Android. + Requires setting `View controller-based status bar appearance -> YES` (or removing the config) in your `Info.plist` file. Only supported on Android and iOS. diff --git a/versioned_docs/version-6.x/typescript.md b/versioned_docs/version-6.x/typescript.md index 75bbdc3b39f..bad3a42959d 100755 --- a/versioned_docs/version-6.x/typescript.md +++ b/versioned_docs/version-6.x/typescript.md @@ -171,7 +171,9 @@ type TabParamList = { #### Combining navigation props -When you nest navigators, the navigation prop of the screen is a combination of multiple navigation props. For example, if we have a tab inside a stack, the `navigation` prop will have both `jumpTo` (from the tab navigator) and `push` (from the stack navigator). To make it easier to combine types from multiple navigators, you can use the `CompositeScreenProps` type: +When you nest navigators, the navigation prop of the screen is a combination of multiple navigation props. For example, if we have a tab inside a stack, the `navigation` prop will have both [`jumpTo`](tab-actions.md#jumpto) (from the tab navigator) and [`push`](stack-actions.md#push) (from the stack navigator). To make it easier to combine types from multiple navigators, you can use the `CompositeScreenProps` type. + +For example, if we have a `Profile` in a navigator, nested inside `Account` screen of a stack navigator, we can combine the types as follows: ```ts import type { CompositeScreenProps } from '@react-navigation/native'; @@ -180,20 +182,23 @@ import type { StackScreenProps } from '@react-navigation/stack'; type ProfileScreenProps = CompositeScreenProps< BottomTabScreenProps, - StackScreenProps + StackScreenProps >; ``` -The `CompositeScreenProps` type takes 2 parameters, first parameter is the type of props for the primary navigation (type for the navigator that owns this screen, in our case the tab navigator which contains the `Profile` screen) and second parameter is the type of props for secondary navigation (type for a parent navigator). The primary type should always have the screen's route name as its second parameter. +The `CompositeScreenProps` type takes 2 parameters: + +- The first parameter is the type for the navigator that owns this screen, in our case the tab navigator which contains the `Profile` screen +- The second parameter is the type of props for a parent navigator, in our case the stack navigator which contains the `Account` screen -For multiple parent navigators, this secondary type should be nested: +For multiple parent navigators, this second parameter can nest another `CompositeScreenProps`: ```ts type ProfileScreenProps = CompositeScreenProps< BottomTabScreenProps, CompositeScreenProps< - StackScreenProps, - DrawerScreenProps + StackScreenProps, + DrawerScreenProps > >; ``` @@ -207,7 +212,7 @@ import type { StackNavigationProp } from '@react-navigation/stack'; type ProfileScreenNavigationProp = CompositeNavigationProp< BottomTabNavigationProp, - StackNavigationProp + StackNavigationProp >; ``` @@ -358,7 +363,7 @@ export type HomeTabParamList = { export type HomeTabScreenProps = CompositeScreenProps< BottomTabScreenProps, - RootStackScreenProps + RootStackScreenProps >; declare global { diff --git a/versioned_docs/version-7.x/auth-flow.md b/versioned_docs/version-7.x/auth-flow.md index c127c2ca638..c3d4e71772a 100755 --- a/versioned_docs/version-7.x/auth-flow.md +++ b/versioned_docs/version-7.x/auth-flow.md @@ -30,45 +30,12 @@ We want the following behavior from our authentication flow: ## How it will work -We can configure different screens to be available based on some condition. For example, if the user is signed in, we can define `Home`, `Profile`, `Settings` etc. If the user is not signed in, we can define `SignIn` and `SignUp` screens. +We can configure different screens to be available based on some condition. For example, if the user is signed in, we want `Home` to be available. If the user is not signed in, we want `SignIn` to be available. -To do this, we need a couple of things: - -1. Define two hooks: `useIsSignedIn` and `useIsSignedOut`, which return a boolean value indicating whether the user is signed in or not. -2. Use the `useIsSignedIn` and `useIsSignedOut` along with the [`if`](static-configuration.md#if) property to define the screens that are available based on the condition. - -This tells React Navigation to show specific screens based on the signed in status. When the signed in status changes, React Navigation will automatically show the appropriate screen. - -## Define the hooks - -To implement the `useIsSignedIn` and `useIsSignedOut` hooks, we can start by creating a context to store the authentication state. Let's call it `SignInContext`: - -```js -import * as React from 'react'; - -const SignInContext = React.createContext(); -``` - -Then we can implement the `useIsSignedIn` and `useIsSignedOut` hooks as follows: - -```js -function useIsSignedIn() { - const isSignedIn = React.useContext(SignInContext); - return isSignedIn; -} - -function useIsSignedOut() { - const isSignedIn = React.useContext(SignInContext); - return !isSignedIn; -} -``` - -We'll discuss how to expose the context value later. - -```js name="Customizing tabs appearance" snack +```js name="Authentication flow" snack import * as React from 'react'; import { View } from 'react-native'; import { createStaticNavigation } from '@react-navigation/native'; @@ -79,40 +46,19 @@ const useIsSignedIn = () => { }; const useIsSignedOut = () => { - return false; + return !useIsSignedIn(); }; -const signedInStack = createNativeStackNavigator({ - screens: { - Home: HomeScreen, - Profile: ProfileScreen, - Settings: SettingsScreen, - }, -}); - -const signedOutStack = createNativeStackNavigator({ - screens: { - SignIn: SignInScreen, - SignUp: SignUpScreen, - }, -}); - // codeblock-focus-start const RootStack = createNativeStackNavigator({ screens: { - LoggedIn: { + Home: { if: useIsSignedIn, - screen: signedInStack, - options: { - headerShown: false, - }, + screen: HomeScreen, }, - LoggedOut: { + SignIn: { if: useIsSignedOut, - screen: signedOutStack, - options: { - headerShown: false, - }, + screen: SignInScreen, }, }, }); @@ -128,29 +74,59 @@ function HomeScreen() { return ; } -function ProfileScreen() { +function SignInScreen() { return ; } +``` -function SettingsScreen() { - return ; -} +Here, for each screen, we have defined a condition using the `if` property which takes a hook. The hook returns a boolean value indicating whether the user is signed in or not. If the hook returns `true`, the screen will be available, otherwise it won't. -function SignInScreen() { - return ; +This means: + +- When `useIsSignedIn` returns `true`, React Navigation will only use the `Home` screen, since it's the only screen matching the condition. +- Similarly, when `useIsSignedOut` returns `true`, React Navigation will use the `SignIn` screen. + +This makes it impossible to navigate to the `Home` when the user is not signed in, and to `SignIn` when the user is signed in. + +When the values returned by `useIsSignedin` and `useIsSignedOut` change, the screens matching the condition will change: + +- Let's say, initially `useIsSignedOut` returns `true`. This means that `SignIn` screens is shown. +- After the user signs in, the return value of `useIsSignedIn` will change to `true` and `useIsSignedOut` will change to `false`, which means: + - React Navigation will see that the `SignIn` screen is no longer matches the condition, so it will remove the screen. + - Then it'll show the `Home` screen automatically because that's the first screen available when `useIsSignedIn` returns `true`. + +The order of the screens matters when there are multiple screens matching the condition. For example, if there are two screens matching `useIsSignedIn`, the first screen will be shown when the condition is `true`. + +## Define the hooks + +To implement the `useIsSignedIn` and `useIsSignedOut` hooks, we can start by creating a context to store the authentication state. Let's call it `SignInContext`: + +```js +import * as React from 'react'; + +const SignInContext = React.createContext(); +``` + +Then we can implement the `useIsSignedIn` and `useIsSignedOut` hooks as follows: + +```js +function useIsSignedIn() { + const isSignedIn = React.useContext(SignInContext); + return isSignedIn; } -function SignUpScreen() { - return ; +function useIsSignedOut() { + return !useIsSignedIn(); } ``` +We'll discuss how to provide the context value later. + - -For example: + -```js name="Customizing tabs appearance" snack +```js name="Authentication flow" snack import * as React from 'react'; import { View } from 'react-native'; import { NavigationContainer } from '@react-navigation/native'; @@ -158,29 +134,17 @@ import { createNativeStackNavigator } from '@react-navigation/native-stack'; const Stack = createNativeStackNavigator(); -const getIsSignedIn = () => { - // custom logic - return true; -}; - export default function App() { - const isSignedIn = getIsSignedIn(); + const isSignedIn = true; return ( // codeblock-focus-start {isSignedIn ? ( - <> - - - - + ) : ( - <> - - - + )} // codeblock-focus-end @@ -192,39 +156,35 @@ function HomeScreen() { return ; } -function ProfileScreen() { - return ; -} - -function SettingsScreen() { - return ; -} - function SignInScreen() { return ; } - -function SignUpScreen() { - return ; -} ``` -When we define screens like this, when `isSignedIn` is `true`, React Navigation will only see the `Home`, `Profile` and `Settings` screens, and when it's `false`, React Navigation will see the `SignIn` and `SignUp` screens. This makes it impossible to navigate to the `Home`, `Profile` and `Settings` screens when the user is not signed in, and to `SignIn` and `SignUp` screens when the user is signed in. +Here, we have conditionally defined the screens based on the value of `isSignedIn`. -This pattern has been in use by other routing libraries such as React Router for a long time, and is commonly known as "Protected routes". Here, our screens which need the user to be signed in are "protected" and cannot be navigated to by other means if the user is not signed in. +This means: -The magic happens when the value of the `isSignedIn` variable changes. Let's say, initially `isSignedIn` is `false`. This means, either `SignIn` or `SignUp` screens are shown. After the user signs in, the value of `isSignedIn` will change to `true`. React Navigation will see that the `SignIn` and `SignUp` screens are no longer defined and so it will remove them. Then it'll show the `Home` screen automatically because that's the first screen defined when `isSignedIn` is `true`. +- When `isSignedIn` is `true`, React Navigation will only see the `Home` screen, since it's the only screen defined based on the condition. +- Similarly, when `isSignedIn` is `false`, React Navigation will only see the `SignIn` screen. -The example shows stack navigator, but you can use the same approach with any navigator. +This makes it impossible to navigate to the `Home` when the user is not signed in, and to `SignIn` when the user is signed in. -By conditionally defining different screens based on a variable, we can implement auth flow in a simple way that doesn't require additional logic to make sure that the correct screen is shown. +When the value of `isSignedin` changes, the screens defined based on the condition will change: + +- Let's say, initially `isSignedin` is `false`. This means that `SignIn` screens is shown. +- After the user signs in, the value of `isSignedin` will change to `true`, which means: + - React Navigation will see that the `SignIn` screen is no longer defined, so it will remove the screen. + - Then it'll show the `Home` screen automatically because that's the first screen defined when `isSignedin` returns `true`. + +The order of the screens matters when there are multiple screens matching the condition. For example, if there are two screens defined based on `isSignedin`, the first screen will be shown when the condition is `true`. -## Define our screens +## Add more screens -In our navigator, we can conditionally define appropriate screens. For our case, let's say we have 3 screens: +For our case, let's say we have 3 screens: - `SplashScreen` - This will show a splash or loading screen when we're restoring the token. - `SignIn` - This is the screen we show if the user isn't signed in already (we couldn't find a token). @@ -255,10 +215,46 @@ const RootStack = createNativeStackNavigator({ const Navigation = createStaticNavigation(RootStack); ``` + + + +```js +const Stack = createNativeStackNavigator(); + +export default function App() { + const isSignedIn = true; + + return ( + + + {isSignedIn ? ( + + ) : ( + + )} + + + ); +} +``` + + + + Notice how we have only defined the `Home` and `SignIn` screens here, and not the `SplashScreen`. The `SplashScreen` should be rendered before we render any navigators so that we don't render incorrect screens before we know whether the user is signed in or not. When we use this in our component, it'd look something like this: + + + ```js if (isLoading) { // We haven't finished checking for the token yet @@ -274,43 +270,6 @@ return ( ); ``` -In the above snippet, `isLoading` means that we're still checking if we have a token. This can usually be done by checking if we have a token in `SecureStore` and validating the token. After we get the token and if it's valid, we need to set the `userToken`. We also have another state called `isSignout` to have a different animation on sign out. - -Next, we're exposing the sign in status via the `SignInContext` so that it's available to the `useIsSignedIn` and `useIsSignedOut` hooks. - -In the above example, we have one screen for each case. But you could also define multiple screens. For example, you probably want to define password reset, signup, etc screens as well when the user isn't signed in. Similarly for the screens accessible after sign in, you probably have more than one screen. We can use [`groups`](static-configuration.md#groups) to define multiple screens: - -```js -const RootStack = createNativeStackNavigator({ - screens: { - // Common screens - }, - groups: { - SignedIn: { - if: useIsSignedIn, - screens: { - Home: HomeScreen, - Profile: ProfileScreen, - }, - }, - SignedOut: { - if: useIsSignedOut, - screens: { - SignIn: SignInScreen, - SignUp: SignUpScreen, - ResetPassword: ResetPasswordScreen, - }, - }, - }, -}); -``` - -:::tip - -If you have both your login-related screens and rest of the screens in Stack navigators, we recommend to use a single Stack navigator and place the conditional inside instead of using 2 different navigators. This makes it possible to have a proper transition animation during login/logout. - -::: - @@ -320,11 +279,12 @@ if (isLoading) { return ; } +const isSignedIn = userToken != null; + return ( - {userToken == null ? ( - // No token found, user isn't signed in + {isSignedIn ? ( ) : ( - // User is signed in )} @@ -345,51 +304,49 @@ return ( -In the above snippet, `isLoading` means that we're still checking if we have a token. This can usually be done by checking if we have a token in `SecureStore` and validating the token. After we get the token and if it's valid, we need to set the `userToken`. We also have another state called `isSignout` to have a different animation on sign out. +In the above snippet, `isLoading` means that we're still checking if we have a token. This can usually be done by checking if we have a token in `SecureStore` and validating the token. -The main thing to notice is that we're conditionally defining screens based on these state variables: - -- `SignIn` screen is only defined if `userToken` is `null` (user is not signed in) -- `Home` screen is only defined if `userToken` is non-null (user is signed in) +Next, we're exposing the sign in status via the `SignInContext` so that it's available to the `useIsSignedIn` and `useIsSignedOut` hooks. -Here, we're conditionally defining one screen for each case. But you could also define multiple screens. For example, you probably want to define password reset, signup, etc screens as well when the user isn't signed in. Similarly, for the screens accessible after signing in, you probably have more than one screen. We can use `React.Fragment` to define multiple screens: +In the above example, we have one screen for each case. But you could also define multiple screens. For example, you probably want to define password reset, signup, etc screens as well when the user isn't signed in. Similarly for the screens accessible after sign in, you probably have more than one screen. -```js -const SignInContext = React.createContext(); - -function useIsSignedIn() { - const isSignedIn = React.useContext(SignInContext); - return isSignedIn; -} - -function useIsSignedOut() { - const isSignedIn = React.useContext(SignInContext); - return !isSignedIn; -} - -/* content */ +We can use [`groups`](static-configuration.md#groups) to define multiple screens: -export default function App() { - /* content */ - - const isSignedIn = userToken != null; - - return ( - - - - ); -} +```js +const RootStack = createNativeStackNavigator({ + screens: { + // Common screens + }, + groups: { + SignedIn: { + if: useIsSignedIn, + screens: { + Home: HomeScreen, + Profile: ProfileScreen, + }, + }, + SignedOut: { + if: useIsSignedOut, + screens: { + SignIn: SignInScreen, + SignUp: SignUpScreen, + ResetPassword: ResetPasswordScreen, + }, + }, + }, +}); ``` +We can use [`React.Fragment`](https://react.dev/reference/react/Fragment) or [`Group`](group.md) to define multiple screens: + ```js -state.userToken == null ? ( +isSignedIn ? ( <> @@ -405,7 +362,7 @@ state.userToken == null ? ( :::tip -If you have both your login-related screens and rest of the screens in two different Stack navigators, we recommend to use a single Stack navigator and place the conditional inside instead of using 2 different navigators. This makes it possible to have a proper transition animation during login/logout. +Instead of having your login-related screens and rest of the screens in two different Stack navigators and render them conditionally, we recommend to use a single Stack navigator and place the conditional inside. This makes it possible to have a proper transition animation during login/logout. ::: @@ -422,8 +379,8 @@ The following is just an example of how you might implement the logic for authen From the previous snippet, we can see that we need 3 state variables: -- `isLoading` - We set this to `true` when we're trying to check if we already have a token saved in `SecureStore` -- `isSignout` - We set this to `true` when user is signing out, otherwise set it to `false` +- `isLoading` - We set this to `true` when we're trying to check if we already have a token saved in `SecureStore`. +- `isSignout` - We set this to `true` when user is signing out, otherwise set it to `false`. This can be used to customize the animation when signing out. - `userToken` - The token for the user. If it's non-null, we assume the user is logged in, otherwise not. So we need to: @@ -472,8 +429,7 @@ function useIsSignedIn() { } function useIsSignedOut() { - const isSignedIn = React.useContext(SignInContext); - return !isSignedIn; + return !useIsSignedIn(); } function SplashScreen() { @@ -619,11 +575,11 @@ const RootStack = createNativeStackNavigator({ screen: HomeScreen, }, SignIn: { + if: useIsSignedOut, screen: SignInScreen, options: { title: 'Sign in', }, - if: useIsSignedOut, }, }, }); @@ -975,6 +931,72 @@ If you have a bunch of shared screens, you can also use [`navigationKey` with a +The examples above show stack navigator, but you can use the same approach with any navigator. + +By specifying a condition for our screens, we can implement auth flow in a simple way that doesn't require additional logic to make sure that the correct screen is shown. + ## Don't manually navigate when conditionally rendering screens It's important to note that when using such a setup, you **don't manually navigate** to the `Home` screen by calling `navigation.navigate('Home')` or any other method. **React Navigation will automatically navigate to the correct screen** when `isSignedIn` changes - `Home` screen when `isSignedIn` becomes `true`, and to `SignIn` screen when `isSignedIn` becomes `false`. You'll get an error if you attempt to navigate manually. + +## Handling deep links after auth + +When using deep links, you may want to handle the case where the user opens a deep link that requires authentication. + +Example scenario: + +- User opens a deep link to `myapp://profile` but is not signed in. +- The app shows the `SignIn` screen. +- After the user signs in, you want to navigate them to the `Profile` screen. + +To achieve this, you can set [`UNSTABLE_routeNamesChangeBehavior`](navigator.md#route-names-change-behavior) to `"lastUnhandled"`: + +:::warning + +This API is experimental and may change in a minor release. + +::: + + + + +```js +const RootStack = createNativeStackNavigator({ + // highlight-next-line + UNSTABLE_routeNamesChangeBehavior: 'lastUnhandled', + screens: { + Home: { + if: useIsSignedIn, + screen: HomeScreen, + }, + SignIn: { + if: useIsSignedOut, + screen: SignInScreen, + options: { + title: 'Sign in', + }, + }, + }, +}); +``` + + + + +```js + + {isSignedIn ? ( + + ) : ( + + )} + +``` + + + + +The `UNSTABLE_routeNamesChangeBehavior` option allows you to control how React Navigation handles navigation when the available screens change because of conditions such as authentication state. When `lastUnhandled` is specified, React Navigation will remember the last screen that couldn't be handled, and after the condition changes, it'll automatically navigate to that screen if it's now available. diff --git a/versioned_docs/version-7.x/bottom-tab-navigator.md b/versioned_docs/version-7.x/bottom-tab-navigator.md index d4c42977505..574ffc16b34 100755 --- a/versioned_docs/version-7.x/bottom-tab-navigator.md +++ b/versioned_docs/version-7.x/bottom-tab-navigator.md @@ -152,6 +152,7 @@ It supports the following values: - `initialRoute` - return to initial screen passed in `initialRouteName` prop, if not passed, defaults to the first screen - `order` - return to screen defined before the focused screen - `history` - return to last visited screen in the navigator; if the same screen is visited multiple times, the older entries are dropped from the history +- `fullHistory` - return to last visited screen in the navigator; doesn't drop duplicate entries unlike `history` - this behavior is useful to match how web pages work - `none` - do not handle back button #### `detachInactiveScreens` @@ -325,7 +326,7 @@ function MyTabBar({ navigation }) { ### Options -The following [options](screen-options.md) can be used to configure the screens in the navigator. These can be specified under `screenOptions` prop of `Tab.navigator` or `options` prop of `Tab.Screen`. +The following [options](screen-options.md) can be used to configure the screens in the navigator. These can be specified under `screenOptions` prop of `Tab.Navigator` or `options` prop of `Tab.Screen`. #### `title` @@ -586,7 +587,7 @@ Style object for the component wrapping the screen content. ### Header related options -You can find the list of header related options [here](elements.md#header). These [options](screen-options.md) can be specified under `screenOptions` prop of `Tab.navigator` or `options` prop of `Tab.Screen`. You don't have to be using `@react-navigation/elements` directly to use these options, they are just documented in that page. +You can find the list of header related options [here](elements.md#header). These [options](screen-options.md) can be specified under `screenOptions` prop of `Tab.Navigator` or `options` prop of `Tab.Screen`. You don't have to be using `@react-navigation/elements` directly to use these options, they are just documented in that page. In addition to those, the following options are also supported in bottom tabs: diff --git a/versioned_docs/version-7.x/community-libraries.md b/versioned_docs/version-7.x/community-libraries.md new file mode 100755 index 00000000000..74b8fc2e3e0 --- /dev/null +++ b/versioned_docs/version-7.x/community-libraries.md @@ -0,0 +1,31 @@ +--- +id: community-libraries +title: Community libraries +sidebar_label: Community libraries +--- + +This guide lists various community libraries that can be used alongside React Navigation to enhance its functionality. + +:::note + +Please refer to the library's documentation to see which version of React Navigation it supports. + +::: + +## react-native-screen-transitions + +A library that provides customizable screen transition animations for React Navigation's [Native Stack Navigator](native-stack-navigator.md). + +[Repository](https://github.com/eds2002/react-native-screen-transitions) + +### react-navigation-header-buttons + +Helps you to render buttons in the navigation bar and handle the styling so you don't have to. It tries to mimic the appearance of native navbar buttons and attempts to offer a simple interface for you to interact with. + +[Repository](https://github.com/vonovak/react-navigation-header-buttons) + +### react-navigation-props-mapper + +Provides simple HOCs that map react-navigation props to your screen components directly - ie. instead of `const user = this.props.route.params.activeUser`, you'd write `const user = this.props.activeUser`. + +[Repository](https://github.com/vonovak/react-navigation-props-mapper) diff --git a/versioned_docs/version-7.x/community-navigators.md b/versioned_docs/version-7.x/community-navigators.md new file mode 100755 index 00000000000..74c1a3278c8 --- /dev/null +++ b/versioned_docs/version-7.x/community-navigators.md @@ -0,0 +1,31 @@ +--- +id: community-navigators +title: Community navigators +sidebar_label: Community navigators +--- + +This guide lists various community navigators for React Navigation. These navigators offer provide UI components for navigation with the familiar React Navigation API. + +If you're looking to build your own navigator, check out the [custom navigators guide](custom-navigators.md). + +:::note + +Please refer to the library's documentation to see which version of React Navigation it supports. + +::: + +## React Native Bottom Tabs + +This project aims to expose the native Bottom Tabs component to React Native. It exposes SwiftUI's TabView on iOS and the material design tab bar on Android. Using `react-native-bottom-tabs` can bring several benefits, including multi-platform support and a native-feeling tab bar. + +[Documentation](https://oss.callstack.com/react-native-bottom-tabs/) + +[Repository](https://github.com/callstackincubator/react-native-bottom-tabs) + +## BottomNavigation - React Native Paper + +The library provides React Navigation integration for its Material Bottom Tabs. Material Bottom Tabs is a material-design themed tab bar on the bottom of the screen that lets you switch between different routes with animation. + +[Documentation](https://callstack.github.io/react-native-paper/docs/guides/bottom-navigation/) + +[Repository](https://github.com/callstack/react-native-paper) diff --git a/versioned_docs/version-7.x/community-solutions.md b/versioned_docs/version-7.x/community-solutions.md new file mode 100755 index 00000000000..18b8f2c81c6 --- /dev/null +++ b/versioned_docs/version-7.x/community-solutions.md @@ -0,0 +1,35 @@ +--- +id: community-solutions +title: Community solutions +sidebar_label: Community solutions +--- + +This guide lists various community navigation solutions built on top of React Navigation that offer a different API or complement React Navigation in some way. + +:::note + +Please refer to the library's documentation to see which version of React Navigation it supports. + +::: + +## Expo Router + +Expo Router is a file-based router for React Native and web applications built by the Expo team. + +[Documentation](https://docs.expo.dev/router/introduction/) + +[Repository](https://github.com/expo/expo/tree/main/packages/expo-router) + +## Solito + +A wrapper around React Navigation and Next.js that lets you share navigation code across platforms. Also, it provides a set of patterns and examples for building cross-platform apps with React Native + Next.js. + +[Documentation](https://solito.dev/) + +[Repository](https://github.com/nandorojo/solito) + +## Navio + +Navio provides a different API for React Navigation. It's main goal is to improve DX by building the app layout in one place and using the power of TypeScript to provide route names autocompletion. + +[Repository](https://github.com/kanzitelli/rn-navio) diff --git a/versioned_docs/version-7.x/contributing.md b/versioned_docs/version-7.x/contributing.md index aa31a3cb0b6..ec05c128ca0 100755 --- a/versioned_docs/version-7.x/contributing.md +++ b/versioned_docs/version-7.x/contributing.md @@ -15,23 +15,9 @@ Here are some of the ways to contribute to the project: - [Bug Fixes](#bug-fixes) - [Suggesting a Feature](#suggesting-a-feature) - [Big Pull Requests](#big-pull-requests) -- [Information](#information) - - [Issue Template](#issue-template) - - [Pull Request Template](#pull-request-template) - - [Forking the Repository](#forking-the-repository) - - [Code Review Guidelines](#code-review-guidelines) - - [Run the Example App](#run-the-example-app) - - [Run Tests](#run-tests) And here are a few helpful resources to aid in getting started: -- [Contributing](#contributing) - - [Reporting Bugs](#reporting-bugs) - - [Improving the Documentation](#improving-the-documentation) - - [Responding to Issues](#responding-to-issues) - - [Bug Fixes](#bug-fixes) - - [Suggesting a Feature](#suggesting-a-feature) - - [Big Pull Requests](#big-pull-requests) - [Information](#information) - [Issue Template](#issue-template) - [Pull Request Template](#pull-request-template) @@ -47,7 +33,7 @@ And here are a few helpful resources to aid in getting started: You can't write code without writing the occasional bug. Especially as React Navigation is moving quickly, bugs happen. When you think you've found one here's what to do: 1. Search the existing issues for one like what you're seeing. If you see one, add a 👍 reaction (please no +1 comments). Read through the comments and see if you can provide any more valuable information to the thread -2. If there are no other issues like yours then create a new one. Be sure to follow the [issue template](https://github.com/react-navigation/react-navigation/blob/main/.github/ISSUE_TEMPLATE.md). +2. If there are no other issues like yours then create a new one. Be sure to follow the [issue template](https://github.com/react-navigation/react-navigation/blob/main/.github/ISSUE_TEMPLATE/bug-report.yml). Creating a high quality reproduction is critical. Without it we likely can't fix the bug and, in an ideal situation, you'll find out that it's not actually a bug of the library but simply done incorrectly in your project. Instant bug fix! @@ -96,7 +82,7 @@ The reason we want to do this is to save everyone time. Maybe that feature alrea ### Issue Template -Before submitting an issue, please take a look at the [issue template](https://github.com/react-navigation/react-navigation/blob/main/.github/ISSUE_TEMPLATE.md) and follow it. This is in place to help everyone better understand the issue you're having and reduce the back and forth to get the necessary information. +Before submitting an issue, please take a look at the [issue template](https://github.com/react-navigation/react-navigation/blob/main/.github/ISSUE_TEMPLATE/bug-report.yml) and follow it. This is in place to help everyone better understand the issue you're having and reduce the back and forth to get the necessary information. Yes, it takes time and effort to complete the issue template. But that's the only way to ask high quality questions that actually get responses. @@ -104,7 +90,7 @@ Would you rather take 1 minute to create an incomplete issue report and wait mon ### Pull Request Template -Much like the issue template, the [pull request template](https://github.com/react-navigation/react-navigation/blob/main/.github/PULL_REQUEST.md) lays out instructions to ensure your pull request gets reviewed in a timely manner and reduces the back and forth. Make sure to look it over before you start writing any code. +Much like the issue template, the [pull request template](https://github.com/react-navigation/react-navigation/blob/main/.github/PULL_REQUEST_TEMPLATE.md) lays out instructions to ensure your pull request gets reviewed in a timely manner and reduces the back and forth. Make sure to look it over before you start writing any code. ### Forking the Repository diff --git a/versioned_docs/version-7.x/custom-navigators.md b/versioned_docs/version-7.x/custom-navigators.md index c8567fa8483..5fa42e00ff1 100755 --- a/versioned_docs/version-7.x/custom-navigators.md +++ b/versioned_docs/version-7.x/custom-navigators.md @@ -4,26 +4,60 @@ title: Custom navigators sidebar_label: Custom navigators --- -Navigators allow you to define your application's navigation structure. Navigators also render common elements such as headers and tab bars which you can configure. +In essence, a navigator is a React component that takes a set of screens and options, and renders them based on its [navigation state](navigation-state.md), generally with additional UI such as headers, tab bars, or drawers. -Under the hood, navigators are plain React components. +React Navigation provides a few built-in navigators, but they might not always fit your needs if you want a very custom behavior or UI. In such cases, you can build your own custom navigators using React Navigation's APIs. -## Built-in Navigators +A custom navigator behaves just like a built-in navigator, and can be used in the same way. This means you can define screens the same way, use [route](route-object.md) and [navigation](navigation-object.md) objects in your screens, and navigate between screens with familiar API. The navigator will also be able to handle deep linking, state persistence, and other features that built-in navigators support. -We include some commonly needed navigators such as: +## Overview -- [`createStackNavigator`](stack-navigator.md) - Renders one screen at a time and provides transitions between screens. When a new screen is opened it is placed on top of the stack. -- [`createDrawerNavigator`](drawer-navigator.md) - Provides a drawer that slides in from the left of the screen by default. -- [`createBottomTabNavigator`](bottom-tab-navigator.md) - Renders a tab bar that lets the user switch between several screens. -- [`createMaterialTopTabNavigator`](material-top-tab-navigator.md) - Renders tab view which lets the user switch between several screens using swipe gesture or the tab bar. +Under the hood, navigators are plain React components that use the [`useNavigationBuilder`](#usenavigationbuilder) hook. -## API for building custom navigators +The navigator component then uses this state to layout the screens appropriately with any additional UI based on the use case. This component is then wrapped in [`createNavigatorFactory`](#createnavigatorfactory) to create the API for the navigator. -A navigator bundles a router and a view which takes the [navigation state](navigation-state.md) and decides how to render it. We export a `useNavigationBuilder` hook to build custom navigators that integrate with rest of React Navigation. +A very basic example looks like this: + +```js +function MyStackNavigator(props) { + const { state, descriptors, NavigationContent } = useNavigationBuilder( + StackRouter, + props + ); + + const focusedRoute = state.routes[state.index]; + const descriptor = descriptors[focusedRoute.key]; + + return {descriptor.render()}; +} + +export const createMyStackNavigator = createNavigatorFactory(MyStackNavigator); +``` + +Now, we have an already working navigator, even though it doesn't do anything special yet. + +Let's break this down: + +- We define a `MyNavigator` component that contains our navigator logic. This is the component that's rendered when you render `` in your app with the `createMyStackNavigator` factory function. +- We use the `useNavigationBuilder` hook and pass it [`StackRouter`](custom-routers.md#built-in-routers), which would make our navigator behave like a stack navigator. Any other router such as `TabRouter`, `DrawerRouter`, or a custom router can be used here as well. +- The hook returns the [navigation state](navigation-state.md) in the `state` property. This is the current state of the navigator. There's also a `descriptors` object which contains the data and helpers for each screen in the navigator. +- We get the focused route from the state with `state.routes[state.index]` - as `state.index` is the index of the currently focused route in the `state.routes` array. +- Then we get the corresponding descriptor for the focused route with `descriptors[focusedRoute.key]` and call the `render()` method on it to get the React element for the screen. +- The content of the navigator is wrapped in `NavigationContent` to provide appropriate context and wrappers. + +With this, we have a basic stack navigator that renders only the focused screen. Unlike the built-in stack navigator, this doesn't keep unfocused screens rendered. But you can loop through `state.routes` and render all of the screens if you want to keep them mounted. You can also read `descriptor.options` to get the [options](screen-options.md) to handle the screen's title, header, and other options. + +This also doesn't have any additional UI apart from the screen content. There are no gestures or animations. So you're free to add any additional UI, gestures, animations etc. as needed. You can also layout the screens in any way you want, such as rendering them side-by-side or in a grid, instead of stacking them on top of each other like the built-in stack navigator does. + +You can see a more complete example of a custom navigator later in this document. + +## API Definition ### `useNavigationBuilder` -This hook allows a component to hook into React Navigation. It accepts the following arguments: +This hook contains the core logic of a navigator, and is responsible for storing and managing the [navigation state](navigation-state.md). It takes a [router](custom-routers.md) as an argument to know how to handle various navigation actions. It then returns the state and helper methods for the navigator component to use. + +It accepts the following arguments: - `createRouter` - A factory method which returns a router object (e.g. `StackRouter`, `TabRouter`). - `options` - Options for the hook and the router. The navigator should forward its props here so that user can provide props to configure the navigator. By default, the following options are accepted: @@ -56,27 +90,9 @@ import { TabActions, } from '@react-navigation/native'; -function TabNavigator({ - id, - initialRouteName, - children, - layout, - screenListeners, - screenOptions, - screenLayout, - tabBarStyle, - contentStyle, -}) { +function TabNavigator({ tabBarStyle, contentStyle, ...rest }) { const { state, navigation, descriptors, NavigationContent } = - useNavigationBuilder(TabRouter, { - id, - initialRouteName, - children, - layout, - screenListeners, - screenOptions, - screenLayout, - }); + useNavigationBuilder(TabRouter, rest); return ( @@ -158,6 +174,12 @@ export function createMyNavigator(config) { } ``` +:::note + +We can also do `export const createMyNavigator = createNavigatorFactory(MyNavigator)` directly instead of wrapping in another function. However, the wrapper function is necessary to have proper [TypeScript support](#type-checking-navigators) for the navigator. + +::: + Then it can be used like this: ```js @@ -210,7 +232,7 @@ import { useNavigationBuilder, } from '@react-navigation/native'; -// Props accepted by the view +// Additional props accepted by the view type TabNavigationConfig = { tabBarStyle: StyleProp; contentStyle: StyleProp; @@ -222,7 +244,6 @@ type TabNavigationOptions = { }; // Map of event name and the type of data (in event.data) -// // canPreventDefault: true adds the defaultPrevented property to the // emitted events. type TabNavigationEventMap = { @@ -232,28 +253,34 @@ type TabNavigationEventMap = { }; }; +// The type of the navigation object for each screen +type TabNavigationProp< + ParamList extends ParamListBase, + RouteName extends keyof ParamList = keyof ParamList, + NavigatorID extends string | undefined = undefined, +> = NavigationProp< + ParamList, + RouteName, + NavigatorID, + TabNavigationState, + TabNavigationOptions, + TabNavigationEventMap +> & + TabActionHelpers; + // The props accepted by the component is a combination of 3 things type Props = DefaultNavigatorOptions< ParamListBase, + string | undefined, TabNavigationState, TabNavigationOptions, - TabNavigationEventMap + TabNavigationEventMap, + TabNavigationProp > & TabRouterOptions & TabNavigationConfig; -function TabNavigator({ - id, - initialRouteName, - children, - layout, - screenListeners, - screenOptions, - screenLayout, - backBehavior, - tabBarStyle, - contentStyle, -}: Props) { +function TabNavigator({ tabBarStyle, contentStyle, ...rest }: Props) { const { state, navigation, descriptors, NavigationContent } = useNavigationBuilder< TabNavigationState, @@ -261,16 +288,7 @@ function TabNavigator({ TabActionHelpers, TabNavigationOptions, TabNavigationEventMap - >(TabRouter, { - id, - initialRouteName, - children, - layout, - screenListeners, - screenOptions, - screenLayout, - backBehavior, - }); + >(TabRouter, rest); return ( @@ -321,6 +339,7 @@ function TabNavigator({ ); } +// The factory function with generic types for type-inference export function createMyNavigator< const ParamList extends ParamListBase, const NavigatorID extends string | undefined = undefined, @@ -419,3 +438,14 @@ const { state, descriptors, navigation, NavigationContent } = // ... ``` + +:::note + +Customizing built-in navigators like this is an advanced use case and generally not necessary. Consider alternatives such as: + +- [`layout`](navigator.md#layout) prop on navigators to add a wrapper around the navigator +- [`UNSTABLE_router`](navigator.md#router) prop on navigators to customize the router behavior + +Also refer to the navigator's documentation to see if any existing API meets your needs. + +::: diff --git a/versioned_docs/version-7.x/custom-routers.md b/versioned_docs/version-7.x/custom-routers.md index 584e13662a0..86b66d3a1f9 100755 --- a/versioned_docs/version-7.x/custom-routers.md +++ b/versioned_docs/version-7.x/custom-routers.md @@ -150,9 +150,10 @@ The library ships with a few standard routers: ## Customizing Routers -You can reuse a router and override the router functions as per your needs, such as customizing how existing actions are handled, adding additional actions etc. +There are two main ways to customize routers: -See [custom navigators](custom-navigators.md) for details on how to override the router with a custom router in an existing navigator. +- Override an existing router with the [`UNSTABLE_router`](navigator.md#router) prop on navigators +- Customized navigators with a custom router, see [extending navigators](custom-navigators.md#extending-navigators) ### Custom Navigation Actions diff --git a/versioned_docs/version-7.x/deep-linking.md b/versioned_docs/version-7.x/deep-linking.md index 6b6389861fc..2dcac6d9f96 100755 --- a/versioned_docs/version-7.x/deep-linking.md +++ b/versioned_docs/version-7.x/deep-linking.md @@ -18,7 +18,12 @@ While you don't need to use the `linking` prop from React Navigation, and can ha Below, we'll go through required configurations so that the deep link integration works. -## Setup with Expo projects +## Setting up deep links + + + + +### Configuring URL scheme First, you will want to specify a URL scheme for your app. This corresponds to the string before `://` in a URL, so if your scheme is `example` then a link to your app would be `example://`. You can register for a scheme in your `app.json` by adding a string under the scheme key: @@ -36,76 +41,108 @@ Next, install `expo-linking` which we'd need to get the deep link prefix: npx expo install expo-linking ``` -Then, let's configure React Navigation to use the `scheme` for parsing incoming deep links: - - - +Then you can use `Linking.createURL` to get the prefix for your app: ```js -import * as Linking from 'expo-linking'; +const linking = { + prefixes: [Linking.createURL('/'), +}; +``` -const prefix = Linking.createURL('/'); +See more details below at [Configuring React Navigation](#configuring-react-navigation). -/* content */ +
+Why use `Linking.createURL`? -function App() { - const linking = { - prefixes: [prefix], - }; +It is necessary to use `Linking.createURL` since the scheme differs between the [Expo Dev Client](https://docs.expo.dev/versions/latest/sdk/dev-client/) and standalone apps. - return ; -} -``` +The scheme specified in `app.json` only applies to standalone apps. In the Expo client app you can deep link using `exp://ADDRESS:PORT/--/` where `ADDRESS` is often `127.0.0.1` and `PORT` is often `19000` - the URL is printed when you run `expo start`. The `Linking.createURL` function abstracts it out so that you don't need to specify them manually. - - +
+ +If you are using universal links, you need to add your domain to the prefixes as well: ```js -import * as Linking from 'expo-linking'; +const linking = { + prefixes: [Linking.createURL('/'), 'https://app.example.com'], +}; +``` -const prefix = Linking.createURL('/'); +### Universal Links on iOS -function App() { - const linking = { - prefixes: [prefix], - }; +To set up iOS universal Links in your Expo app, you need to configure your [app config](https://docs.expo.dev/workflow/configuration) to include the associated domains and entitlements: - return ( - Loading...}> - {/* content */} - - ); +```json +{ + "expo": { + "ios": { + "associatedDomains": ["applinks:app.example.com"], + "entitlements": { + "com.apple.developer.associated-domains": ["applinks:app.example.com"] + } + } + } } ``` -
-
+You will also need to setup [Associated Domains](https://developer.apple.com/documentation/Xcode/supporting-associated-domains) on your server. -The reason that is necessary to use `Linking.createURL` is that the scheme will differ depending on whether you're in the client app or in a standalone app. +See [Expo's documentation on iOS Universal Links](https://docs.expo.dev/linking/ios-universal-links/) for more details. -The scheme specified in `app.json` only applies to standalone apps. In the Expo client app you can deep link using `exp://ADDRESS:PORT/--/` where `ADDRESS` is often `127.0.0.1` and `PORT` is often `19000` - the URL is printed when you run `expo start`. The `Linking.createURL` function abstracts it out so that you don't need to specify them manually. +### App Links on Android -If you are using universal links, you need to add your domain to the prefixes as well: +To set up Android App Links in your Expo app, you need to configure your [app config](https://docs.expo.dev/workflow/configuration) to include the `intentFilters`: -```js -const linking = { - prefixes: [Linking.createURL('/'), 'https://app.example.com'], -}; +```json +{ + "expo": { + "android": { + "intentFilters": [ + { + "action": "VIEW", + "autoVerify": true, + "data": [ + { + "scheme": "https", + "host": "app.example.com" + } + ], + "category": ["BROWSABLE", "DEFAULT"] + } + ] + } + } +} ``` -## Set up with bare React Native projects +You will also need to [declare the association](https://developer.android.com/training/app-links/verify-android-applinks#web-assoc) between your website and your intent filters by hosting a Digital Asset Links JSON file. + +See [Expo's documentation on Android App Links](https://docs.expo.dev/linking/android-app-links/) for more details. + +
+ ### Setup on iOS Let's configure the native iOS app to open based on the `example://` URI scheme. -You'll need to link `RCTLinking` to your project by following the steps described here. To be able to listen to incoming app links, you'll need to add the following lines to `AppDelegate.m` in your project: +You'll need to add the `LinkingIOS` folder into your header search paths as described [here](https://reactnative.dev/docs/linking-libraries-ios#step-3). Then you'll need to add the following lines to your or `AppDelegate.swift` or `AppDelegate.mm` file: + + + + +```swift +func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool { + return RCTLinkingManager.application(app, open: url, options: options) +} +``` + + + ```objc -// Add the header at the top of the file: #import -// Add this inside `@implementation AppDelegate` above `@end`: - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary *)options @@ -114,10 +151,31 @@ You'll need to link `RCTLinking` to your project by following the steps describe } ``` + + + If your app is using [Universal Links](https://developer.apple.com/ios/universal-links/), you'll need to add the following code as well: + + + +```swift +func application( + _ application: UIApplication, + continue userActivity: NSUserActivity, + restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool { + return RCTLinkingManager.application( + application, + continue: userActivity, + restorationHandler: restorationHandler + ) + } +``` + + + + ```objc -// Add this inside `@implementation AppDelegate` above `@end`: - (BOOL)application:(UIApplication *)application continueUserActivity:(nonnull NSUserActivity *)userActivity restorationHandler:(nonnull void (^)(NSArray> * _Nullable))restorationHandler { @@ -127,6 +185,9 @@ If your app is using [Universal Links](https://developer.apple.com/ios/universal } ``` + + + Now you need to add the scheme to your project configuration. The easiest way to do this is with the `uri-scheme` package by running the following: @@ -208,13 +269,70 @@ After adding them, it should look like this: - + ``` Then, you need to [declare the association](https://developer.android.com/training/app-links/verify-android-applinks#web-assoc) between your website and your intent filters by hosting a Digital Asset Links JSON file. + +
+ +## Configuring React Navigation + +To handle deep links, you need to configure React Navigation to use the `scheme` for parsing incoming deep links: + + + + +```js +const linking = { + prefixes: [ + 'example://', // Or `Linking.createURL('/')` for Expo apps + ], +}; + +function App() { + return ; +} +``` + + + + +```js +const linking = { + prefixes: [ + 'example://', // Or `Linking.createURL('/')` for Expo apps + ], +}; + +function App() { + return ( + Loading...}> + {/* content */} + + ); +} +``` + + + + +If you are using universal links, you need to add your domain to the prefixes as well: + +```js +const linking = { + prefixes: [ + 'example://', // Or `Linking.createURL('/')` for Expo apps + 'https://app.example.com', + ], +}; +``` + +See [configuring links](configuring-links.md) to see further details on how to configure links in React Navigation. + ## Testing deep links Before testing deep links, make sure that you rebuild and install the app in your emulator/simulator/device. @@ -231,7 +349,7 @@ If you're testing on Android, run: npx react-native run-android ``` -If you're using Expo managed workflow and testing on Expo client, you don't need to rebuild the app. However, you will need to use the correct address and port that's printed when you run `expo start` ([see above](#setup-with-expo-projects)), e.g. `exp://127.0.0.1:19000/--/`. +If you're using Expo managed workflow and testing on Expo client, you don't need to rebuild the app. However, you will need to use the correct address and port that's printed when you run `expo start`, e.g. `exp://127.0.0.1:19000/--/`. If you want to test with your custom scheme in your Expo app, you will need rebuild your standalone app by running `expo build:ios -t simulator` or `expo build:android` and install the resulting binaries. @@ -289,56 +407,55 @@ Or if using Expo client: adb shell am start -W -a android.intent.action.VIEW -d "exp://127.0.0.1:19000/--/chat/jane" host.exp.exponent ``` -## Third-party integrations +## Integrating with other tools + +In addition to deep links and universal links with React Native's `Linking` API, you may also want to integrate other tools for handling incoming links, e.g. Push Notifications - so that tapping on a notification can open the app to a specific screen. -React Native's `Linking` isn't the only way to handle deep linking. You might also want to integrate other services such as [Firebase Dynamic Links](https://firebase.google.com/docs/dynamic-links), [Branch](https://help.branch.io/developers-hub/docs/react-native) etc. which provide their own API for getting notified of incoming links. +To achieve this, you'd need to override how React Navigation subscribes to incoming links. To do so, you can provide your own [`getInitialURL`](navigation-container.md#linkinggetinitialurl) and [`subscribe`](navigation-container.md#linkingsubscribe) functions. -To achieve this, you'd need to override how React Navigation subscribes to incoming links. To do so, you can provide your own [`getInitialURL`](navigation-container.md#linkinggetinitialurl) and [`subscribe`](navigation-container.md#linkingsubscribe) functions: +Here is an example integration with [expo-notifications](https://docs.expo.dev/versions/latest/sdk/notifications): -```js name="Third-party integrations" +```js name="Expo Notifications" const linking = { - prefixes: ['myapp://', 'https://myapp.com'], + prefixes: ['example://', 'https://app.example.com'], // Custom function to get the URL which was used to open the app async getInitialURL() { - // First, you would need to get the initial URL from your third-party integration - // The exact usage depend on the third-party SDK you use - // For example, to get the initial URL for Firebase Dynamic Links: - const { isAvailable } = utils().playServicesAvailability; - - if (isAvailable) { - const initialLink = await dynamicLinks().getInitialLink(); + // First, handle deep links + const url = await Linking.getInitialURL(); - if (initialLink) { - return initialLink.url; - } + if (url != null) { + return url; } - // As a fallback, you may want to do the default deep link handling - const url = await Linking.getInitialURL(); + // Handle URL from expo push notifications + const response = await Notifications.getLastNotificationResponseAsync(); - return url; + return response?.notification.request.content.data.url; }, // Custom function to subscribe to incoming links subscribe(listener) { - // Listen to incoming links from Firebase Dynamic Links - const unsubscribeFirebase = dynamicLinks().onLink(({ url }) => { - listener(url); - }); - - // Listen to incoming links from deep linking + // Listen to incoming links for deep links const linkingSubscription = Linking.addEventListener('url', ({ url }) => { listener(url); }); + // Listen to expo push notifications when user interacts with them + const pushNotificationSubscription = + Notifications.addNotificationResponseReceivedListener((response) => { + const url = response.notification.request.content.data.url; + + listener(url); + }); + return () => { // Clean up the event listeners - unsubscribeFirebase(); linkingSubscription.remove(); + pushNotificationSubscription.remove(); }; }, }; @@ -347,47 +464,44 @@ const linking = { -```js name="Third-party integrations" +```js name="Expo Notifications" const linking = { - prefixes: ['myapp://', 'https://myapp.com'], + prefixes: ['example://', 'https://app.example.com'], // Custom function to get the URL which was used to open the app async getInitialURL() { - // First, you would need to get the initial URL from your third-party integration - // The exact usage depend on the third-party SDK you use - // For example, to get the initial URL for Firebase Dynamic Links: - const { isAvailable } = utils().playServicesAvailability; - - if (isAvailable) { - const initialLink = await dynamicLinks().getInitialLink(); + // First, handle deep links + const url = await Linking.getInitialURL(); - if (initialLink) { - return initialLink.url; - } + if (url != null) { + return url; } - // As a fallback, you may want to do the default deep link handling - const url = await Linking.getInitialURL(); + // Handle URL from expo push notifications + const response = await Notifications.getLastNotificationResponseAsync(); - return url; + return response?.notification.request.content.data.url; }, // Custom function to subscribe to incoming links subscribe(listener) { - // Listen to incoming links from Firebase Dynamic Links - const unsubscribeFirebase = dynamicLinks().onLink(({ url }) => { - listener(url); - }); - - // Listen to incoming links from deep linking + // Listen to incoming links for deep links const linkingSubscription = Linking.addEventListener('url', ({ url }) => { listener(url); }); + // Listen to expo push notifications when user interacts with them + const pushNotificationSubscription = + Notifications.addNotificationResponseReceivedListener((response) => { + const url = response.notification.request.content.data.url; + + listener(url); + }); + return () => { // Clean up the event listeners - unsubscribeFirebase(); linkingSubscription.remove(); + pushNotificationSubscription.remove(); }; }, diff --git a/versioned_docs/version-7.x/drawer-layout.md b/versioned_docs/version-7.x/drawer-layout.md index 045b88280f2..0a5d8ddb7d1 100644 --- a/versioned_docs/version-7.x/drawer-layout.md +++ b/versioned_docs/version-7.x/drawer-layout.md @@ -20,56 +20,36 @@ To use this package, open a Terminal in the project root and run: npm install react-native-drawer-layout ``` -Then, you need to install and configure the libraries that are required by the drawer: +The library depends on [`react-native-gesture-handler`](https://docs.swmansion.com/react-native-gesture-handler/) for gestures and [`react-native-reanimated`](https://docs.swmansion.com/react-native-reanimated/) for animations. -1. First, install [`react-native-gesture-handler`](https://docs.swmansion.com/react-native-gesture-handler/) and [`react-native-reanimated`](https://docs.swmansion.com/react-native-reanimated/) (at least version 2 or 3). + + - If you have a Expo managed project, in your project directory, run: +If you have a Expo managed project, in your project directory, run: - ```bash - npx expo install react-native-gesture-handler react-native-reanimated - ``` - - If you have a bare React Native project, in your project directory, run: - - ```bash npm2yarn - npm install react-native-gesture-handler react-native-reanimated - ``` - -2. Configure the Reanimated Babel Plugin in your project following the [installation guide](https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/getting-started). - -3. To finalize the installation of `react-native-gesture-handler`, we need to conditionally import it. To do this, create 2 files: - - ```js title="gesture-handler.native.js" - // Only import react-native-gesture-handler on native platforms - import 'react-native-gesture-handler'; - ``` - - ```js title="gesture-handler.js" - // Don't import react-native-gesture-handler on web - ``` - - Now, add the following at the **top** (make sure it's at the top and there's nothing else before it) of your entry file, such as `index.js` or `App.js`: - - ```js - import './gesture-handler'; - ``` +```bash +npx expo install react-native-gesture-handler react-native-reanimated react-native-worklets +``` - Since the drawer layout doesn't use `react-native-gesture-handler` on Web, this avoids unnecessarily increasing the bundle size. + + - :::warning +If you have a bare React Native project, in your project directory, run: - If you are building for Android or iOS, do not skip this step, or your app may crash in production even if it works fine in development. This is not applicable to other platforms. +```bash npm2yarn +npm install react-native-gesture-handler react-native-reanimated react-native-worklets +``` - ::: +After installation, configure the Reanimated Babel Plugin in your project following the [installation guide](https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/getting-started). -4. If you're on a Mac and developing for iOS, you also need to install the pods (via [Cocoapods](https://cocoapods.org/)) to complete the linking. + + - ```bash - npx pod-install ios - ``` +If you're on a Mac and developing for iOS, you also need to install [pods](https://cocoapods.org/) to complete the linking. -We're done! Now you can build and run the app on your device/simulator. +```bash +npx pod-install ios +``` ## Quick start diff --git a/versioned_docs/version-7.x/drawer-navigator.md b/versioned_docs/version-7.x/drawer-navigator.md index 78fd52c3e05..9f04798ea15 100644 --- a/versioned_docs/version-7.x/drawer-navigator.md +++ b/versioned_docs/version-7.x/drawer-navigator.md @@ -20,54 +20,36 @@ To use this navigator, ensure that you have [`@react-navigation/native` and its npm install @react-navigation/drawer ``` -Then, you need to install and configure the libraries that are required by the drawer navigator: +The navigator depends on [`react-native-gesture-handler`](https://docs.swmansion.com/react-native-gesture-handler/) for gestures and [`react-native-reanimated`](https://docs.swmansion.com/react-native-reanimated/) for animations. -1. First, install [`react-native-gesture-handler`](https://docs.swmansion.com/react-native-gesture-handler/) and [`react-native-reanimated`](https://docs.swmansion.com/react-native-reanimated/) (at least version 2 or 3). + + - If you have a Expo managed project, in your project directory, run: +If you have a Expo managed project, in your project directory, run: - ```bash - npx expo install react-native-gesture-handler react-native-reanimated - ``` - - If you have a bare React Native project, in your project directory, run: - - ```bash npm2yarn - npm install react-native-gesture-handler react-native-reanimated - ``` - -2. Configure the Reanimated Babel Plugin in your project following the [installation guide](https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/getting-started). - -3. To finalize the installation of `react-native-gesture-handler`, we need to conditionally import it. To do this, create 2 files: - - ```js title="gesture-handler.native.js" - // Only import react-native-gesture-handler on native platforms - import 'react-native-gesture-handler'; - ``` - - ```js title="gesture-handler.js" - // Don't import react-native-gesture-handler on web - ``` - - Now, add the following at the **top** (make sure it's at the top and there's nothing else before it) of your entry file, such as `index.js` or `App.js`: +```bash +npx expo install react-native-gesture-handler react-native-reanimated react-native-worklets +``` - ```js - import './gesture-handler'; - ``` + + - Since the drawer navigator doesn't use `react-native-gesture-handler` on Web, this avoids unnecessarily increasing the bundle size. +If you have a bare React Native project, in your project directory, run: - :::warning +```bash npm2yarn +npm install react-native-gesture-handler react-native-reanimated react-native-worklets +``` - If you are building for Android or iOS, do not skip this step, or your app may crash in production even if it works fine in development. This is not applicable to other platforms. +After installation, configure the Reanimated Babel Plugin in your project following the [installation guide](https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/getting-started). - ::: + + -4. If you're on a Mac and developing for iOS, you also need to install the pods (via [Cocoapods](https://cocoapods.org/)) to complete the linking. +If you're on a Mac and developing for iOS, you also need to install [pods](https://cocoapods.org/) to complete the linking. - ```bash - npx pod-install ios - ``` +```bash +npx pod-install ios +``` ## Usage @@ -203,6 +185,7 @@ It supports the following values: - `initialRoute` - return to initial screen passed in `initialRouteName` prop, if not passed, defaults to the first screen - `order` - return to screen defined before the focused screen - `history` - return to last visited screen in the navigator; if the same screen is visited multiple times, the older entries are dropped from the history +- `fullHistory` - return to last visited screen in the navigator; doesn't drop duplicate entries unlike `history` - this behavior is useful to match how web pages work - `none` - do not handle back button #### `defaultStatus` @@ -304,7 +287,7 @@ To use the custom component, we need to pass it in the `drawerContent` prop: ### Options -The following [options](screen-options.md) can be used to configure the screens in the navigator. These can be specified under `screenOptions` prop of `Drawer.navigator` or `options` prop of `Drawer.Screen`. +The following [options](screen-options.md) can be used to configure the screens in the navigator. These can be specified under `screenOptions` prop of `Drawer.Navigator` or `options` prop of `Drawer.Screen`. #### `title` @@ -581,7 +564,7 @@ It only works when there is a stack navigator (e.g. [stack navigator](stack-navi ### Header related options -You can find the list of header related options [here](elements.md#header). These [options](screen-options.md) can be specified under `screenOptions` prop of `Drawer.navigator` or `options` prop of `Drawer.Screen`. You don't have to be using `@react-navigation/elements` directly to use these options, they are just documented in that page. +You can find the list of header related options [here](elements.md#header). These [options](screen-options.md) can be specified under `screenOptions` prop of `Drawer.Navigator` or `options` prop of `Drawer.Screen`. You don't have to be using `@react-navigation/elements` directly to use these options, they are just documented in that page. In addition to those, the following options are also supported in drawer: diff --git a/versioned_docs/version-7.x/getting-started.md b/versioned_docs/version-7.x/getting-started.md index 9e747fe04b3..2e7d1b8dc5b 100755 --- a/versioned_docs/version-7.x/getting-started.md +++ b/versioned_docs/version-7.x/getting-started.md @@ -15,10 +15,10 @@ If you're already familiar with JavaScript, React and React Native, then you'll Here are some resources to help you out: -1. [React Native](https://reactnative.dev/docs/getting-started) -2. [Main Concepts of React](https://react.dev/learn) -3. [React Hooks](https://react.dev/reference/react) -4. [React Context](https://react.dev/learn/passing-data-deeply-with-context) (Advanced) +1. [Main Concepts of React](https://react.dev/learn) +2. [Getting started with React Native](https://reactnative.dev/docs/getting-started) +3. [React Hooks](https://react.dev/reference/react/hooks) +4. [React Context](https://react.dev/learn/passing-data-deeply-with-context) ## Minimum requirements @@ -42,17 +42,20 @@ Otherwise, you can follow the instructions below to install React Navigation int ## Installation -Install the required packages in your React Native project: +The `@react-navigation/native` package contains the core functionality of React Navigation. + +In your project directory, run: ```bash npm2yarn npm install @react-navigation/native ``` -React Navigation is made up of some core utilities and those are then used by navigators to create the navigation structure in your app. Don't worry too much about this for now, it'll become clear soon enough! To frontload the installation work, let's also install and configure dependencies used by most navigators, then we can move forward with starting to write some code. +### Installing dependencies -The libraries we will install now are [`react-native-screens`](https://github.com/software-mansion/react-native-screens) and [`react-native-safe-area-context`](https://github.com/th3rdwave/react-native-safe-area-context). If you already have these libraries installed and at the latest version, you are done here! Otherwise, read on. +Let's also install and configure dependencies used by most navigators. The libraries we will install now are [`react-native-screens`](https://github.com/software-mansion/react-native-screens) and [`react-native-safe-area-context`](https://github.com/th3rdwave/react-native-safe-area-context). -### Installing dependencies into an Expo managed project + + In your project directory, run: @@ -60,11 +63,10 @@ In your project directory, run: npx expo install react-native-screens react-native-safe-area-context ``` -This will install versions of these libraries that are compatible. - -You can now continue to ["Hello React Navigation"](hello-react-navigation.md) to start writing some code. +This will install versions of these libraries that are compatible with your Expo SDK version. -### Installing dependencies into a bare React Native project + + In your project directory, run: @@ -72,50 +74,61 @@ In your project directory, run: npm install react-native-screens react-native-safe-area-context ``` -:::note - -You might get warnings related to peer dependencies after installation. They are usually caused by incorrect version ranges specified in some packages. You can safely ignore these warnings as long as your app builds and works as expected. - -::: - If you're on a Mac and developing for iOS, you need to install the pods (via [Cocoapods](https://cocoapods.org/)) to complete the linking. ```bash npx pod-install ios ``` -`react-native-screens` package requires one additional configuration step to properly -work on Android devices. Edit `MainActivity.kt` or `MainActivity.java` file which is located under `android/app/src/main/java//`. +#### Configuring `react-native-screens` on Android -Add the highlighted code to the body of `MainActivity` class: +[`react-native-screens`](https://github.com/software-mansion/react-native-screens) requires one additional configuration to properly work on Android. + +Edit `MainActivity.kt` or `MainActivity.java` file under `android/app/src/main/java//`, and add the highlighted code to the body of `MainActivity` class: ```kotlin +// highlight-start +import android.os.Bundle +import com.swmansion.rnscreens.fragment.restoration.RNScreensFragmentFactory +// highlight-end + +// ... + class MainActivity: ReactActivity() { // ... - // highlight-start override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(null) + // highlight-start + supportFragmentManager.fragmentFactory = RNScreensFragmentFactory() + super.onCreate(savedInstanceState) + // highlight-end } - // highlight-end // ... } ``` - - + + ```java +// highlight-start +import android.os.Bundle; +import com.swmansion.rnscreens.fragment.restoration.RNScreensFragmentFactory; +// highlight-end + +// ... + public class MainActivity extends ReactActivity { // ... - // highlight-start @Override protected void onCreate(Bundle savedInstanceState) { - super.onCreate(null); + // highlight-start + getSupportFragmentManager().setFragmentFactory(new RNScreensFragmentFactory()); + super.onCreate(savedInstanceState); + // highlight-end } - // highlight-end // ... } ``` @@ -123,19 +136,25 @@ public class MainActivity extends ReactActivity { -and make sure to add the following import statement at the top of this file below your package statement: +This change is required to avoid crashes related to View state being not persisted consistently across Activity restarts. -```java -import android.os.Bundle; -``` +#### Opting-out of predictive back on Android -This change is required to avoid crashes related to View state being not persisted consistently across Activity restarts. +React Navigation doesn't yet support Android's predictive back gesture. Disabling it is necessary for the system back gesture to work properly with React Navigation. -:::info +To opt out, in `AndroidManifest.xml`, in the `` tag (or `` tag to opt-out at activity level), set the `android:enableOnBackInvokedCallback` flag to `false`: -When you use a navigator (such as stack navigator), you'll need to follow the installation instructions of that navigator for any additional dependencies. If you're getting an error "Unable to resolve module", you need to install that module in your project. +```xml + + + +``` -::: + + ## Setting up React Navigation @@ -143,36 +162,24 @@ Once you've installed and configured the dependencies, you can move on to settin When using React Navigation, you configure [**navigators**](glossary-of-terms.md#navigator) in your app. Navigators handle the transition between screens in your app and provide UI such as header, tab bar etc. -There are 2 primary ways to configure the navigators: - -### Static configuration - -The static configuration API has reduced boilerplate and simplifies things such as TypeScript types and deep linking. If you're starting a new project or are new to React Navigation, this is the **recommended way** to set up your app. If you need more flexibility in the future, you can always mix and match with the dynamic configuration. +:::info -Continue to ["Hello React Navigation"](hello-react-navigation.md?config=static) to start writing some code with the static API. +When you use a navigator (such as stack navigator), you'll need to follow the installation instructions of that navigator for any additional dependencies. -### Dynamic configuration +::: -The dynamic configuration allows for more flexibility but requires more boilerplate and configuration (e.g. for deep links, typescript etc.). +There are 2 primary ways to configure the navigators: -To get started with dynamic configuration, first, we need to wrap your app in `NavigationContainer`. Usually, you'd do this in your entry file, such as `index.js` or `App.js`: +### Static configuration -```js -import * as React from 'react'; -// highlight-next-line -import { NavigationContainer } from '@react-navigation/native'; +The static configuration API lets you write your configuration in an object, and is defined statically, though some aspects of the configuration can still can be changed dynamically. This has reduced boilerplate and simplifies things such as TypeScript types and deep linking. -export default function App() { - return ( - {/* Rest of your app code */} - ); -} -``` +If you're starting a new project or are new to React Navigation, this is the **recommended way** to set up your app. If you need more flexibility in the future, you can always mix and match with the dynamic configuration. -:::warning +Continue to ["Hello React Navigation"](hello-react-navigation.md?config=static) to start writing some code with the static API. -In a typical React Native app, the `NavigationContainer` should be only used once in your app at the root. You shouldn't nest multiple `NavigationContainer`s unless you have a specific use case for them. +### Dynamic configuration -::: +The dynamic configuration API lets you write your configuration in React components, and can change at runtime based on state or props. This allows for more flexibility but requires significantly more boilerplate and configuration for Typescript types, deep linking etc. Continue to ["Hello React Navigation"](hello-react-navigation.md?config=dynamic) to start writing some code with the dynamic API. diff --git a/versioned_docs/version-7.x/handling-safe-area.md b/versioned_docs/version-7.x/handling-safe-area.md index 99937132e48..225bfa87c1d 100755 --- a/versioned_docs/version-7.x/handling-safe-area.md +++ b/versioned_docs/version-7.x/handling-safe-area.md @@ -473,6 +473,6 @@ Similarly, you could apply these paddings in `contentContainerStyle` of `FlatLis ## Summary -- Use `useSafeAreaInsets` hook from `react-native-safe-area-context` instead of `SafeAreaView` component +- Use [`useSafeAreaInsets`](https://appandflow.github.io/react-native-safe-area-context/api/use-safe-area-insets) hook from `react-native-safe-area-context` instead of [`SafeAreaView`](https://reactnative.dev/docs/safeareaview) component - Don't wrap your whole app in `SafeAreaView`, instead apply the styles to content inside your screens - Apply only specific insets using the `useSafeAreaInsets` hook for more control diff --git a/versioned_docs/version-7.x/header-buttons.md b/versioned_docs/version-7.x/header-buttons.md index 025baecf061..8861a6120b4 100755 --- a/versioned_docs/version-7.x/header-buttons.md +++ b/versioned_docs/version-7.x/header-buttons.md @@ -297,6 +297,6 @@ Generally, this is what you want. But it's possible that in some circumstances t ## Summary -- You can set buttons in the header through the `headerLeft` and `headerRight` properties in `options`. -- The back button is fully customizable with `headerLeft`, but if you just want to change the title or image, there are other `options` for that — `headerBackTitle`, `headerBackTitleStyle`, and `headerBackImageSource`. -- You can use a callback for the options prop to access `navigation` and `route` objects. +- You can set buttons in the header through the [`headerLeft`](elements.md#headerleft) and [`headerRight`](elements.md#headerright) properties in [`options`](screen-options.md). +- The back button is fully customizable with `headerLeft`, but if you only want to change the title or image, there are other `options` for that — [`headerBackTitle`](native-stack-navigator.md#headerbacktitle), [`headerBackTitleStyle`](native-stack-navigator.md#headerbacktitlestyle), and [`headerBackImageSource`](native-stack-navigator.md#headerbackimagesource). +- You can use a callback for the options prop to access [`navigation`](navigation-object.md) and [`route`](route-object.md) objects. diff --git a/versioned_docs/version-7.x/headers.md b/versioned_docs/version-7.x/headers.md index 9bfd242ea5e..04f18811603 100755 --- a/versioned_docs/version-7.x/headers.md +++ b/versioned_docs/version-7.x/headers.md @@ -689,6 +689,6 @@ You can read the full list of available `options` for screens inside of a native ## Summary -- You can customize the header inside of the `options` property of your screens. Read the full list of options [in the API reference](native-stack-navigator.md#options). -- The `options` property can be an object or a function. When it is a function, it is provided with an object with the `navigation` and `route` objects. -- You can also specify shared `screenOptions` in the stack navigator configuration when you initialize it. This will apply to all screens in the navigator. +- You can customize the header inside of the [`options`](screen-options.md) property of your screens. Read the full list of options [in the API reference](native-stack-navigator.md#options). +- The `options` property can be an object or a function. When it is a function, it is provided with an object with the [`navigation`](navigation-object.md) and [`route`](route-object.md) objects. +- You can also specify shared [`screenOptions`](screen-options.md#screenoptions-prop-on-the-navigator) in the stack navigator configuration when you initialize it. This will apply to all screens in the navigator. diff --git a/versioned_docs/version-7.x/hello-react-navigation.md b/versioned_docs/version-7.x/hello-react-navigation.md index 5f4bfeaf0db..74e4e98f602 100755 --- a/versioned_docs/version-7.x/hello-react-navigation.md +++ b/versioned_docs/version-7.x/hello-react-navigation.md @@ -42,7 +42,7 @@ npm install @react-navigation/elements `createNativeStackNavigator` is a function that takes a configuration object containing the screens and customization options. The screens are React Components that render the content displayed by the navigator. -`createStaticNavigation` is a function that takes the navigator defined earlier and returns a component that can be rendered in the app. It's only called once in the app. +`createStaticNavigation` is a function that takes the navigator defined earlier and returns a component that can be rendered in the app. It's only called once in the app. Usually, we'd render the returned component at the root of our app, which is usually the component exported from `App.js`, `App.tsx` etc., or used with `AppRegistry.registerComponent`, `Expo.registerRootComponent` etc. ```js name="Native Stack Example" snack // In App.js in a new project @@ -73,12 +73,18 @@ export default function App() { } ``` +:::warning + +In a typical React Native app, the `createStaticNavigation` function should be only used once in your app at the root. + +::: + `createNativeStackNavigator` is a function that returns an object containing 2 properties: `Screen` and `Navigator`. Both of them are React components used for configuring the navigator. The `Navigator` should contain `Screen` elements as its children to define the configuration for routes. -`NavigationContainer` is a component that manages our navigation tree and contains the [navigation state](navigation-state.md). This component must wrap all the navigators in the app. Usually, we'd render this component at the root of our app, which is usually the component exported from `App.js`. +`NavigationContainer` is a component that manages our navigation tree and contains the [navigation state](navigation-state.md). This component must wrap all the navigators in the app. Usually, we'd render this component at the root of our app, which is usually the component exported from `App.js`, `App.tsx` etc., or used with `AppRegistry.registerComponent`, `Expo.registerRootComponent` etc. ```js name="Native Stack Example" snack // In App.js in a new project @@ -115,6 +121,12 @@ export default function App() { } ``` +:::warning + +In a typical React Native app, the `NavigationContainer` should be only used once in your app at the root. You shouldn't nest multiple `NavigationContainer`s unless you have a specific use case for them. + +::: + @@ -525,19 +537,19 @@ If you are using TypeScript, you will need to specify the types accordingly. You - React Native doesn't have a built-in API for navigation like a web browser does. React Navigation provides this for you, along with the iOS and Android gestures and animations to transition between screens. -- `createNativeStackNavigator` is a function that takes the screens configuration and renders our content. +- [`createNativeStackNavigator`](native-stack-navigator.md) is a function that takes the screens configuration and renders our content. - Each property under screens refers to the name of the route, and the value is the component to render for the route. -- To specify what the initial route in a stack is, provide an `initialRouteName` option for the navigator. -- To specify screen-specific options, we can specify an `options` property, and for common options, we can specify `screenOptions`. +- To specify what the initial route in a stack is, provide an [`initialRouteName`](navigator.md#initial-route-name) option for the navigator. +- To specify screen-specific options, we can specify an [`options`](screen-options.md#options-prop-on-screen) property, and for common options, we can specify [`screenOptions`](screen-options.md#screenoptions-prop-on-the-navigator). - React Native doesn't have a built-in API for navigation like a web browser does. React Navigation provides this for you, along with the iOS and Android gestures and animations to transition between screens. -- `Stack.Navigator` is a component that takes route configuration as its children with additional props for configuration and renders our content. -- Each `Stack.Screen` component takes a `name` prop which refers to the name of the route and `component` prop which specifies the component to render for the route. These are the 2 required props. -- To specify what the initial route in a stack is, provide an `initialRouteName` as the prop for the navigator. -- To specify screen-specific options, we can pass an `options` prop to `Stack.Screen`, and for common options, we can pass `screenOptions` to `Stack.Navigator`. +- [`Stack.Navigator`](native-stack-navigator.md) is a component that takes route configuration as its children with additional props for configuration and renders our content. +- Each [`Stack.Screen`](screen.md) component takes a [`name`](screen.md#name) prop which refers to the name of the route and [`component`](screen.md#component) prop which specifies the component to render for the route. These are the 2 required props. +- To specify what the initial route in a stack is, provide an [`initialRouteName`](navigator.md#initial-route-name) as the prop for the navigator. +- To specify screen-specific options, we can pass an [`options`](screen-options.md#options-prop-on-screen) prop to `Stack.Screen`, and for common options, we can pass [`screenOptions`](screen-options.md#screenoptions-prop-on-the-navigator) to `Stack.Navigator`. diff --git a/versioned_docs/version-7.x/material-top-tab-navigator.md b/versioned_docs/version-7.x/material-top-tab-navigator.md index 7324ed4a8a7..3cf4b61bd5d 100755 --- a/versioned_docs/version-7.x/material-top-tab-navigator.md +++ b/versioned_docs/version-7.x/material-top-tab-navigator.md @@ -20,7 +20,10 @@ To use this navigator, ensure that you have [`@react-navigation/native` and its npm install @react-navigation/material-top-tabs ``` -Then, you need to install [`react-native-pager-view`](https://github.com/callstack/react-native-pager-view) which is required by the navigator. +The navigator depends on [`react-native-pager-view`](https://github.com/callstack/react-native-pager-view) for rendering the pages. + + + If you have a Expo managed project, in your project directory, run: @@ -28,13 +31,19 @@ If you have a Expo managed project, in your project directory, run: npx expo install react-native-pager-view ``` + + + If you have a bare React Native project, in your project directory, run: ```bash npm2yarn npm install react-native-pager-view ``` -If you're on a Mac and developing for iOS, you also need to install the pods (via [Cocoapods](https://cocoapods.org/)) to complete the linking. + + + +If you're on a Mac and developing for iOS, you also need to install [pods](https://cocoapods.org/) to complete the linking. ```bash npx pod-install ios diff --git a/versioned_docs/version-7.x/modal.md b/versioned_docs/version-7.x/modal.md index b55c05b78d5..e9921b5d235 100755 --- a/versioned_docs/version-7.x/modal.md +++ b/versioned_docs/version-7.x/modal.md @@ -185,7 +185,7 @@ Instead of specifying this option for a group, it's also possible to specify it ## Summary -- To change the type of transition on a stack navigator you can use the `presentation` option. +- To change the type of transition on a stack navigator you can use the [`presentation`](native-stack-navigator.md#presentation) option. - When `presentation` is set to `modal`, the screens behave like a modal, i.e. they have a bottom to top transition and may show part of the previous screen in the background. - Setting `presentation: 'modal'` on a group makes all the screens in the group modals, so to use non-modal transitions on other screens, we add another group with the default configuration. diff --git a/versioned_docs/version-7.x/more-resources.md b/versioned_docs/version-7.x/more-resources.md index b76b7398f9d..8e71db8cf3a 100755 --- a/versioned_docs/version-7.x/more-resources.md +++ b/versioned_docs/version-7.x/more-resources.md @@ -1,7 +1,7 @@ --- id: more-resources -title: More Resources -sidebar_label: More Resources +title: More resources +sidebar_label: More resources --- ## Talks diff --git a/versioned_docs/version-7.x/native-bottom-tab-navigator.md b/versioned_docs/version-7.x/native-bottom-tab-navigator.md new file mode 100755 index 00000000000..d712521ff07 --- /dev/null +++ b/versioned_docs/version-7.x/native-bottom-tab-navigator.md @@ -0,0 +1,469 @@ +--- +id: native-bottom-tab-navigator +title: Native Bottom Tabs Navigator +sidebar_label: Native Bottom Tabs +--- + +:::warning + +This navigator is currently experimental. The API will change in future releases. + +Currently only iOS and Android are supported. Use [`createBottomTabNavigator`](bottom-tab-navigator.md) for web support. + +::: + +Native Bottom Tabs displays screens with a tab bar to switch between them. + + + + + +The navigator uses native components on iOS and Android for better platform integration. On iOS, it uses `UITabBarController` and on Android, it uses `BottomNavigationView`. + +## Installation + +To use this navigator, ensure that you have [`@react-navigation/native` and its dependencies (follow this guide)](getting-started.md), then install [`@react-navigation/bottom-tabs`](https://github.com/react-navigation/react-navigation/tree/main/packages/bottom-tabs): + +```bash npm2yarn +npm install @react-navigation/bottom-tabs +``` + +The navigator requires React Native 0.79 or above is required. If you're using [Expo](https://expo.dev/), it requires SDK 53 or above. + +## Usage + +To use this navigator, import it from `@react-navigation/bottom-tabs/unstable`: + + + + +```js name="Bottom Tab Navigator" +import { createNativeBottomTabNavigator } from '@react-navigation/bottom-tabs/unstable'; + +const MyTabs = createNativeBottomTabNavigator({ + screens: { + Home: HomeScreen, + Profile: ProfileScreen, + }, +}); +``` + + + + +```js name="Bottom Tab Navigator" +import { createNativeBottomTabNavigator } from '@react-navigation/bottom-tabs/unstable'; + +const Tab = createNativeBottomTabNavigator(); + +function MyTabs() { + return ( + + + + + ); +} +``` + + + + +## Notes + +- Liquid Glass effect on iOS 26+ requires your app to be built with Xcode 26 or above. +- On Android, at most 5 tabs are supported. This is a limitation of the underlying native component. + +## API Definition + +### Props + +In addition to the [common props](navigator.md#configuration) shared by all navigators, the bottom tab navigator accepts the following additional props: + +#### `backBehavior` + +This controls what happens when `goBack` is called in the navigator. This includes pressing the device's back button or back gesture on Android. + +It supports the following values: + +- `firstRoute` - return to the first screen defined in the navigator (default) +- `initialRoute` - return to initial screen passed in `initialRouteName` prop, if not passed, defaults to the first screen +- `order` - return to screen defined before the focused screen +- `history` - return to last visited screen in the navigator; if the same screen is visited multiple times, the older entries are dropped from the history +- `fullHistory` - return to last visited screen in the navigator; doesn't drop duplicate entries unlike `history` - this behavior is useful to match how web pages work +- `none` - do not handle back button + +### Options + +The following [options](screen-options.md) can be used to configure the screens in the navigator. These can be specified under `screenOptions` prop of `Tab.Navigator` or `options` prop of `Tab.Screen`. + +#### `title` + +Generic title that can be used as a fallback for `headerTitle` and `tabBarLabel`. + +#### `tabBarSystemItem` + +Uses iOS built-in tab bar items with standard iOS styling and localized titles. Supported values: + +- `bookmarks` +- `contacts` +- `downloads` +- `favorites` +- `featured` +- `history` +- `more` +- `mostRecent` +- `mostViewed` +- `recents` +- `search` +- `topRated` + +The [`tabBarIcon`](#tabbaricon) and [`tabBarLabel`](#tabbarlabel) options will override the icon and label from the system item. If you want to keep the system behavior on iOS, but need to provide icon and label for other platforms, use `Platform.OS` or `Platform.select` to conditionally set `undefined` for `tabBarIcon` and `tabBarLabel` on iOS. + +##### Search tab on iOS 26+ + +The `tabBarSystemItem` option has special styling and behavior when set to `search` on iOS 26+. + +Additionally, when the `search` tab is selected, the tab bar transforms into a search field if the screen in the tab navigator or a nested [native stack navigator](native-stack-navigator.md) has [`headerSearchBarOptions`](native-stack-navigator.md#headersearchbaroptions) configured and the native header is shown with [`headerShown: true`](native-stack-navigator.md#headershown). This won't work if a custom header is provided with the `header` option. + +Example: + +```js +tabBarSystemItem: 'search', +headerShown: true, +headerSearchBarOptions: { + placeholder: 'Search', +}, +``` + + + +#### `tabBarLabel` + +Title string of a tab displayed in the tab bar. + +Overrides the label provided by [`tabBarSystemItem`](#tabbarsystemitem) on iOS. + +If not provided, or set to `undefined`: + +- The system values are used if [`tabBarSystemItem`](#tabbarsystemitem) is set on iOS. +- Otherwise, it falls back to the [`title`](#title) or route name. + +#### `tabBarLabelVisibilityMode` + +The label visibility mode for the tab bar items. Supported values: + +- `auto` - the system decides when to show or hide labels +- `selected` - labels are shown only for the selected tab +- `labeled` - labels are always shown +- `unlabeled` - labels are never shown + +Only supported on Android. + +#### `tabBarLabelStyle` + +Style object for the tab label. Supported properties: + +- `fontFamily` +- `fontSize` +- `fontWeight` +- `fontStyle` + +Example: + +```js +tabBarLabelStyle: { + fontSize: 16, + fontFamily: 'Georgia', + fontWeight: 300, +}, +``` + +#### `tabBarIcon` + +Icon to display for the tab. It overrides the icon provided by [`tabBarSystemItem`](#tabbarsystemitem) on iOS. + +It can be an icon object or a function that given `{ focused: boolean, color: string, size: number }` returns an icon object. + +The icon can be of following types: + +- Local image - Supported on iOS and Android + + ```js + tabBarIcon: { + type: 'image', + source: require('./path/to/icon.png'), + } + ``` + + On iOS, you can additionally pass a `tinted` property to control whether the icon should be tinted with the active/inactive color: + + ```js + tabBarIcon: { + type: 'image', + source: require('./path/to/icon.png'), + tinted: false, + } + ``` + + The image is tinted by default. + +- [SF Symbols](https://developer.apple.com/sf-symbols/) name - Supported on iOS + + ```js + tabBarIcon: { + type: 'sfSymbol', + name: 'heart', + } + ``` + +- [Drawable resource](https://developer.android.com/guide/topics/resources/drawable-resource) name - Supported on Android + + ```js + tabBarIcon: { + type: 'drawableResource', + name: 'sunny', + } + ``` + +To render different icons for active and inactive states, you can use a function: + +```js +tabBarIcon: ({ focused }) => { + return { + type: 'sfSymbol', + name: focused ? 'heart' : 'heart-outline', + }; +}, +``` + +This is only supported on iOS. On Android, the icon specified for inactive state will be used for both active and inactive states. + +To provide different icons for different platforms, you can use [`Platform.select`](https://reactnative.dev/docs/platform-specific-code): + +```js +tabBarIcon: Platform.select({ + ios: { + type: 'sfSymbol', + name: 'heart', + }, + android: { + type: 'drawableResource', + name: 'heart_icon', + }, +}); +``` + +#### `tabBarBadge` + +Text to show in a badge on the tab icon. Accepts a `string` or a `number`. + +#### `tabBarBadgeStyle` + +Style for the badge on the tab icon. Supported properties: + +- `backgroundColor` +- `color` + +Example: + +```js +tabBarBadgeStyle: { + backgroundColor: 'yellow', + color: 'black', +}, +``` + +Only supported on Android. + +#### `tabBarActiveTintColor` + +Color for the icon and label in the active tab. + +#### `tabBarInactiveTintColor` + +Color for the icon and label in the inactive tabs. + +Only supported on Android. + +#### `tabBarActiveIndicatorColor` + +Background color of the active indicator. + +Only supported on Android. + +#### `tabBarActiveIndicatorEnabled` + +Whether the active indicator should be used. Defaults to `true`. + +Only supported on Android. + +#### `tabBarRippleColor` + +Color of the ripple effect when pressing a tab. + +Only supported on Android. + +#### `tabBarStyle` + +Style object for the tab bar. Supported properties: + +- `backgroundColor` - Only supported on Android and iOS 18 and below. +- `shadowColor` - Only supported on iOS 18 and below. + +On iOS 26+, the background color automatically changes based on the content behind the tab bar and can't be overridden. + +#### `tabBarBlurEffect` + +Blur effect applied to the tab bar on iOS 18 and lower when tab screen is selected. + +Supported values: + +- `none` - no blur effect +- `systemDefault` - default blur effect applied by the system +- `extraLight` +- `light` +- `dark` +- `regular` +- `prominent` +- `systemUltraThinMaterial` +- `systemThinMaterial` +- `systemMaterial` +- `systemThickMaterial` +- `systemChromeMaterial` +- `systemUltraThinMaterialLight` +- `systemThinMaterialLight` +- `systemMaterialLight` +- `systemThickMaterialLight` +- `systemChromeMaterialLight` +- `systemUltraThinMaterialDark` +- `systemThinMaterialDark` +- `systemMaterialDark` +- `systemThickMaterialDark` +- `systemChromeMaterialDark` + +Defaults to `systemDefault`. + +Only supported on iOS 18 and below. + +#### `tabBarControllerMode` + +The display mode for the tab bar. Supported values: + +- `auto` - the system sets the display mode based on the tab’s content +- `tabBar` - the system displays the content only as a tab bar +- `tabSidebar` - the tab bar is displayed as a sidebar + +Only supported on iOS 18 and above. Not supported on tvOS. + +#### `tabBarMinimizeBehavior` + +The minimize behavior for the tab bar. Supported values: + +- `auto` - resolves to the system default minimize behavior +- `never` - the tab bar does not minimize +- `onScrollDown` - the tab bar minimizes when scrolling down and + expands when scrolling back up +- `onScrollUp` - the tab bar minimizes when scrolling up and expands + when scrolling back down + +Only supported on iOS 26 and above. + + + +#### `lazy` + +Whether this screen should render only after the first time it's accessed. Defaults to `true`. Set it to `false` if you want to render the screen on the initial render of the navigator. + +#### `popToTopOnBlur` + +Boolean indicating whether any nested stack should be popped to the top of the stack when navigating away from this tab. Defaults to `false`. + +It only works when there is a stack navigator (e.g. [stack navigator](stack-navigator.md) or [native stack navigator](native-stack-navigator.md)) nested under the tab navigator. + +### Header related options + +The navigator does not show a header by default. It renders a native stack header if `headerShown` is set to `true` in the screen options explicitly, or if a custom header is provided with the `header` option. Header related options require a header to be shown. + +It supports most of the [header related options supported in `@react-navigation/native-stack`](native-stack-navigator.md#header-related-options) apart from the options related to the back button (prefixed with `headerBack`). + +### Events + +The navigator can [emit events](navigation-events.md) on certain actions. Supported events are: + +#### `tabPress` + +This event is fired when the user presses the tab button for the current screen in the tab bar. By default a tab press does several things: + +- If the tab is not focused, tab press will focus that tab +- If the tab is already focused: + - If the screen for the tab renders a scroll view, you can use [`useScrollToTop`](use-scroll-to-top.md) to scroll it to top + - If the screen for the tab renders a stack navigator, a `popToTop` action is performed on the stack + +The default behavior of the tab press is controlled natively and cannot be prevented. + +```js +React.useEffect(() => { + const unsubscribe = navigation.addListener('tabPress', (e) => { + // Do something manually + // ... + }); + + return unsubscribe; +}, [navigation]); +``` + +#### `transitionStart` + +This event is fired when the transition animation starts for the current screen. + +Example: + +```js +React.useEffect(() => { + const unsubscribe = navigation.addListener('transitionStart', (e) => { + // Do something + }); + + return unsubscribe; +}, [navigation]); +``` + +#### `transitionEnd` + +This event is fired when the transition animation ends for the current screen. + +Example: + +```js +React.useEffect(() => { + const unsubscribe = navigation.addListener('transitionEnd', (e) => { + // Do something + }); + + return unsubscribe; +}, [navigation]); +``` + +### Helpers + +The tab navigator adds the following methods to the navigation object: + +#### `jumpTo` + +Navigates to an existing screen in the tab navigator. The method accepts following arguments: + +- `name` - _string_ - Name of the route to jump to. +- `params` - _object_ - Screen params to use for the destination route. + +```js +navigation.jumpTo('Profile', { owner: 'Michaś' }); +``` diff --git a/versioned_docs/version-7.x/native-stack-navigator.md b/versioned_docs/version-7.x/native-stack-navigator.md index 57991096c0f..f1e12a971e2 100755 --- a/versioned_docs/version-7.x/native-stack-navigator.md +++ b/versioned_docs/version-7.x/native-stack-navigator.md @@ -158,1145 +158,1224 @@ The following [options](screen-options.md) can be used to configure the screens String that can be used as a fallback for `headerTitle`. -#### `headerBackButtonMenuEnabled` - -Boolean indicating whether to show the menu on longPress of iOS >= 14 back button. Defaults to `true`. +#### `statusBarAnimation` -Only supported on iOS. +Sets the status bar animation (similar to the `StatusBar` component). Defaults to `fade` on iOS and `none` on Android. -Header back button menu enabled +Supported values: -#### `headerBackVisible` +- `"fade"` +- `"none"` +- `"slide"` -Whether the back button is visible in the header. You can use it to show a back button alongside `headerLeft` if you have specified it. +On Android, setting either `fade` or `slide` will set the transition of status bar color. On iOS, this option applies to appereance animation of the status bar. -This will have no effect on the first screen in the stack. +Requires setting `View controller-based status bar appearance -> YES` (or removing the config) in your `Info.plist` file. -#### `headerBackTitle` +Only supported on Android and iOS. -Title string used by the back button on iOS. Defaults to the previous scene's title, "Back" or arrow icon depending on the available space. See `headerBackButtonDisplayMode` to read about limitations and customize the behavior. +#### `statusBarHidden` -Use `headerBackButtonDisplayMode: "minimal"` to hide it. +Whether the status bar should be hidden on this screen. -Only supported on iOS. +Requires setting `View controller-based status bar appearance -> YES` (or removing the config) in your `Info.plist` file. -Header back title +Only supported on Android and iOS. -#### `headerBackButtonDisplayMode` +#### `statusBarStyle` -How the back button displays icon and title. +Sets the status bar color (similar to the `StatusBar` component). Supported values: -- "default" - Displays one of the following depending on the available space: previous screen's title, generic title (e.g. 'Back') or no title (only icon). -- "generic" – Displays one of the following depending on the available space: generic title (e.g. 'Back') or no title (only icon). -- "minimal" – Always displays only the icon without a title. +- `"auto"` (iOS only) +- `"inverted"` (iOS only) +- `"dark"` +- `"light"` -The space-aware behavior is disabled when: +Defaults to `auto` on iOS and `light` on Android. -- The iOS version is 13 or lower -- Custom font family or size is set (e.g. with `headerBackTitleStyle`) -- Back button menu is disabled (e.g. with `headerBackButtonMenuEnabled`) +Requires setting `View controller-based status bar appearance -> YES` (or removing the config) in your `Info.plist` file. -In such cases, a static title and icon are always displayed. +Only supported on Android and iOS. -Only supported on iOS. +#### `statusBarBackgroundColor` -#### `headerBackTitleStyle` +:::warning -Style object for header back title. Supported properties: +This option is deprecated and will be removed in a future release (for apps targeting Android SDK 35 or above edge-to-edge mode is enabled by default +and it is expected that the edge-to-edge will be enforced in future SDKs, see [here](https://developer.android.com/about/versions/15/behavior-changes-15#ux) for more information). -- `fontFamily` -- `fontSize` +::: -Only supported on iOS. +Sets the background color of the status bar (similar to the `StatusBar` component). -Header back title style +Only supported on Android. -Example: +#### `statusBarTranslucent` -```js - headerBackTitleStyle: { - fontSize: 14, - fontFamily: 'Georgia', - }, -``` +:::warning -#### `headerBackImageSource` +This option is deprecated and will be removed in a future release (for apps targeting Android SDK 35 or above edge-to-edge mode is enabled by default +and it is expected that the edge-to-edge will be enforced in future SDKs, see [here](https://developer.android.com/about/versions/15/behavior-changes-15#ux) for more information). -Image to display in the header as the icon in the back button. Defaults to back icon image for the platform +::: -- A chevron on iOS -- An arrow on Android +Sets the translucency of the status bar (similar to the `StatusBar` component). Defaults to `false`. -#### `headerLargeStyle` +Only supported on Android. -Style of the header when a large title is shown. The large title is shown if `headerLargeTitle` is `true` and the edge of any scrollable content reaches the matching edge of the header. +#### `contentStyle` -Supported properties: +Style object for the scene content. -- backgroundColor +#### `animationMatchesGesture` -Only supported on iOS. +Whether the gesture to dismiss should use animation provided to `animation` prop. Defaults to `false`. - +Only supported on iOS. -#### `headerLargeTitle` +#### `fullScreenGestureEnabled` -Whether to enable header with large title which collapses to regular header on scroll. -Defaults to `false`. +Whether the gesture to dismiss should work on the whole screen. Using gesture to dismiss with this option results in the same transition animation as `simple_push`. This behavior can be changed by setting `customAnimationOnGesture` prop. Achieving the default iOS animation isn't possible due to platform limitations. Defaults to `false`. -For large title to collapse on scroll, the content of the screen should be wrapped in a scrollable view such as `ScrollView` or `FlatList`. If the scrollable area doesn't fill the screen, the large title won't collapse on scroll. You also need to specify `contentInsetAdjustmentBehavior="automatic"` in your `ScrollView`, `FlatList` etc. +Doesn't affect the behavior of screens presented modally. Only supported on iOS. - - -#### `headerLargeTitleShadowVisible` +#### `fullScreenGestureShadowEnabled` -Whether drop shadow of header is visible when a large title is shown. +Whether the full screen dismiss gesture has shadow under view during transition. Defaults to `true`. -#### `headerLargeTitleStyle` +This does not affect the behavior of transitions that don't use gestures enabled by `fullScreenGestureEnabled` prop. -Style object for large title in header. Supported properties: +#### `gestureEnabled` -- `fontFamily` -- `fontSize` -- `fontWeight` -- `color` +Whether you can use gestures to dismiss this screen. Defaults to `true`. Only supported on iOS. -Only supported on iOS. +#### `animationTypeForReplace` -Header large title style +The type of animation to use when this screen replaces another screen. Defaults to `push`. -Example: +Supported values: -```js - headerLargeTitleStyle: { - fontFamily: 'Georgia', - fontSize: 22, - fontWeight: '500', - color: 'blue', - }, -``` +- `push`: the new screen will perform push animation. -#### `headerShown` + -Whether to show the header. The header is shown by default. Setting this to `false` hides the header. +- `pop`: the new screen will perform pop animation. -#### `headerStyle` + -Style object for header. Supported properties: +#### `animation` -- `backgroundColor` +How the screen should animate when pushed or popped. - +Supported values: -#### `headerShadowVisible` +- `default`: use the platform default animation + -Whether to hide the elevation shadow (Android) or the bottom border (iOS) on the header. +- `fade`: fade screen in or out + -Android: -Header shadow visible Android +- `fade_from_bottom`: fade the new screen from bottom + -iOS: -Header shadow visible iOS +- `flip`: flip the screen, requires `presentation: "modal"` (iOS only) + -#### `headerTransparent` +- `simple_push`: default animation, but without shadow and native header transition (iOS only, uses default animation on Android) + -Boolean indicating whether the navigation bar is translucent. +- `slide_from_bottom`: slide in the new screen from bottom + -Defaults to `false`. Setting this to `true` makes the header absolutely positioned - so that the header floats over the screen so that it overlaps the content underneath, and changes the background color to `transparent` unless specified in `headerStyle`. +- `slide_from_right`: slide in the new screen from right (Android only, uses default animation on iOS) + -This is useful if you want to render a semi-transparent header or a blurred background. +- `slide_from_left`: slide in the new screen from left (Android only, uses default animation on iOS) + -Note that if you don't want your content to appear under the header, you need to manually add a top margin to your content. React Navigation won't do it automatically. +- `none`: don't animate the screen + -To get the height of the header, you can use [`HeaderHeightContext`](elements.md#headerheightcontext) with [React's Context API](https://react.dev/reference/react/useContext#contextconsumer) or [`useHeaderHeight`](elements.md#useheaderheight). +#### `presentation` -#### `headerBlurEffect` +How should the screen be presented. -Blur effect for the translucent header. The `headerTransparent` option needs to be set to `true` for this to work. +Only supported on Android and iOS. Supported values: -- `extraLight` +- `card`: the new screen will be pushed onto a stack, which means the default animation will be slide from the side on iOS, the animation on Android will vary depending on the OS version and theme. + -- `light` - Header blur effect light +- `modal`: the new screen will be presented modally. this also allows for a nested stack to be rendered inside the screen. + -- `dark` - Header blur effect dark +- `transparentModal`: the new screen will be presented modally, but in addition, the previous screen will stay so that the content below can still be seen if the screen has translucent background. + -- `regular` - Header blur effect regular +- `containedModal`: will use "UIModalPresentationCurrentContext" modal style on iOS and will fallback to "modal" on Android. + -- `prominent` - Header blur effect systemUltraThinMaterial +- `containedTransparentModal`: will use "UIModalPresentationOverCurrentContext" modal style on iOS and will fallback to "transparentModal" on Android. + -- `systemUltraThinMaterial` - Header blur effect systemUltraThinMaterial +- `fullScreenModal`: will use "UIModalPresentationFullScreen" modal style on iOS and will fallback to "modal" on Android. A screen using this presentation style can't be dismissed by gesture. + -- `systemThinMaterial` - Header blur effect systemThinMaterial +- `formSheet`: will use "BottomSheetBehavior" on Android and "UIModalPresentationFormSheet" modal style on iOS. + + -- `systemMaterial` - Header blur effect systemMaterial +##### Using Form Sheet -- `systemThickMaterial` - Header blur effect systemThickMaterial +To use Form Sheet for your screen, add `presentation: 'formSheet'` to the `options`. -- `systemChromeMaterial` - Header blur effect systemChromeMaterial + + -- `systemUltraThinMaterialLight` - Header blur effect systemUltraThinMaterialLight +```js name="Form Sheet" snack +import * as React from 'react'; +import { Text, View } from 'react-native'; +import { + createStaticNavigation, + useNavigation, +} from '@react-navigation/native'; +import { Button } from '@react-navigation/elements'; -- `systemThinMaterialLight` - Header blur effect systemThinMaterialLight +// codeblock-focus-start +import { createNativeStackNavigator } from '@react-navigation/native-stack'; -- `systemMaterialLight` - Header blur effect systemMaterialLight +// codeblock-focus-end -- `systemThickMaterialLight` - Header blur effect systemThickMaterialLight +function HomeScreen() { + const navigation = useNavigation(); -- `systemChromeMaterialLight` - Header blur effect systemChromeMaterialLight + return ( + + Home Screen + + + ); +} -- `systemUltraThinMaterialDark` - Header blur effect systemUltraThinMaterialDark -- `systemThinMaterialDark` - Header blur effect systemThinMaterialDark +function ProfileScreen() { + const navigation = useNavigation(); -- `systemMaterialDark` - Header blur effect systemMaterialDark -- `systemThickMaterialDark` - Header blur effect systemThickMaterialDark + return ( + + Profile Screen + + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam accumsan + euismod enim, quis porta ligula egestas sed. Maecenas vitae consequat + odio, at dignissim lorem. Ut euismod eros ac mi ultricies, vel pharetra + tortor commodo. Interdum et malesuada fames ac ante ipsum primis in + faucibus. Nullam at urna in metus iaculis aliquam at sed quam. In + ullamcorper, ex ut facilisis commodo, urna diam posuere urna, at + condimentum mi orci ac ipsum. In hac habitasse platea dictumst. Donec + congue pharetra ipsum in finibus. Nulla blandit finibus turpis, non + vulputate elit viverra a. Curabitur in laoreet nisl. + + + + ); +} -- `systemChromeMaterialDark` - Header blur effect systemChromeMaterialDark +// codeblock-focus-start +const MyStack = createNativeStackNavigator({ + screens: { + Home: { + screen: HomeScreen, + }, + Profile: { + screen: ProfileScreen, + options: { + presentation: 'formSheet', + headerShown: false, + sheetAllowedDetents: 'fitToContents', + }, + }, + }, +}); +// codeblock-focus-end -Only supported on iOS. +const Navigation = createStaticNavigation(MyStack); -#### `headerBackground` +export default function App() { + return ; +} +``` -Function which returns a React Element to render as the background of the header. This is useful for using backgrounds such as an image or a gradient. + + - Header background +```js name="Form Sheet" snack +import * as React from 'react'; +import { Text, View } from 'react-native'; +import { NavigationContainer, useNavigation } from '@react-navigation/native'; +import { Button } from '@react-navigation/elements'; +// codeblock-focus-start +import { createNativeStackNavigator } from '@react-navigation/native-stack'; -Example: +const Stack = createNativeStackNavigator(); -```js - headerBackground: () => ( - + + - ), +
+ ); +} +// codeblock-focus-end + +function HomeScreen() { + const navigation = useNavigation(); + + return ( + + Home Screen + + + ); +} + +function ProfileScreen() { + const navigation = useNavigation(); + + return ( + + Profile Screen + + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam accumsan + euismod enim, quis porta ligula egestas sed. Maecenas vitae consequat + odio, at dignissim lorem. Ut euismod eros ac mi ultricies, vel pharetra + tortor commodo. Interdum et malesuada fames ac ante ipsum primis in + faucibus. Nullam at urna in metus iaculis aliquam at sed quam. In + ullamcorper, ex ut facilisis commodo, urna diam posuere urna, at + condimentum mi orci ac ipsum. In hac habitasse platea dictumst. Donec + congue pharetra ipsum in finibus. Nulla blandit finibus turpis, non + vulputate elit viverra a. Curabitur in laoreet nisl. + + + + ); +} + +export default function App() { + return ( + + + + ); +} ``` -#### `headerTintColor` + + -Tint color for the header. Changes the color of back button and title. +:::warning - Header tint color +Due to technical issues in platform component integration with `react-native`, `presentation: 'formSheet'` has limited support for `flex: 1`. -#### `headerLeft` +On Android, using `flex: 1` on a top-level content container passed to a `formSheet` with `showAllowedDetents: 'fitToContents'` causes the sheet to not display at all, leaving only the dimmed background visible. This is because it is the sheet, not the parent who is source of the size. Setting fixed values for `sheetAllowedDetents`, e.g. `[0.4, 0.9]`, works correctly (content is aligned for the highest detent). -Function which returns a React Element to display on the left side of the header. This replaces the back button. See `headerBackVisible` to show the back button along side left element. +On iOS, `flex: 1` with `showAllowedDetents: 'fitToContents'` works properly but setting a fixed value for `showAllowedDetents` causes the screen to not respect the `flex: 1` style - the height of the container does not fill the `formSheet` fully, but rather inherits intrinsic size of its contents. This tradeoff is _currently_ necessary to prevent ["sheet flickering" problem on iOS](https://github.com/software-mansion/react-native-screens/issues/1722). -Header right +If you don't use `flex: 1` but the content's height is less than max screen height, the rest of the sheet might become translucent or use the default theme background color (you can see this happening on the screenshots in the descrption of [this PR](https://github.com/software-mansion/react-native-screens/pull/2462)). To match the sheet to the background of your content, set `backgroundColor` in the `contentStyle` prop of the given screen. -Example: +On Android, there are also some problems with getting nested ScrollViews to work properly. The solution is to set `nestedScrollEnabled` on the `ScrollView`, but this does not work if the content's height is less than the `ScrollView`'s height. Please see [this PR](https://github.com/facebook/react-native/pull/44099) for details and suggested [workaround](https://github.com/facebook/react-native/pull/44099#issuecomment-2058469661). -```js - headerLeft: () => ( - - ), - headerBackVisible: true, - headerBackTitle: 'Back', -``` +On Android, nested stack and `headerShown` prop are not currently supported for screens with `presentation: 'formSheet'`. -#### `headerRight` +::: -Function which returns a React Element to display on the right side of the header. +#### `sheetAllowedDetents` - Header right +:::note -Example: +Works only when `presentation` is set to `formSheet`. -```js -headerRight: () => ; -``` +::: -#### `headerTitle` + -String or a function that returns a React Element to be used by the header. Defaults to `title` or name of the screen. +Describes heights where a sheet can rest. -When a function is passed, it receives `tintColor` and`children` in the options object as an argument. The title string is passed in `children`. +Supported values: -Note that if you render a custom element by passing a function, animations for the title won't work. +- `fitToContents` - intents to set the sheet height to the height of its contents. +- Array of fractions, e.g. `[0.25, 0.5, 0.75]`: + - Heights should be described as fraction (a number from `[0, 1]` interval) of screen height / maximum detent height. + - The array **must** be sorted in ascending order. This invariant is verified only in developement mode, where violation results in error. + - iOS accepts any number of detents, while **Android is limited to three** - any surplus values, beside first three are ignored. -#### `headerTitleAlign` +Defaults to `[1.0]`. -How to align the header title. Possible values: +Only supported on Android and iOS. -- `left` - Header title align left +#### `sheetElevation` -- `center` - Header title align center +:::note -Defaults to `left` on platforms other than iOS. +Works only when `presentation` is set to `formSheet`. + +::: + + + +Integer value describing elevation of the sheet, impacting shadow on the top edge of the sheet. + +Not dynamic - changing it after the component is rendered won't have an effect. + +Defaults to `24`. + +Only supported on Android. + +#### `sheetExpandsWhenScrolledToEdge` + +:::note + +Works only when `presentation` is set to `formSheet`. + +::: + + + +Whether the sheet should expand to larger detent when scrolling. + +Defaults to `true`. + +Only supported on iOS. + +:::warning + +Please note that for this interaction to work, the ScrollView must be "first-subview-chain" descendant of the Screen component. This restriction is due to platform requirements. + +::: + +#### `sheetCornerRadius` + +:::note + +Works only when `presentation` is set to `formSheet`. + +::: + + + +The corner radius that the sheet will try to render with. -Not supported on iOS. It's always `center` on iOS and cannot be changed. +If set to non-negative value it will try to render sheet with provided radius, else it will apply system default. -#### `headerTitleStyle` +If left unset, system default is used. -Style object for header title. Supported properties: +Only supported on Android and iOS. -- `fontFamily` -- `fontSize` -- `fontWeight` -- `color` +#### `sheetInitialDetentIndex` - Header title style +:::note -Example: +Works only when `presentation` is set to `formSheet`. -```js - headerTitleStyle: { - color: 'blue', - fontSize: 22, - fontFamily: 'Georgia', - fontWeight: 300, - }, -``` +::: -#### `headerSearchBarOptions` + -Options to render a native search bar on iOS. Search bars are rarely static so normally it is controlled by passing an object to `headerSearchBarOptions` navigation option in the component's body. +**Index** of the detent the sheet should expand to after being opened. -You also need to specify `contentInsetAdjustmentBehavior="automatic"` in your `ScrollView`, `FlatList` etc. If you don't have a `ScrollView`, specify `headerTransparent: false`. +If the specified index is out of bounds of `sheetAllowedDetents` array, in dev environment more errors will be thrown, in production the value will be reset to default value. -Example: +Additionaly there is `last` value available, when set the sheet will expand initially to last (largest) detent. -```js -React.useLayoutEffect(() => { - navigation.setOptions({ - headerSearchBarOptions: { - // search bar options - }, - }); -}, [navigation]); -``` +Defaults to `0` - which represents first detent in the detents array. -Supported properties are: +Only supported on Android and iOS. -##### `ref` +#### `sheetGrabberVisible` -Ref to manipulate the search input imperatively. It contains the following methods: +:::note -- `focus` - focuses the search bar -- `blur` - removes focus from the search bar -- `setText` - sets the search bar's content to given value -- `clearText` - removes any text present in the search bar input field -- `cancelSearch` - cancel the search and close the search bar +Works only when `presentation` is set to `formSheet`. -##### `autoCapitalize` +::: -Controls whether the text is automatically auto-capitalized as it is entered by the user. -Possible values: + -- `none` -- `words` -- `sentences` -- `characters` +Boolean indicating whether the sheet shows a grabber at the top. -Defaults to `sentences`. +Defaults to `false`. -##### `autoFocus` +Only supported on iOS. -Whether to automatically focus search bar when it's shown. Defaults to `false`. +#### `sheetLargestUndimmedDetentIndex` -Only supported on Android. +:::note -##### `barTintColor` +Works only when `presentation` is set to `formSheet`. -The search field background color. By default bar tint color is translucent. +::: -Only supported on iOS. + -Header search bar options - Bar tint color +The largest sheet detent for which a view underneath won't be dimmed. -##### `tintColor` +This prop can be set to an number, which indicates index of detent in `sheetAllowedDetents` array for which there won't be a dimming view beneath the sheet. -The color for the cursor caret and cancel button text. +Additionaly there are following options available: -Only supported on iOS. +- `none` - there will be dimming view for all detents levels, +- `last` - there won't be a dimming view for any detent level. -Header search bar options - Tint color +Defaults to `none`, indicating that the dimming view should be always present. -##### `cancelButtonText` +Only supported on Android and iOS. -The text to be used instead of default `Cancel` button text. +#### `orientation` -Only supported on iOS. +The display orientation to use for the screen. -##### `disableBackButtonOverride` +Supported values: -Whether the back button should close search bar's text input or not. Defaults to `false`. +- `default` - resolves to "all" without "portrait_down" on iOS. On Android, this lets the system decide the best orientation. +- `all`: all orientations are permitted. +- `portrait`: portrait orientations are permitted. +- `portrait_up`: right-side portrait orientation is permitted. +- `portrait_down`: upside-down portrait orientation is permitted. +- `landscape`: landscape orientations are permitted. +- `landscape_left`: landscape-left orientation is permitted. +- `landscape_right`: landscape-right orientation is permitted. -Only supported on Android. +Only supported on Android and iOS. -##### `hideNavigationBar` +#### `autoHideHomeIndicator` -Boolean indicating whether to hide the navigation bar during searching. Defaults to `true`. +Boolean indicating whether the home indicator should prefer to stay hidden. Defaults to `false`. Only supported on iOS. -##### `hideWhenScrolling` +#### `gestureDirection` -Boolean indicating whether to hide the search bar when scrolling. Defaults to `true`. +Sets the direction in which you should swipe to dismiss the screen. -Only supported on iOS. +Supported values: -##### `inputType` +- `vertical` – dismiss screen vertically +- `horizontal` – dismiss screen horizontally (default) -The type of the input. Defaults to `"text"`. +When using `vertical` option, options `fullScreenGestureEnabled: true`, `customAnimationOnGesture: true` and `animation: 'slide_from_bottom'` are set by default. -Supported values: +Only supported on iOS. -- `"text"` -- `"phone"` -- `"number"` -- `"email"` +#### `animationDuration` -Only supported on Android. +Changes the duration (in milliseconds) of `slide_from_bottom`, `fade_from_bottom`, `fade` and `simple_push` transitions on iOS. Defaults to `350`. -##### `obscureBackground` +The duration of `default` and `flip` transitions isn't customizable. -Boolean indicating whether to obscure the underlying content with semi-transparent overlay. Defaults to `true`. +Only supported on iOS. -##### `placeholder` +#### `navigationBarColor` -Text displayed when search field is empty. +:::warning -##### `textColor` +This option is deprecated and will be removed in a future release (for apps targeting Android SDK 35 or above edge-to-edge mode is enabled by default +and it is expected that the edge-to-edge will be enforced in future SDKs, see [here](https://developer.android.com/about/versions/15/behavior-changes-15#ux) for more information). -The color of the text in the search field. +::: -Header search bar options - Text color +Sets the navigation bar color. Defaults to initial status bar color. -##### `hintTextColor` +Only supported on Android. -The color of the hint text in the search field. +#### `navigationBarHidden` + +Boolean indicating whether the navigation bar should be hidden. Defaults to `false`. Only supported on Android. -Header search bar options - Hint text color +#### `freezeOnBlur` -##### `headerIconColor` +Boolean indicating whether to prevent inactive screens from re-rendering. Defaults to `false`. +Defaults to `true` when `enableFreeze()` from `react-native-screens` package is run at the top of the application. -The color of the search and close icons shown in the header +Only supported on iOS and Android. -Only supported on Android. +### Header related options -Header search bar options - Header icon color +The navigator supports following options to configure the header: -##### `shouldShowHintSearchIcon` +#### `headerBackButtonMenuEnabled` -Whether to show the search hint icon when search bar is focused. Defaults to `true`. +Boolean indicating whether to show the menu on longPress of iOS >= 14 back button. Defaults to `true`. -Only supported on Android. +Only supported on iOS. -##### `onBlur` +Header back button menu enabled -A callback that gets called when search bar has lost focus. +#### `headerBackVisible` -##### `onCancelButtonPress` +Whether the back button is visible in the header. You can use it to show a back button alongside `headerLeft` if you have specified it. -A callback that gets called when the cancel button is pressed. +This will have no effect on the first screen in the stack. -##### `onChangeText` +#### `headerBackTitle` -A callback that gets called when the text changes. It receives the current text value of the search bar. +Title string used by the back button on iOS. Defaults to the previous scene's title, "Back" or arrow icon depending on the available space. See `headerBackButtonDisplayMode` to read about limitations and customize the behavior. -Example: +Use `headerBackButtonDisplayMode: "minimal"` to hide it. -```js -const [search, setSearch] = React.useState(''); +Only supported on iOS. -React.useLayoutEffect(() => { - navigation.setOptions({ - headerSearchBarOptions: { - onChangeText: (event) => setSearch(event.nativeEvent.text), - }, - }); -}, [navigation]); -``` +Header back title -#### `header` +#### `headerBackButtonDisplayMode` -Custom header to use instead of the default header. +How the back button displays icon and title. -This accepts a function that returns a React Element to display as a header. The function receives an object containing the following properties as the argument: +Supported values: -- `navigation` - The navigation object for the current screen. -- `route` - The route object for the current screen. -- `options` - The options for the current screen -- `back` - Options for the back button, contains an object with a `title` property to use for back button label. +- "default" - Displays one of the following depending on the available space: previous screen's title, generic title (e.g. 'Back') or no title (only icon). +- "generic" – Displays one of the following depending on the available space: generic title (e.g. 'Back') or no title (only icon). +- "minimal" – Always displays only the icon without a title. -Example: +The space-aware behavior is disabled when: -```js -import { getHeaderTitle } from '@react-navigation/elements'; +- The iOS version is 13 or lower +- Custom font family or size is set (e.g. with `headerBackTitleStyle`) +- Back button menu is disabled (e.g. with `headerBackButtonMenuEnabled`) -// .. +In such cases, a static title and icon are always displayed. -header: ({ navigation, route, options, back }) => { - const title = getHeaderTitle(options, route.name); +Only supported on iOS. - return ( - : undefined - } - style={options.headerStyle} - /> - ); -}; -``` +#### `headerBackTitleStyle` -To set a custom header for all the screens in the navigator, you can specify this option in the `screenOptions` prop of the navigator. +Style object for header back title. Supported properties: -Note that if you specify a custom header, the native functionality such as large title, search bar etc. won't work. +- `fontFamily` +- `fontSize` -#### `statusBarAnimation` +Only supported on iOS. -Sets the status bar animation (similar to the `StatusBar` component). Defaults to `fade` on iOS and `none` on Android. +Header back title style -Supported values: +Example: -- `"fade"` -- `"none"` -- `"slide"` +```js + headerBackTitleStyle: { + fontSize: 14, + fontFamily: 'Georgia', + }, +``` -On Android, setting either `fade` or `slide` will set the transition of status bar color. On iOS, this option applies to appereance animation of the status bar. +#### `headerBackImageSource` -Requires setting `View controller-based status bar appearance -> YES` (or removing the config) in your `Info.plist` file. +Image to display in the header as the icon in the back button. Defaults to back icon image for the platform -Only supported on Android and iOS. +- A chevron on iOS +- An arrow on Android -#### `statusBarHidden` +#### `headerLargeStyle` -Whether the status bar should be hidden on this screen. +Style of the header when a large title is shown. The large title is shown if `headerLargeTitle` is `true` and the edge of any scrollable content reaches the matching edge of the header. -Requires setting `View controller-based status bar appearance -> YES` (or removing the config) in your `Info.plist` file. +Supported properties: -Only supported on Android and iOS. +- backgroundColor -#### `statusBarStyle` +Only supported on iOS. -Sets the status bar color (similar to the `StatusBar` component). Defaults to `auto`. + -- `"auto"` -- `"inverted"` (iOS only) -- `"dark"` -- `"light"` +#### `headerLargeTitle` -Requires setting `View controller-based status bar appearance -> YES` (or removing the config) in your `Info.plist` file. +Whether to enable header with large title which collapses to regular header on scroll. +Defaults to `false`. -Only supported on Android and iOS. +For large title to collapse on scroll, the content of the screen should be wrapped in a scrollable view such as `ScrollView` or `FlatList`. If the scrollable area doesn't fill the screen, the large title won't collapse on scroll. You also need to specify `contentInsetAdjustmentBehavior="automatic"` in your `ScrollView`, `FlatList` etc. -#### `statusBarBackgroundColor` +Only supported on iOS. -:::warning + -::: +#### `headerLargeTitleShadowVisible` -Sets the background color of the status bar (similar to the `StatusBar` component). +Whether drop shadow of header is visible when a large title is shown. -Only supported on Android. +#### `headerLargeTitleStyle` -#### `statusBarTranslucent` +Style object for large title in header. Supported properties: -:::warning +- `fontFamily` +- `fontSize` +- `fontWeight` +- `color` -This option is deprecated and will be removed in a future release (for apps targeting Android SDK 35 or above edge-to-edge mode is enabled by default -and it is expected that the edge-to-edge will be enforced in future SDKs, see [here](https://developer.android.com/about/versions/15/behavior-changes-15#ux) for more information). +Only supported on iOS. -::: +Header large title style -Sets the translucency of the status bar (similar to the `StatusBar` component). Defaults to `false`. +Example: -Only supported on Android. +```js + headerLargeTitleStyle: { + fontFamily: 'Georgia', + fontSize: 22, + fontWeight: '500', + color: 'blue', + }, +``` -#### `contentStyle` +#### `headerStyle` -Style object for the scene content. +Style object for header. Supported properties: -#### `animationMatchesGesture` +- `backgroundColor` -Whether the gesture to dismiss should use animation provided to `animation` prop. Defaults to `false`. + -Only supported on iOS. +#### `headerShadowVisible` -#### `fullScreenGestureEnabled` +Whether to hide the elevation shadow (Android) or the bottom border (iOS) on the header. -Whether the gesture to dismiss should work on the whole screen. Using gesture to dismiss with this option results in the same transition animation as `simple_push`. This behavior can be changed by setting `customAnimationOnGesture` prop. Achieving the default iOS animation isn't possible due to platform limitations. Defaults to `false`. +Android: +Header shadow visible Android -Doesn't affect the behavior of screens presented modally. +iOS: +Header shadow visible iOS -Only supported on iOS. +#### `headerTransparent` -#### `fullScreenGestureShadowEnabled` +Boolean indicating whether the navigation bar is translucent. -Whether the full screen dismiss gesture has shadow under view during transition. Defaults to `true`. +Defaults to `false`. Setting this to `true` makes the header absolutely positioned - so that the header floats over the screen so that it overlaps the content underneath, and changes the background color to `transparent` unless specified in `headerStyle`. -This does not affect the behavior of transitions that don't use gestures enabled by `fullScreenGestureEnabled` prop. +This is useful if you want to render a semi-transparent header or a blurred background. -#### `gestureEnabled` +Note that if you don't want your content to appear under the header, you need to manually add a top margin to your content. React Navigation won't do it automatically. -Whether you can use gestures to dismiss this screen. Defaults to `true`. Only supported on iOS. +To get the height of the header, you can use [`HeaderHeightContext`](elements.md#headerheightcontext) with [React's Context API](https://react.dev/reference/react/useContext#contextconsumer) or [`useHeaderHeight`](elements.md#useheaderheight). -#### `animationTypeForReplace` +#### `headerBlurEffect` -The type of animation to use when this screen replaces another screen. Defaults to `push`. +Blur effect for the translucent header. The `headerTransparent` option needs to be set to `true` for this to work. Supported values: -- `push`: the new screen will perform push animation. +- `extraLight` - +- `light` + Header blur effect light -- `pop`: the new screen will perform pop animation. +- `dark` + Header blur effect dark - +- `regular` + Header blur effect regular -#### `animation` +- `prominent` + Header blur effect systemUltraThinMaterial -How the screen should animate when pushed or popped. +- `systemUltraThinMaterial` + Header blur effect systemUltraThinMaterial -Only supported on Android and iOS. +- `systemThinMaterial` + Header blur effect systemThinMaterial -Supported values: +- `systemMaterial` + Header blur effect systemMaterial -- `default`: use the platform default animation - +- `systemThickMaterial` + Header blur effect systemThickMaterial -- `fade`: fade screen in or out - +- `systemChromeMaterial` + Header blur effect systemChromeMaterial -- `fade_from_bottom`: fade the new screen from bottom - +- `systemUltraThinMaterialLight` + Header blur effect systemUltraThinMaterialLight -- `flip`: flip the screen, requires `presentation: "modal"` (iOS only) - +- `systemThinMaterialLight` + Header blur effect systemThinMaterialLight -- `simple_push`: default animation, but without shadow and native header transition (iOS only, uses default animation on Android) - +- `systemMaterialLight` + Header blur effect systemMaterialLight -- `slide_from_bottom`: slide in the new screen from bottom - +- `systemThickMaterialLight` + Header blur effect systemThickMaterialLight -- `slide_from_right`: slide in the new screen from right (Android only, uses default animation on iOS) - +- `systemChromeMaterialLight` + Header blur effect systemChromeMaterialLight -- `slide_from_left`: slide in the new screen from left (Android only, uses default animation on iOS) - +- `systemUltraThinMaterialDark` + Header blur effect systemUltraThinMaterialDark +- `systemThinMaterialDark` + Header blur effect systemThinMaterialDark -- `none`: don't animate the screen - +- `systemMaterialDark` + Header blur effect systemMaterialDark +- `systemThickMaterialDark` + Header blur effect systemThickMaterialDark -#### `presentation` +- `systemChromeMaterialDark` + Header blur effect systemChromeMaterialDark -How should the screen be presented. +Only supported on iOS. -Only supported on Android and iOS. +#### `headerBackground` -Supported values: +Function which returns a React Element to render as the background of the header. This is useful for using backgrounds such as an image or a gradient. -- `card`: the new screen will be pushed onto a stack, which means the default animation will be slide from the side on iOS, the animation on Android will vary depending on the OS version and theme. - + Header background -- `modal`: the new screen will be presented modally. this also allows for a nested stack to be rendered inside the screen. - +Example: -- `transparentModal`: the new screen will be presented modally, but in addition, the previous screen will stay so that the content below can still be seen if the screen has translucent background. - +```js + headerBackground: () => ( + + ), +``` -- `containedModal`: will use "UIModalPresentationCurrentContext" modal style on iOS and will fallback to "modal" on Android. - +#### `headerTintColor` -- `containedTransparentModal`: will use "UIModalPresentationOverCurrentContext" modal style on iOS and will fallback to "transparentModal" on Android. - +Tint color for the header. Changes the color of back button and title. -- `fullScreenModal`: will use "UIModalPresentationFullScreen" modal style on iOS and will fallback to "modal" on Android. A screen using this presentation style can't be dismissed by gesture. - + Header tint color -- `formSheet`: will use "BottomSheetBehavior" on Android and "UIModalPresentationFormSheet" modal style on iOS. - - +#### `headerLeft` -##### Using Form Sheet +Function which returns a React Element to display on the left side of the header. This replaces the back button. See `headerBackVisible` to show the back button along side left element. It receives the following properties in the arguments: -To use Form Sheet for your screen, add `presentation: 'formSheet'` to the `options`. +- `tintColor` - The tint color to apply. Defaults to the [theme](themes.md)'s primary color. +- `canGoBack` - Boolean indicating whether there is a screen to go back to. +- `label` - Label text for the button. Usually the title of the previous screen. +- `href` - The `href` to use for the anchor tag on web - - +Header right + +Example: + +```js + headerLeft: () => ( + + ), + headerBackVisible: true, + headerBackTitle: 'Back', +``` -```js name="Form Sheet" snack -import * as React from 'react'; -import { Text, View } from 'react-native'; -import { - createStaticNavigation, - useNavigation, -} from '@react-navigation/native'; -import { Button } from '@react-navigation/elements'; +#### `unstable_headerLeftItems` -// codeblock-focus-start -import { createNativeStackNavigator } from '@react-navigation/native-stack'; +:::warning -// codeblock-focus-end +This option is experimental and may change in a minor release. -function HomeScreen() { - const navigation = useNavigation(); +::: - return ( - - Home Screen - - - ); -} +Function which returns an array of items to display as on the left side of the header. This will override `headerLeft` if both are specified. It receives the following properties in the arguments: -function ProfileScreen() { - const navigation = useNavigation(); +- `tintColor` - The tint color to apply. Defaults to the [theme](themes.md)'s primary color. +- `canGoBack` - Boolean indicating whether there is a screen to go back to. - return ( - - Profile Screen - - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam accumsan - euismod enim, quis porta ligula egestas sed. Maecenas vitae consequat - odio, at dignissim lorem. Ut euismod eros ac mi ultricies, vel pharetra - tortor commodo. Interdum et malesuada fames ac ante ipsum primis in - faucibus. Nullam at urna in metus iaculis aliquam at sed quam. In - ullamcorper, ex ut facilisis commodo, urna diam posuere urna, at - condimentum mi orci ac ipsum. In hac habitasse platea dictumst. Donec - congue pharetra ipsum in finibus. Nulla blandit finibus turpis, non - vulputate elit viverra a. Curabitur in laoreet nisl. - - - - ); -} +Example: -// codeblock-focus-start -const MyStack = createNativeStackNavigator({ - screens: { - Home: { - screen: HomeScreen, - }, - Profile: { - screen: ProfileScreen, - options: { - presentation: 'formSheet', - headerShown: false, - sheetAllowedDetents: 'fitToContents', - }, +```js +unstable_headerRightItems: () => [ + { + type: 'button', + title: 'Edit', + onPress: () => { + // Do something }, }, -}); -// codeblock-focus-end - -const Navigation = createStaticNavigation(MyStack); - -export default function App() { - return ; -} +], ``` - - - -```js name="Form Sheet" snack -import * as React from 'react'; -import { Text, View } from 'react-native'; -import { NavigationContainer, useNavigation } from '@react-navigation/native'; -import { Button } from '@react-navigation/elements'; -// codeblock-focus-start -import { createNativeStackNavigator } from '@react-navigation/native-stack'; +See [Header items](#header-items) for more information. -const Stack = createNativeStackNavigator(); +Only supported on iOS. -function MyStack() { - return ( - - - - - ); -} -// codeblock-focus-end +#### `headerRight` -function HomeScreen() { - const navigation = useNavigation(); +Function which returns a React Element to display on the right side of the header. It receives the following properties in the arguments: - return ( - - Home Screen - - - ); -} +- `tintColor` - The tint color to apply. Defaults to the [theme](themes.md)'s primary color. +- `canGoBack` - Boolean indicating whether there is a screen to go back to. -function ProfileScreen() { - const navigation = useNavigation(); + Header right - return ( - - Profile Screen - - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam accumsan - euismod enim, quis porta ligula egestas sed. Maecenas vitae consequat - odio, at dignissim lorem. Ut euismod eros ac mi ultricies, vel pharetra - tortor commodo. Interdum et malesuada fames ac ante ipsum primis in - faucibus. Nullam at urna in metus iaculis aliquam at sed quam. In - ullamcorper, ex ut facilisis commodo, urna diam posuere urna, at - condimentum mi orci ac ipsum. In hac habitasse platea dictumst. Donec - congue pharetra ipsum in finibus. Nulla blandit finibus turpis, non - vulputate elit viverra a. Curabitur in laoreet nisl. - - - - ); -} +Example: -export default function App() { - return ( - - - - ); -} +```js +headerRight: () => ; ``` - - +#### `unstable_headerRightItems` :::warning -Due to technical issues in platform component integration with `react-native`, `presentation: 'formSheet'` has limited support for `flex: 1`. +This option is experimental and may change in a minor release. -On Android, using `flex: 1` on a top-level content container passed to a `formSheet` with `showAllowedDetents: 'fitToContents'` causes the sheet to not display at all, leaving only the dimmed background visible. This is because it is the sheet, not the parent who is source of the size. Setting fixed values for `sheetAllowedDetents`, e.g. `[0.4, 0.9]`, works correctly (content is aligned for the highest detent). +::: -On iOS, `flex: 1` with `showAllowedDetents: 'fitToContents'` works properly but setting a fixed value for `showAllowedDetents` causes the screen to not respect the `flex: 1` style - the height of the container does not fill the `formSheet` fully, but rather inherits intrinsic size of its contents. This tradeoff is _currently_ necessary to prevent ["sheet flickering" problem on iOS](https://github.com/software-mansion/react-native-screens/issues/1722). +Function which returns an array of items to display as on the right side of the header. This will override `headerRight` if both are specified. It receives the following properties in the arguments: -If you don't use `flex: 1` but the content's height is less than max screen height, the rest of the sheet might become translucent or use the default theme background color (you can see this happening on the screenshots in the descrption of [this PR](https://github.com/software-mansion/react-native-screens/pull/2462)). To match the sheet to the background of your content, set `backgroundColor` in the `contentStyle` prop of the given screen. +- `tintColor` - The tint color to apply. Defaults to the [theme](themes.md)'s primary color. +- `canGoBack` - Boolean indicating whether there is a screen to go back to. -On Android, there are also some problems with getting nested ScrollViews to work properly. The solution is to set `nestedScrollEnabled` on the `ScrollView`, but this does not work if the content's height is less than the `ScrollView`'s height. Please see [this PR](https://github.com/facebook/react-native/pull/44099) for details and suggested [workaround](https://github.com/facebook/react-native/pull/44099#issuecomment-2058469661). -::: +Example: -#### `sheetAllowedDetents` +```js +unstable_headerRightItems: () => [ + { + type: 'button', + title: 'Edit', + onPress: () => { + // Do something + }, + }, +], +``` -:::note +See [Header items](#header-items) for more information. -Works only when `presentation` is set to `formSheet`. +Only supported on iOS. -::: +#### `headerTitle` - +String or a function that returns a React Element to be used by the header. Defaults to `title` or name of the screen. -Describes heights where a sheet can rest. +When a function is passed, it receives `tintColor` and`children` in the options object as an argument. The title string is passed in `children`. -Supported values: +Note that if you render a custom element by passing a function, animations for the title won't work. -- `fitToContents` - intents to set the sheet height to the height of its contents. -- Array of fractions, e.g. `[0.25, 0.5, 0.75]`: - - Heights should be described as fraction (a number from `[0, 1]` interval) of screen height / maximum detent height. - - The array **must** be sorted in ascending order. This invariant is verified only in developement mode, where violation results in error. - - iOS accepts any number of detents, while **Android is limited to three** - any surplus values, beside first three are ignored. +#### `headerTitleAlign` -Defaults to `[1.0]`. +How to align the header title. Possible values: -Only supported on Android and iOS. +- `left` + Header title align left -#### `sheetElevation` +- `center` + Header title align center -:::note +Defaults to `left` on platforms other than iOS. -Works only when `presentation` is set to `formSheet`. +Not supported on iOS. It's always `center` on iOS and cannot be changed. -::: +#### `headerTitleStyle` - +Style object for header title. Supported properties: -Integer value describing elevation of the sheet, impacting shadow on the top edge of the sheet. +- `fontFamily` +- `fontSize` +- `fontWeight` +- `color` -Not dynamic - changing it after the component is rendered won't have an effect. + Header title style -Defaults to `24`. +Example: -Only supported on Android. +```js + headerTitleStyle: { + color: 'blue', + fontSize: 22, + fontFamily: 'Georgia', + fontWeight: 300, + }, +``` -#### `sheetExpandsWhenScrolledToEdge` +#### `headerSearchBarOptions` -:::note +Options to render a native search bar on iOS. Search bars are rarely static so normally it is controlled by passing an object to `headerSearchBarOptions` navigation option in the component's body. -Works only when `presentation` is set to `formSheet`. +You also need to specify `contentInsetAdjustmentBehavior="automatic"` in your `ScrollView`, `FlatList` etc. If you don't have a `ScrollView`, specify `headerTransparent: false`. -::: +Example: - +```js +React.useLayoutEffect(() => { + navigation.setOptions({ + headerSearchBarOptions: { + // search bar options + }, + }); +}, [navigation]); +``` -Whether the sheet should expand to larger detent when scrolling. +Supported properties are: -Defaults to `true`. +##### `ref` -Only supported on iOS. +Ref to manipulate the search input imperatively. It contains the following methods: -:::warning +- `focus` - focuses the search bar +- `blur` - removes focus from the search bar +- `setText` - sets the search bar's content to given value +- `clearText` - removes any text present in the search bar input field +- `cancelSearch` - cancel the search and close the search bar -Please note that for this interaction to work, the ScrollView must be "first-subview-chain" descendant of the Screen component. This restriction is due to platform requirements. +##### `autoCapitalize` + +Controls whether the text is automatically auto-capitalized as it is entered by the user. +Possible values: + +- `none` +- `words` +- `sentences` +- `characters` -::: +Defaults to `sentences`. -#### `sheetCornerRadius` +##### `autoFocus` -:::note +Whether to automatically focus search bar when it's shown. Defaults to `false`. -Works only when `presentation` is set to `formSheet`. +Only supported on Android. -::: +##### `barTintColor` - +The search field background color. By default bar tint color is translucent. -The corner radius that the sheet will try to render with. +Only supported on iOS. -If set to non-negative value it will try to render sheet with provided radius, else it will apply system default. +Header search bar options - Bar tint color -If left unset, system default is used. +##### `tintColor` -Only supported on Android and iOS. +The color for the cursor caret and cancel button text. -#### `sheetInitialDetentIndex` +Only supported on iOS. -:::note +Header search bar options - Tint color -Works only when `presentation` is set to `formSheet`. +##### `cancelButtonText` -::: +The text to be used instead of default `Cancel` button text. - +Only supported on iOS. -**Index** of the detent the sheet should expand to after being opened. +##### `disableBackButtonOverride` -If the specified index is out of bounds of `sheetAllowedDetents` array, in dev environment more errors will be thrown, in production the value will be reset to default value. +Whether the back button should close search bar's text input or not. Defaults to `false`. -Additionaly there is `last` value available, when set the sheet will expand initially to last (largest) detent. +Only supported on Android. -Defaults to `0` - which represents first detent in the detents array. +##### `hideNavigationBar` -Only supported on Android and iOS. +Boolean indicating whether to hide the navigation bar during searching. Defaults to `true`. -#### `sheetGrabberVisible` +Only supported on iOS. -:::note +##### `hideWhenScrolling` -Works only when `presentation` is set to `formSheet`. +Boolean indicating whether to hide the search bar when scrolling. Defaults to `true`. -::: +Only supported on iOS. - +##### `inputType` -Boolean indicating whether the sheet shows a grabber at the top. +The type of the input. Defaults to `"text"`. -Defaults to `false`. +Supported values: -Only supported on iOS. +- `"text"` +- `"phone"` +- `"number"` +- `"email"` -#### `sheetLargestUndimmedDetentIndex` +Only supported on Android. -:::note +##### `obscureBackground` -Works only when `presentation` is set to `formSheet`. +Boolean indicating whether to obscure the underlying content with semi-transparent overlay. Defaults to `true`. -::: +##### `placeholder` - +Text displayed when search field is empty. -The largest sheet detent for which a view underneath won't be dimmed. +##### `textColor` -This prop can be set to an number, which indicates index of detent in `sheetAllowedDetents` array for which there won't be a dimming view beneath the sheet. +The color of the text in the search field. -Additionaly there are following options available: +Header search bar options - Text color -- `none` - there will be dimming view for all detents levels, -- `last` - there won't be a dimming view for any detent level. +##### `hintTextColor` -Defaults to `none`, indicating that the dimming view should be always present. +The color of the hint text in the search field. -Only supported on Android and iOS. +Only supported on Android. -#### `orientation` +Header search bar options - Hint text color -The display orientation to use for the screen. +##### `headerIconColor` -Supported values: +The color of the search and close icons shown in the header -- `default` - resolves to "all" without "portrait_down" on iOS. On Android, this lets the system decide the best orientation. -- `all`: all orientations are permitted. -- `portrait`: portrait orientations are permitted. -- `portrait_up`: right-side portrait orientation is permitted. -- `portrait_down`: upside-down portrait orientation is permitted. -- `landscape`: landscape orientations are permitted. -- `landscape_left`: landscape-left orientation is permitted. -- `landscape_right`: landscape-right orientation is permitted. +Only supported on Android. -Only supported on Android and iOS. +Header search bar options - Header icon color -#### `autoHideHomeIndicator` +##### `shouldShowHintSearchIcon` -Boolean indicating whether the home indicator should prefer to stay hidden. Defaults to `false`. +Whether to show the search hint icon when search bar is focused. Defaults to `true`. -Only supported on iOS. +Only supported on Android. -#### `gestureDirection` +##### `onBlur` -Sets the direction in which you should swipe to dismiss the screen. +A callback that gets called when search bar has lost focus. -Supported values: +##### `onCancelButtonPress` -- `vertical` – dismiss screen vertically -- `horizontal` – dismiss screen horizontally (default) +A callback that gets called when the cancel button is pressed. -When using `vertical` option, options `fullScreenGestureEnabled: true`, `customAnimationOnGesture: true` and `animation: 'slide_from_bottom'` are set by default. +##### `onChangeText` -Only supported on iOS. +A callback that gets called when the text changes. It receives the current text value of the search bar. -#### `animationDuration` +Example: -Changes the duration (in milliseconds) of `slide_from_bottom`, `fade_from_bottom`, `fade` and `simple_push` transitions on iOS. Defaults to `350`. +```js +const [search, setSearch] = React.useState(''); -The duration of `default` and `flip` transitions isn't customizable. +React.useLayoutEffect(() => { + navigation.setOptions({ + headerSearchBarOptions: { + onChangeText: (event) => setSearch(event.nativeEvent.text), + }, + }); +}, [navigation]); +``` -Only supported on iOS. +#### `headerShown` -#### `navigationBarColor` +Whether to show the header. The header is shown by default. Setting this to `false` hides the header. -:::warning +#### `header` -This option is deprecated and will be removed in a future release (for apps targeting Android SDK 35 or above edge-to-edge mode is enabled by default -and it is expected that the edge-to-edge will be enforced in future SDKs, see [here](https://developer.android.com/about/versions/15/behavior-changes-15#ux) for more information). +Custom header to use instead of the default header. -::: +This accepts a function that returns a React Element to display as a header. The function receives an object containing the following properties as the argument: -Sets the navigation bar color. Defaults to initial status bar color. +- `navigation` - The navigation object for the current screen. +- `route` - The route object for the current screen. +- `options` - The options for the current screen +- `back` - Options for the back button, contains an object with a `title` property to use for back button label. -Only supported on Android. +Example: -#### `navigationBarHidden` +```js +import { getHeaderTitle } from '@react-navigation/elements'; -Boolean indicating whether the navigation bar should be hidden. Defaults to `false`. +// .. -Only supported on Android. +header: ({ navigation, route, options, back }) => { + const title = getHeaderTitle(options, route.name); -#### `freezeOnBlur` + return ( + : undefined + } + style={options.headerStyle} + /> + ); +}; +``` -Boolean indicating whether to prevent inactive screens from re-rendering. Defaults to `false`. -Defaults to `true` when `enableFreeze()` from `react-native-screens` package is run at the top of the application. +To set a custom header for all the screens in the navigator, you can specify this option in the `screenOptions` prop of the navigator. -Only supported on iOS and Android. +Note that if you specify a custom header, the native functionality such as large title, search bar etc. won't work. ### Events @@ -1428,3 +1507,238 @@ const MyView = () => { ); }; ``` + +## Header items + +The [`unstable_headerLeftItems`](#unstable_headerleftitems) and [`unstable_headerRightItems`](#unstable_headerrightitems) options allow you to add header items to the left and right side of the header respectively. This items can show native buttons, menus or custom React elements. + +On iOS 26+, the header right items can also be collapsed into an overflow menu by the system when there is not enough space to show all items. Note that custom elements (with `type: 'custom'`) won't be collapsed into the overflow menu. + +Header items + +Header item with menu + +There are 3 categories of items that can be displayed in the header: + +### Action + +A regular button that performs an action when pressed, or shows a menu. + +Common properties: + +- `type`: Must be `button` or `menu`. +- `label`: Label of the item. The label is not shown if `icon` is specified. However, it is used by screen readers, or if the header items get collapsed due to lack of space. +- `labelStyle`: Style object for the label. Supported properties: + - `fontFamily` + - `fontSize` + - `fontWeight` + - `color` +- `icon`: Optional icon to show instead of the label. + + The icon can be an image: + + ```js + { + type: 'image', + source: require('./path/to/image.png'), + } + ``` + + Or a [SF Symbols](https://developer.apple.com/sf-symbols/) name: + + ```js + { + type: 'sfSymbol', + name: 'heart', + } + ``` + +- `variant`: Visual variant of the button. Supported values: + - `plain` (default) + - `done` + - `prominent` +- `tintColor`: Tint color to apply to the item. +- `disabled`: Whether the item is disabled. +- `width`: Width of the item. +- `hidesSharedBackground` (iOS 26+): Whether the background this item may share with other items should be hidden. Setting this to `true` hides the liquid glass background. +- `sharesBackground` (iOS 26+): Whether this item can share a background with other items. +- `identifier` (iOS 26+) - An identifier used to match items across transitions. +- `badge` (iOS 26+): An optional badge to display alongside the item. Supported properties: + - `value`: The value to display in the badge. It can be a string or a number. + - `style`: Style object for the badge. Supported properties: + - `fontFamily` + - `fontSize` + - `fontWeight` + - `color` +- `accessibilityLabel`: Accessibility label for the item. +- `accessibilityHint`: Accessibility hint for the item. + +Supported properties when `type` is `button`: + +- `onPress`: Function to call when the button is pressed. +- `selected`: Whether the button is in a selected state. + +Example: + +```js +unstable_headerRightItems: () => [ + { + type: 'button', + label: 'Edit', + icon: { + type: 'sfSymbol', + name: 'pencil', + }, + onPress: () => { + // Do something + }, + }, +], +``` + +Supported properties when `type` is `menu`: + +- `changesSelectionAsPrimaryAction`: Whether the menu is a selection menu. Tapping an item in a selection menu will add a checkmark to the selected item. Defaults to `false`. +- `menu`: An object containing the menu items. It contains the following properties: + + - `title`: Optional title to show on top of the menu. + - `items`: An array of menu items. A menu item can be either an `action` or a `submenu`. + + - `action`: An object with the following properties: + + - `type`: Must be `action`. + - `label`: Label of the menu item. + - `icon`: Optional icon to show alongside the label. The icon can be a [SF Symbols](https://developer.apple.com/sf-symbols/) name: + + ```js + { + type: 'sfSymbol', + name: 'trash', + } + ``` + + - `onPress`: Function to call when the menu item is pressed. + - `state`: Optional state of the menu item. Supported values: + - `on` + - `off` + - `mixed` + - `disabled`: Whether the menu item is disabled. + - `destructive`: Whether the menu item is styled as destructive. + - `hidden`: Whether the menu item is hidden. + - `keepsMenuPresented`: Whether to keep the menu open after selecting this item. Defaults to `false`. + - `discoverabilityLabel`: An elaborated title that explains the purpose of the action. + + - `submenu`: An object with the following properties: + + - `type`: Must be `submenu`. + - `label`: Label of the submenu item. + - `icon`: Optional icon to show alongside the label. The icon can be a [SF Symbols](https://developer.apple.com/sf-symbols/) name: + + ```js + { + type: 'sfSymbol', + name: 'pencil', + } + ``` + + - `items`: An array of menu items (can be either `action` or `submenu`). + +Example: + +```js +unstable_headerRightItems: () => [ + { + type: 'menu', + label: 'Options', + icon: { + type: 'sfSymbol', + name: 'ellipsis', + }, + menu: { + title: 'Options', + items: [ + { + type: 'action', + label: 'Edit', + icon: { + type: 'sfSymbol', + name: 'pencil', + }, + onPress: () => { + // Do something + }, + }, + { + type: 'submenu', + label: 'More', + items: [ + { + type: 'action', + label: 'Delete', + destructive: true, + onPress: () => { + // Do something + }, + }, + ], + }, + ], + }, + }, +], +``` + +### Spacing + +An item to add spacing between other items in the header. + +Supported properties: + +- `type`: Must be `spacing`. +- `spacing`: Amount of spacing to add. + +```js +unstable_headerRightItems: () => [ + { + type: 'button', + label: 'Edit', + onPress: () => { + // Do something + }, + }, + { + type: 'spacing', + spacing: 10, + }, + { + type: 'button', + label: 'Delete', + onPress: () => { + // Do something + }, + }, +], +``` + +### Custom + +A custom item to display any React Element in the header. + +Supported properties: + +- `type`: Must be `custom`. +- `element`: A React Element to display as the item. +- `hidesSharedBackground`: Whether the background this item may share with other items in the bar should be hidden. Setting this to `true` hides the liquid glass background on iOS 26+. + +Example: + +```js +unstable_headerRightItems: () => [ + { + type: 'custom', + element: , + }, +], +``` + +The advantage of using this over [`headerLeft`](#headerleft) or [`headerRight`](#headerright) options is that it supports features like shared background on iOS 26+. diff --git a/versioned_docs/version-7.x/navigating-without-navigation-prop.md b/versioned_docs/version-7.x/navigating-without-navigation-prop.md index e01cb26729f..0200c83209a 100755 --- a/versioned_docs/version-7.x/navigating-without-navigation-prop.md +++ b/versioned_docs/version-7.x/navigating-without-navigation-prop.md @@ -13,7 +13,7 @@ Sometimes you need to trigger a navigation action from places where you do not h - You need to navigate from inside a component without needing to pass the `navigation` prop down, see [`useNavigation`](use-navigation.md) instead. The `ref` behaves differently, and many helper methods specific to screens aren't available. - You need to handle deep links or universal links. Doing this with the `ref` has many edge cases. See [configuring links](configuring-links.md) for more information on handling deep linking. -- You need to integrate with third party libraries, such as push notifications, branch etc. See [third party integrations for deep linking](deep-linking.md#third-party-integrations) instead. +- You need to integrate with third party libraries, such as push notifications, branch etc. See [Integrating with other tools](deep-linking.md#integrating-with-other-tools) instead. **Do** use the `ref` if: diff --git a/versioned_docs/version-7.x/navigating.md b/versioned_docs/version-7.x/navigating.md index 8babb1a7e98..73057ef5193 100755 --- a/versioned_docs/version-7.x/navigating.md +++ b/versioned_docs/version-7.x/navigating.md @@ -358,8 +358,8 @@ export default function App() { ## Summary -- `navigation.navigate('RouteName')` pushes a new route to the native stack navigator if you're not already on that route. -- We can call `navigation.push('RouteName')` as many times as we like and it will continue pushing routes. -- The header bar will automatically show a back button, but you can programmatically go back by calling `navigation.goBack()`. On Android, the hardware back button just works as expected. -- You can go back to an existing screen in the stack with `navigation.popTo('RouteName')`, and you can go back to the first screen in the stack with `navigation.popToTop()`. -- The `navigation` object is available to all screen components with the [`useNavigation`](use-navigation.md) hook. +- [`navigation.navigate('RouteName')`](navigation-object.md#navigate) pushes a new route to the native stack navigator if you're not already on that route. +- We can call [`navigation.push('RouteName')`](stack-actions.md#push) as many times as we like and it will continue pushing routes. +- The header bar will automatically show a back button, but you can programmatically go back by calling [`navigation.goBack()`](navigation-object.md#goback). On Android, the hardware back button just works as expected. +- You can go back to an existing screen in the stack with [`navigation.popTo('RouteName')`](stack-actions.md#popto), and you can go back to the first screen in the stack with [`navigation.popToTop()`](stack-actions.md#poptotop). +- The [`navigation`](navigation-object.md) object is available to all screen components with the [`useNavigation`](use-navigation.md) hook. diff --git a/versioned_docs/version-7.x/navigation-actions.md b/versioned_docs/version-7.x/navigation-actions.md index 21f1c76250d..91b6dfa41fc 100755 --- a/versioned_docs/version-7.x/navigation-actions.md +++ b/versioned_docs/version-7.x/navigation-actions.md @@ -70,9 +70,6 @@ function HomeScreen() { > Navigate to Profile - ); } @@ -90,52 +87,6 @@ function ProfileScreen({ route }) { > Profile! {route.params.user}'s profile - - - - ); } @@ -192,9 +143,6 @@ function HomeScreen() { > Navigate to Profile - ); } @@ -213,52 +161,6 @@ function ProfileScreen({ route }) { > Profile! {route.params.user}'s profile - - - - ); } @@ -343,9 +245,6 @@ function HomeScreen() { > Navigate to Profile - ); } @@ -363,13 +262,6 @@ function ProfileScreen({ route }) { > Profile! {route.params.user}'s profile - - - ); } @@ -465,9 +336,6 @@ function HomeScreen() { > Navigate to Profile - ); } @@ -486,17 +354,6 @@ function ProfileScreen({ route }) { > Profile! {route.params.user}'s profile - - - ); } @@ -634,15 +470,6 @@ function HomeScreen() { > Navigate to Profile - ); } @@ -662,46 +489,9 @@ function ProfileScreen({ route }) { {route.params.user}'s profile - - - - ); } @@ -789,50 +570,9 @@ function ProfileScreen({ route }) { {route.params.user}'s profile - - - - ); } @@ -916,48 +653,13 @@ function ProfileScreen({ route }) { > Profile! {route.params.user}'s profile - - - - ); } @@ -1039,31 +738,6 @@ function ProfileScreen({ route }) { > Profile! {route.params.user}'s profile - - - ); } @@ -1409,53 +1080,14 @@ function ProfileScreen({ route }) { > Profile! {route.params.user}'s profile - - - ); @@ -1511,9 +1143,6 @@ function HomeScreen() { > Navigate to Profile - ); } @@ -1534,51 +1163,184 @@ function ProfileScreen({ route }) { {route.params.user}'s profile + + ); +} + +const Stack = createStackNavigator(); + +export default function App() { + return ( + + + + + + + ); +} +``` + + + + +If you want to replace params for a particular route, you can add a `source` property referring to the route key: + + + + +```js name="Common actions setParams" snack +import * as React from 'react'; +import { View, Text } from 'react-native'; +import { Button } from '@react-navigation/elements'; +import { + createStaticNavigation, + useNavigation, + CommonActions, +} from '@react-navigation/native'; +import { createStackNavigator } from '@react-navigation/stack'; + +function HomeScreen() { + const navigation = useNavigation(); + + return ( + + Home! + + ); +} + +function ProfileScreen({ route }) { + const navigation = useNavigation(); + return ( + + Profile! + {route.params.user}'s profile + + ); +} + +const Stack = createStackNavigator({ + screens: { + Home: HomeScreen, + Profile: ProfileScreen, + }, +}); + +const Navigation = createStaticNavigation(Stack); + +export default function App() { + return ; +} +``` + + + + +```js name="Common actions setParams" snack +import * as React from 'react'; +import { View, Text } from 'react-native'; +import { Button } from '@react-navigation/elements'; +import { + NavigationContainer, + CommonActions, + useNavigation, +} from '@react-navigation/native'; +import { createStackNavigator } from '@react-navigation/stack'; + +function HomeScreen() { + const navigation = useNavigation(); + + return ( + + Home! + + + ); +} + +function ProfileScreen({ route }) { + const navigation = useNavigation(); + + return ( + + Profile! + {route.params.user}'s profile ); @@ -1601,12 +1363,18 @@ export default function App() { -If you want to set params for a particular route, you can add a `source` property referring to the route key: +If the `source` property is explicitly set to `undefined`, it'll replace the params for the focused route. + +### replaceParams + +The `replaceParams` action allows to replace params for a certain route. It takes the following arguments: + +- `params` - _object_ - required - New params to use for the route. -```js name="Common actions setParams" snack +```js name="Common actions replaceParams" snack import * as React from 'react'; import { View, Text } from 'react-native'; import { Button } from '@react-navigation/elements'; @@ -1639,9 +1407,6 @@ function HomeScreen() { > Navigate to Profile - ); } @@ -1659,53 +1424,14 @@ function ProfileScreen({ route }) { > Profile! {route.params.user}'s profile - - - ); @@ -1728,7 +1454,7 @@ export default function App() { -```js name="Common actions setParams" snack +```js name="Common actions replaceParams" snack import * as React from 'react'; import { View, Text } from 'react-native'; import { Button } from '@react-navigation/elements'; @@ -1761,9 +1487,6 @@ function HomeScreen() { > Navigate to Profile - ); } @@ -1784,51 +1507,187 @@ function ProfileScreen({ route }) { {route.params.user}'s profile + + ); +} + +const Stack = createStackNavigator(); + +export default function App() { + return ( + + + + + + + ); +} +``` + + + + +If you want to replace params for a particular route, you can add a `source` property referring to the route key: + + + + +```js name="Common actions replaceParams" snack +import * as React from 'react'; +import { View, Text } from 'react-native'; +import { Button } from '@react-navigation/elements'; +import { + createStaticNavigation, + useNavigation, + CommonActions, +} from '@react-navigation/native'; +import { createStackNavigator } from '@react-navigation/stack'; + +function HomeScreen() { + const navigation = useNavigation(); + + return ( + + Home! + + + ); +} + +function ProfileScreen({ route }) { + const navigation = useNavigation(); + return ( + + Profile! + {route.params.user}'s profile + + ); +} + +const Stack = createStackNavigator({ + screens: { + Home: HomeScreen, + Profile: ProfileScreen, + }, +}); + +const Navigation = createStaticNavigation(Stack); + +export default function App() { + return ; +} +``` + + + + +```js name="Common actions replaceParams" snack +import * as React from 'react'; +import { View, Text } from 'react-native'; +import { Button } from '@react-navigation/elements'; +import { + NavigationContainer, + CommonActions, + useNavigation, +} from '@react-navigation/native'; +import { createStackNavigator } from '@react-navigation/stack'; + +function HomeScreen() { + const navigation = useNavigation(); + + return ( + + Home! + + + ); +} + +function ProfileScreen({ route }) { + const navigation = useNavigation(); + + return ( + + Profile! + {route.params.user}'s profile ); @@ -1851,4 +1710,4 @@ export default function App() { -If the `source` property is explicitly set to `undefined`, it'll set the params for the focused route. +If the `source` property is explicitly set to `undefined`, it'll replace the params for the focused route. diff --git a/versioned_docs/version-7.x/navigation-container.md b/versioned_docs/version-7.x/navigation-container.md index 7fed8c52420..536dd908ae2 100644 --- a/versioned_docs/version-7.x/navigation-container.md +++ b/versioned_docs/version-7.x/navigation-container.md @@ -747,7 +747,7 @@ This option is not available on Web. ##### `linking.getStateFromPath` -You can optionally override the way React Navigation parses links to a state object by providing your own implementation. +React Navigation handles deep links and [URLs on Web](web-support.md) by parsing the path to a [navigation state](navigation-state.md) object based on the [linking config](#linkingconfig). You can optionally override the way the parsing happens by providing your own `getStateFromPath` function. Example: @@ -795,9 +795,12 @@ Example: ##### `linking.getPathFromState` -You can optionally override the way React Navigation serializes state objects to link by providing your own implementation. This is necessary for proper web support if you have specified `getStateFromPath`. +On Web, React Navigation automatically updates the [URL in the browser's address bar](web-support.md) to match the current navigation state by serializing the state to a path based on the [linking config](#linkingconfig). You can optionally override the way the serialization happens by providing your own `getPathFromState` function. + +If you provide a custom [`getStateFromPath`](#linkinggetstatefrompath), you should also provide a custom `getPathFromState` to ensure that the parsing and serialization are consistent with each other for Web support to work correctly. Example: + @@ -840,6 +843,56 @@ Example: +##### `linking.getActionFromState` + +The state parsed with [`getStateFromPath`](#linkinggetstatefrompath) is used as the initial state of the navigator. But for subsequent deep links and URLs, the state is converted to a navigation action. Typically it is a [`navigate`](navigation-actions.md#navigate) action. + +You can provide a custom `getActionFromState` function to customize how the state is converted to an action. + +Example: + + + + +```js + +``` + + + + +```js + + {/* content */} + +``` + + + + ### `fallback` React Element to use as a fallback while we resolve deep links. Defaults to `null`. diff --git a/versioned_docs/version-7.x/navigation-events.md b/versioned_docs/version-7.x/navigation-events.md index f76a7af1b07..1d3c6430a14 100644 --- a/versioned_docs/version-7.x/navigation-events.md +++ b/versioned_docs/version-7.x/navigation-events.md @@ -25,6 +25,12 @@ For most cases, the [`useFocusEffect`](use-focus-effect.md) hook might be approp This event is emitted when the screen goes out of focus. +:::note + +In some cases, such as going back from a screen in [native-stack navigator](native-stack-navigator.md), the screen may not receive the `blur` event as the screen is unmounted immediately. For cleaning up resources, it's recommended to use the cleanup function of [`useFocusEffect`](use-focus-effect.md) hook instead that considers both blur and unmounting of the screen. + +::: + ### `state` This event is emitted when the navigator's state changes. This event receives the navigator's state in the event data (`event.data.state`). @@ -263,7 +269,7 @@ class Profile extends React.Component { } ``` -One thing to keep in mind is that you can only listen to events from the immediate navigator with `addListener`. For example, if you try to add a listener in a screen that's inside a stack that's nested in a tab, it won't get the `tabPress` event. If you need to listen to an event from a parent navigator, you may use [`navigation.getParent`](navigation-object.md#getparent) to get a reference to the parent screen's navigation object and add a listener. +Keep in mind that you can only listen to events from the immediate navigator with `addListener`. For example, if you try to add a listener in a screen that's inside a stack that's nested in a tab, it won't get the `tabPress` event. If you need to listen to an event from a parent navigator, you may use [`navigation.getParent`](navigation-object.md#getparent) to get a reference to the parent screen's navigation object and add a listener. ```js const unsubscribe = navigation @@ -275,6 +281,12 @@ const unsubscribe = navigation Here `'MyTabs'` refers to the value you pass in the `id` prop of the parent `Tab.Navigator` whose event you want to listen to. +:::warning + +The component needs to be rendered for the listeners to be added in `useEffect` or `componentDidMount`. Navigators such as [bottom tabs](bottom-tab-navigator.md) and [drawer](drawer-navigator.md) lazily render the screen after navigating to it. So if your listener is not being called, double check that the component is rendered. + +::: + ### `listeners` prop on `Screen` Sometimes you might want to add a listener from the component where you defined the navigator rather than inside the screen. You can use the `listeners` prop on the `Screen` component to add listeners. The `listeners` prop takes an object with the event names as keys and the listener callbacks as values. diff --git a/versioned_docs/version-7.x/navigation-lifecycle.md b/versioned_docs/version-7.x/navigation-lifecycle.md index 4119652a4e4..d2a2317652b 100755 --- a/versioned_docs/version-7.x/navigation-lifecycle.md +++ b/versioned_docs/version-7.x/navigation-lifecycle.md @@ -474,7 +474,7 @@ export default function App() { See [Navigation events](navigation-events.md) for more details on the available events and the API usage. -Instead of adding event listeners manually, we can use the [`useFocusEffect`](use-focus-effect.md) hook to perform side effects. It's like React's `useEffect` hook, but it ties into the navigation lifecycle. +For performing side effects, we can use the [`useFocusEffect`](use-focus-effect.md) hook instead of subscribing to events. It's like React's `useEffect` hook, but it ties into the navigation lifecycle. Example: @@ -622,7 +622,11 @@ export default function App() { If you want to render different things based on if the screen is focused or not, you can use the [`useIsFocused`](use-is-focused.md) hook which returns a boolean indicating whether the screen is focused. +If you want to know if the screen is focused or not inside of an event listener, you can use the [`navigation.isFocused()`](navigation-object.md#isfocused) method. Note that using this method doesn't trigger a re-render like the `useIsFocused` hook does, so it is not suitable for rendering different things based on focus state. + ## Summary -- While React's lifecycle methods are still valid, React Navigation adds more events that you can subscribe to through the `navigation` object. -- You may also use the `useFocusEffect` or `useIsFocused` hooks. +- React Navigation does not unmount screens when navigating away from them +- The [`useFocusEffect`](use-focus-effect.md) hook is analogous to React's [`useEffect`](https://react.dev/reference/react/useEffect) but is tied to the navigation lifecycle instead of the component lifecycle. +- The [`useIsFocused`](use-is-focused.md) hook and [`navigation.isFocused()`](navigation-object.md#isfocused) method can be used to determine if a screen is currently focused. +- React Navigation emits [`focus`](navigation-events.md#focus) and [`blur`](navigation-events.md#blur) events that can be listened to when a screen comes into focus or goes out of focus. diff --git a/versioned_docs/version-7.x/navigation-object.md b/versioned_docs/version-7.x/navigation-object.md index 86977ed26f9..42af46e61b2 100755 --- a/versioned_docs/version-7.x/navigation-object.md +++ b/versioned_docs/version-7.x/navigation-object.md @@ -451,7 +451,7 @@ The `reset` method lets us replace the navigator state with a new state: -```js name="Navigate - replace and reset" snack +```js name="Navigation object replace and reset" snack import * as React from 'react'; import { Button } from '@react-navigation/elements'; import { View, Text } from 'react-native'; @@ -590,7 +590,7 @@ export default App; -```js name="Navigate - replace and reset" snack +```js name="Navigation object replace and reset" snack import * as React from 'react'; import { Button } from '@react-navigation/elements'; import { View, Text } from 'react-native'; @@ -972,7 +972,7 @@ The `setParams` method lets us update the params (`route.params`) of the current -```js name="Navigate - setParams" snack +```js name="Navigation object setParams" snack import * as React from 'react'; import { Button } from '@react-navigation/elements'; import { View, Text } from 'react-native'; @@ -1074,7 +1074,7 @@ export default App; -```js name="Navigate - setParams" snack +```js name="Navigation object setParams" snack import * as React from 'react'; import { Button } from '@react-navigation/elements'; import { View, Text } from 'react-native'; @@ -1173,6 +1173,214 @@ export default App; +### `replaceParams` + +The `replaceParams` method lets us replace the params (`route.params`) of the current screen with a new params object. + + + + +```js name="Navigation object replaceParams" snack +import * as React from 'react'; +import { Button } from '@react-navigation/elements'; +import { View, Text } from 'react-native'; +import { + useNavigation, + createStaticNavigation, +} from '@react-navigation/native'; +import { createNativeStackNavigator } from '@react-navigation/native-stack'; + +function HomeScreen() { + const navigation = useNavigation(); + + return ( + + This is the home screen of the app + + + ); +} + +// codeblock-focus-start +function ProfileScreen({ route }) { + const navigation = useNavigation(); + + return ( + + Profile Screen + Friends: + {route.params.friends[0]} + {route.params.friends[1]} + {route.params.friends[2]} + + + + ); +} +// codeblock-focus-end + +const Stack = createNativeStackNavigator({ + initialRouteName: 'Home', + screens: { + Home: HomeScreen, + Profile: { + screen: ProfileScreen, + options: ({ route }) => ({ title: route.params.title }), + }, + }, +}); + +const Navigation = createStaticNavigation(Stack); + +function App() { + return ; +} + +export default App; +``` + + + + +```js name="Navigation object replaceParams" snack +import * as React from 'react'; +import { Button } from '@react-navigation/elements'; +import { View, Text } from 'react-native'; +import { NavigationContainer, useNavigation } from '@react-navigation/native'; +import { createNativeStackNavigator } from '@react-navigation/native-stack'; + +function HomeScreen() { + const navigation = useNavigation(); + + return ( + + This is the home screen of the app + + + ); +} + +// codeblock-focus-start +function ProfileScreen({ route }) { + const navigation = useNavigation(); + + return ( + + Profile Screen + Friends: + {route.params.friends[0]} + {route.params.friends[1]} + {route.params.friends[2]} + + + + ); +} +// codeblock-focus-end + +const Stack = createNativeStackNavigator(); + +function App() { + return ( + + + + ({ title: route.params.title })} + /> + + + ); +} + +export default App; +``` + + + + ### `setOptions` The `setOptions` method lets us set screen options from within the component. This is useful if we need to use the component's props, state or context to configure our screen. @@ -1180,7 +1388,7 @@ The `setOptions` method lets us set screen options from within the component. Th -```js name="Navigate - setOptions" snack +```js name="Navigation object setOptions" snack import * as React from 'react'; import { View, Text, TextInput } from 'react-native'; import { Button } from '@react-navigation/elements'; @@ -1270,7 +1478,7 @@ export default App; -```js name="Navigate - setOptions" snack +```js name="Navigation object setOptions" snack import * as React from 'react'; import { View, Text, TextInput } from 'react-native'; import { Button } from '@react-navigation/elements'; diff --git a/versioned_docs/version-7.x/navigation-solutions-and-community-libraries.md b/versioned_docs/version-7.x/navigation-solutions-and-community-libraries.md deleted file mode 100755 index 1891a0afcf4..00000000000 --- a/versioned_docs/version-7.x/navigation-solutions-and-community-libraries.md +++ /dev/null @@ -1,71 +0,0 @@ ---- -id: navigation-solutions-and-community-libraries -title: Navigation Solutions and Community Libraries -sidebar_label: Community Libraries ---- - -:::note - -Libraries listed in this guide may not have been updated to work with the latest version of React Navigation. Please refer to the library's documentation to see which version of React Navigation it supports. - -::: - -## Solutions built on top of React Navigation - -### Solito - -A tiny wrapper around React Navigation and Next.js that lets you share navigation code across platforms. Also, it provides a set of patterns and examples for building cross-platform apps with React Native + Next.js. - -[Documentation](https://solito.dev/) - -[github.com/nandorojo/solito](https://github.com/nandorojo/solito) - -### Expo Router - -File-based router for React Native apps. With Expo Router pages are automatically generated by simply creating files in a project. - -[Documentation](https://expo.github.io/router/docs) - -[github.com/expo/router](https://github.com/expo/router) - -### Navio - -A navigation library built on top of React Navigation. It's main goal is to improve DX by building the app layout in one place and using the power of TypeScript to provide route names autocompletion. - -[github.com/kanzitelli/rn-navio](https://github.com/kanzitelli/rn-navio) - -[Demo on Snack](https://snack.expo.dev/@kanzitelli/rn-navio-snack) - -## Community libraries - -### react-native-paper - -The [React Native Paper](https://callstack.github.io/react-native-paper/) library provides React Navigation integration for its Material Bottom Tabs. Material Bottom Tabs is a material-design themed tab bar on the bottom of the screen that lets you switch between different routes with animation. - -[callstack.github.io/react-native-paper/docs/guides/bottom-navigation](https://callstack.github.io/react-native-paper/docs/guides/bottom-navigation/) - -### react-native-screens - -This project aims to expose native navigation container components to React Native and React Navigation can integrate with it since version 2.14.0. Using `react-native-screens` brings several benefits, such as support for the ["reachability feature"](https://www.cnet.com/how-to/how-to-use-reachability-on-iphone-6-6-plus/) on iOS, and improved memory consumption on both platforms. - -[github.com/software-mansion/react-native-screens](https://github.com/software-mansion/react-native-screens) - -### react-navigation-header-buttons - -Helps you to render buttons in the navigation bar and handle the styling so you don't have to. It tries to mimic the appearance of native navbar buttons and attempts to offer a simple interface for you to interact with. - -[github.com/vonovak/react-navigation-header-buttons](https://github.com/vonovak/react-navigation-header-buttons) - -[Demo on expo](https://expo.io/@vonovak/navbar-buttons-demo) - -### react-navigation-props-mapper - -Provides simple HOCs that map react-navigation props to your screen components directly - ie. instead of `const user = this.props.route.params.activeUser`, you'd write `const user = this.props.activeUser`. - -[github.com/vonovak/react-navigation-props-mapper](https://github.com/vonovak/react-navigation-props-mapper) - -## react-native-bottom-tabs - -This project aims to expose the native Bottom Tabs component to React Native. It exposes SwiftUI's TabView on iOS and the material design tab bar on Android. Using `react-native-bottom-tabs` can bring several benefits, including multi-platform support and a native-feeling tab bar. - -[github.com/okwasniewski/react-native-bottom-tabs](https://github.com/okwasniewski/react-native-bottom-tabs) diff --git a/versioned_docs/version-7.x/navigation-state.md b/versioned_docs/version-7.x/navigation-state.md index 46d2bf13a2c..43d2a59a7b5 100644 --- a/versioned_docs/version-7.x/navigation-state.md +++ b/versioned_docs/version-7.x/navigation-state.md @@ -30,14 +30,14 @@ There are few properties present in every navigation state object: - `routes` - List of route objects (screens) which are rendered in the navigator. It also represents the history in a stack navigator. There should be at least one item present in this array. - `index` - Index of the focused route object in the `routes` array. - `history` - A list of visited items. This is an optional property and not present in all navigators. For example, it's only present in tab and drawer navigators in the core. The shape of the items in the `history` array can vary depending on the navigator. There should be at least one item present in this array. -- `stale` - A navigation state is assumed to be stale unless the `stale` property is explicitly set to `false`. This means that the state object needs to be ["rehydrated"](#partial-state-objects). +- `stale` - A navigation state is assumed to be stale unless the `stale` property is explicitly set to `false`. This means that the state object needs to be ["rehydrated"](#stale-state-objects). Each route object in a `routes` array may contain the following properties: - `key` - Unique key of the screen. Created automatically or added while navigating to this screen. - `name` - Name of the screen. Defined in navigator component hierarchy. - `params` - An optional object containing params which is defined while navigating e.g. `navigate('Home', { sortBy: 'latest' })`. -- `state` - An optional object containing the navigation state of a child navigator nested inside this screen. +- `state` - An optional object containing the [stale navigation state](#stale-state-objects) of a child navigator nested inside this screen. For example, a stack navigator containing a tab navigator nested inside it's home screen may have a navigation state object like this: @@ -69,11 +69,17 @@ const state = { It's important to note that even if there's a nested navigator, the `state` property on the `route` object is not added until a navigation happens, hence it's not guaranteed to exist. -## Partial state objects +## Stale state objects -Earlier there was a mention of `stale` property in the navigation state. A stale navigation state means that the state object needs to be rehydrated or fixed or fixed up, such as adding missing keys, removing invalid screens etc. before being used. As a user, you don't need to worry about it, React Navigation will fix up any issues in a state object automatically unless `stale` is set to `false`. If you're writing a [custom router](custom-routers.md), the `getRehydratedState` method let's you write custom rehydration logic to fix up state objects. +Earlier there was a mention of `stale` property in the navigation state. If the `stale` property is set to `true` or is missing, the state is assumed to be stale. A stale navigation state means that the state object may be partial, such as missing keys or routes, contain invalid routes, or may not be up-to-date. A stale state can be a result of [deep linking](deep-linking.md), r[estoring from a persisted state](state-persistence.md) etc. -This also applies to the `index` property: `index` should be the last route in a stack, and if a different value was specified, React Navigation fixes it. For example, if you wanted to reset your app's navigation state to have it display the `Profile` route, and have the `Home` route displayed upon going back, and did the below, +If you're accessing the navigation state of a navigator using the built-in APIs such as [`useNavigationState()`](use-navigation-state.md), [`navigation.getState()`](navigation-object.md#getstate) etc., the state object is guaranteed to be complete and not stale. However, if you try to access a child navigator's state with the `state` property on the `route` object, it maybe a stale or partial state object. So it's not recommended to use this property directly. + +Using the [`ref.getRootState()`](navigation-container.md#getrootstate) API will always return a complete and up-to-date state object for the current navigation tree, including any nested child navigators. + +When React Navigation encounters stale or partial state, it will automatically fix it up before using it. This includes adding missing keys, removing any invalid routes, ensuring the `index` is correct etc. This process of fixing stale state is called **rehydration**. If you're writing a [custom router](custom-routers.md), the `getRehydratedState` method lets you write custom rehydration logic to fix up state objects. + +For example, `index` should be the last route in a stack, and if a different value was specified, React Navigation fixes it. For example, if you wanted to reset your app's navigation state to have it display the `Profile` route, and have the `Home` route displayed upon going back, and dispatched the following action: ```js navigation.reset({ @@ -82,7 +88,7 @@ navigation.reset({ }); ``` -React Navigation would correct `index` to 1, and display the route and perform navigation as intended. +React Navigation would correct `index` to `1` before the routes are displayed. This feature comes handy when doing operations such as [reset](navigation-actions.md#reset), [providing a initial state](navigation-container.md#initialstate) etc., as you can safely omit many properties from the navigation state object and relying on React Navigation to add those properties for you, making your code simpler. For example, you can only provide a `routes` array without any keys and React Navigation will automatically add everything that's needed to make it work: @@ -118,4 +124,4 @@ If you want React Navigation to fix invalid state, you need to make sure that yo ::: -When you're providing a state object in [`initialState`](navigation-container.md#initialstate), React Navigation will always assume that it's a stale state object, which makes sure that things like state persistence work smoothly without extra manipulation of the state object. +When you're providing a state object in [`initialState`](navigation-container.md#initialstate), React Navigation will always assume that it's a stale state object, since navigation configuration may have changed since the last time. This makes sure that things like [state persistence](state-persistence.md) work smoothly without extra manipulation of the state object. diff --git a/versioned_docs/version-7.x/navigator.md b/versioned_docs/version-7.x/navigator.md index b38a8aec7c3..f34638f4be2 100644 --- a/versioned_docs/version-7.x/navigator.md +++ b/versioned_docs/version-7.x/navigator.md @@ -309,3 +309,155 @@ function MyStack() { + +### Router + +:::warning + +This API is experimental and may change in a minor release. + +::: + +Routers can be customized with the `UNSTABLE_router` prop on navigator to override how navigation actions are handled. + +It takes a function that receives the original router and returns an object with overrides: + + + + +```js +const MyStack = createNativeStackNavigator({ + // highlight-start + UNSTABLE_router: (original) => ({ + getStateForAction(state, action) { + if (action.type === 'SOME_ACTION') { + // Custom logic + } + + // Fallback to original behavior + return original.getStateForAction(state, action); + }, + }), + // highlight-end + screens: { + Home: HomeScreen, + Profile: ProfileScreen, + }, +}); +``` + + + + +```js +const Stack = createNativeStackNavigator(); + +function MyStack() { + return ( + ({ + getStateForAction(state, action) { + if (action.type === 'SOME_ACTION') { + // Custom logic + } + + // Fallback to original behavior + return original.getStateForAction(state, action); + }, + })} + // highlight-end + > + + + + ); +} +``` + + + + +The function passed to `UNSTABLE_router` **must be a pure function and cannot reference outside dynamic variables**. + +The overrides object is shallow merged with the original router. So you don't need to specify all properties of the router, only the ones you want to override. + +See [custom routers](custom-routers.md) for more details on routers. + +### Route names change behavior + +:::warning + +This API is experimental and may change in a minor release. + +::: + +When the list of available routes in a navigator changes dynamically, e.g. based on conditional rendering, looping over data from an API etc., the navigator needs to update the [navigation state](navigation-state.md) according to the new list of routes. + +By default, it works as follows: + +- Any routes not present in the new available list of routes are removed from the navigation state +- If the currently focused route is still present in the new available list of routes, it remains focused. +- If the currently focused route has been removed, but the navigation state has other routes that are present in the new available list, the first route in from the list of rendered routes becomes focused. +- If none of the routes in the navigation state are present in the new available list of routes, one of the following things can happen based on the `UNSTABLE_routeNamesChangeBehavior` prop: + - `'firstMatch'` - The first route defined in the new list of routes becomes focused. This is the default behavior based on [`getStateForRouteNamesChange`](custom-routers.md) in the router. + - `'lastUnhandled'` - The last state that was unhandled due to conditional rendering is restored. + +Example cases where state might have been unhandled: + +- Opened a deep link to a screen, but a login screen was shown. +- Navigated to a screen containing a navigator, but a different screen was shown. +- Reset the navigator to a state with different routes not matching the available list of routes. + +In these cases, specifying `'lastUnhandled'` will reuse the unhandled state if present. If there's no unhandled state, it will fallback to `'firstMatch'` behavior. + +Caveats: + +- Direct navigation is only handled for `NAVIGATE` actions. +- Unhandled state is restored only if the current state becomes invalid, i.e. it doesn't contain any currently defined screens. + +Example usage: + + + + +```js +const RootStack = createNativeStackNavigator({ + // highlight-next-line + UNSTABLE_routeNamesChangeBehavior: 'lastUnhandled', + screens: { + Home: { + if: useIsSignedIn, + screen: HomeScreen, + }, + SignIn: { + if: useIsSignedOut, + screen: SignInScreen, + options: { + title: 'Sign in', + }, + }, + }, +}); +``` + + + + +```js + + {isSignedIn ? ( + + ) : ( + + )} + +``` + + + + +The most common use case for this is to [show the correct screen based on authentication based on deep link](auth-flow.md#handling-deep-links-after-auth). diff --git a/versioned_docs/version-7.x/params.md b/versioned_docs/version-7.x/params.md index 47124f64d5c..4905bd4ef55 100755 --- a/versioned_docs/version-7.x/params.md +++ b/versioned_docs/version-7.x/params.md @@ -193,9 +193,11 @@ export default function App() { } ``` +The `setParams` method merges the new params with the existing ones. To replace the existing params, you can use [`replaceParams`](navigation-object.md#replaceparams) instead. + :::note -Avoid using `setParams` to update screen options such as `title` etc. If you need to update options, use [`setOptions`](navigation-object.md#setoptions) instead. +Avoid using `setParams` or `replaceParams` to update screen options such as `title` etc. If you need to update options, use [`setOptions`](navigation-object.md#setoptions) instead. ::: @@ -371,6 +373,17 @@ export default function App() { See [Nesting navigators](nesting-navigators.md) for more details on nesting. +## Reserved param names + +Some param names are reserved by React Navigation as part of the API for nested navigators. The list of the reserved param names are as follows: + +- `screen` +- `params` +- `initial` +- `state` + +You should avoid using these param names in your code unless navigating to a screen containing a nested navigator. Otherwise it will result in unexpected behavior, such as the screen not being able to access the params you passed. If you need to pass data to a nested screen, use a different names for the param. + ## What should be in params Params are essentially options for a screen. They should contain the minimal data required to show a screen, nothing more. If the data is used by multiple screens, it should be in a global store or global cache. Params is not designed for state management. @@ -421,8 +434,9 @@ In essence, pass the least amount of data required to identify a screen in param ## Summary -- `navigate` and `push` accept an optional second argument to let you pass parameters to the route you are navigating to. For example: `navigation.navigate('RouteName', { paramName: 'value' })`. -- You can read the params through `route.params` inside a screen -- You can update the screen's params with `navigation.setParams` -- Initial params can be passed via the `initialParams` prop on `Screen` +- [`navigate`](navigation-actions.md#navigate) and [`push`](stack-actions.md#push) accept an optional second argument to let you pass parameters to the route you are navigating to. For example: `navigation.navigate('RouteName', { paramName: 'value' })`. +- You can read the params through [`route.params`](route-object.md) inside a screen +- You can update the screen's params with [`navigation.setParams`](navigation-object.md#setparams) or [`navigation.replaceParams`](navigation-object.md#replaceparams) +- Initial params can be passed via the [`initialParams`](screen.md#initial-params) prop on `Screen` or in the navigator config - Params should contain the minimal data required to show a screen, nothing more +- Some [param names are reserved](#reserved-param-names) by React Navigation and should be avoided diff --git a/versioned_docs/version-7.x/screen-tracking.md b/versioned_docs/version-7.x/screen-tracking.md index 26e6a0970ec..87786b0bfe4 100644 --- a/versioned_docs/version-7.x/screen-tracking.md +++ b/versioned_docs/version-7.x/screen-tracking.md @@ -181,3 +181,9 @@ export default function App() { + +:::note + +If you are building a library that wants to provide screen tracking integration with React Navigation, you can accept a [`ref`](navigation-container.md#ref) to the navigation container and use the [`ready`](navigation-container.md#ready) and [`state`](navigation-container.md#state) events instead of `onReady` and `onStateChange` props to keep your logic self-contained. + +::: diff --git a/versioned_docs/version-7.x/shared-element-transitions.md b/versioned_docs/version-7.x/shared-element-transitions.md index 7b4f50d6888..18d5fd917f0 100644 --- a/versioned_docs/version-7.x/shared-element-transitions.md +++ b/versioned_docs/version-7.x/shared-element-transitions.md @@ -7,12 +7,14 @@ sidebar_label: Shared element transitions import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; -This guide covers how to animate elements between screens. This feature is known as a [Shared Element Transition](https://docs.swmansion.com/react-native-reanimated/docs/api/sharedElementTransitions) and it's implemented in the [`@react-navigation/native-stack`](native-stack-navigator.md) with [React Native Reanimated](https://docs.swmansion.com/react-native-reanimated/). +This guide covers how to animate elements between screens. This feature is known as a [Shared Element Transition](https://docs.swmansion.com/react-native-reanimated/docs/shared-element-transitions/overview/) and it's implemented in the [`@react-navigation/native-stack`](native-stack-navigator.md) with [React Native Reanimated](https://docs.swmansion.com/react-native-reanimated/). :::warning As of writing this guide, Shared Element Transitions are considered an experimental feature not recommended for production use. +Shared Element Transitions are currently only supported on **old React Native architecture** (Paper). + :::