|
| 1 | +--- |
| 2 | +id: native-bottom-tab-navigator |
| 3 | +title: Native Bottom Tabs Navigator |
| 4 | +sidebar_label: Native Bottom Tabs |
| 5 | +--- |
| 6 | + |
| 7 | +:::warning |
| 8 | + |
| 9 | +This navigator is currently experimental. The API will change in future releases. |
| 10 | + |
| 11 | +Currently only iOS and Android are supported. Use [`createBottomTabNavigator`](bottom-tab-navigator.md) for web support. |
| 12 | + |
| 13 | +::: |
| 14 | + |
| 15 | +Native Bottom Tabs displays screens with a tab bar to switch between them. |
| 16 | + |
| 17 | +The navigator uses native components on iOS and Android for better platform integration. On iOS, it uses `UITabBarController` and on Android, it uses `BottomNavigationView`. |
| 18 | + |
| 19 | +## Installation |
| 20 | + |
| 21 | +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): |
| 22 | + |
| 23 | +```bash npm2yarn |
| 24 | +npm install @react-navigation/bottom-tabs |
| 25 | +``` |
| 26 | + |
| 27 | +## Usage |
| 28 | + |
| 29 | +To use this navigator, import it from `@react-navigation/bottom-tabs/unstable`: |
| 30 | + |
| 31 | +<Tabs groupId="config" queryString="config"> |
| 32 | +<TabItem value="static" label="Static" default> |
| 33 | + |
| 34 | +```js name="Bottom Tab Navigator" |
| 35 | +import { createNativeBottomTabNavigator } from '@react-navigation/bottom-tabs/unstable'; |
| 36 | + |
| 37 | +const MyTabs = createNativeBottomTabNavigator({ |
| 38 | + screens: { |
| 39 | + Home: HomeScreen, |
| 40 | + Profile: ProfileScreen, |
| 41 | + }, |
| 42 | +}); |
| 43 | +``` |
| 44 | + |
| 45 | +</TabItem> |
| 46 | +<TabItem value="dynamic" label="Dynamic"> |
| 47 | + |
| 48 | +```js name="Bottom Tab Navigator" |
| 49 | +import { createNativeBottomTabNavigator } from '@react-navigation/bottom-tabs/unstable'; |
| 50 | + |
| 51 | +const Tab = createNativeBottomTabNavigator(); |
| 52 | + |
| 53 | +function MyTabs() { |
| 54 | + return ( |
| 55 | + <Tab.Navigator> |
| 56 | + <Tab.Screen name="Home" component={HomeScreen} /> |
| 57 | + <Tab.Screen name="Profile" component={ProfileScreen} /> |
| 58 | + </Tab.Navigator> |
| 59 | + ); |
| 60 | +} |
| 61 | +``` |
| 62 | + |
| 63 | +</TabItem> |
| 64 | +</Tabs> |
| 65 | + |
| 66 | +## API Definition |
| 67 | + |
| 68 | +### Props |
| 69 | + |
| 70 | +In addition to the [common props](navigator.md#configuration) shared by all navigators, the bottom tab navigator accepts the following additional props: |
| 71 | + |
| 72 | +#### `backBehavior` |
| 73 | + |
| 74 | +This controls what happens when `goBack` is called in the navigator. This includes pressing the device's back button or back gesture on Android. |
| 75 | + |
| 76 | +It supports the following values: |
| 77 | + |
| 78 | +- `firstRoute` - return to the first screen defined in the navigator (default) |
| 79 | +- `initialRoute` - return to initial screen passed in `initialRouteName` prop, if not passed, defaults to the first screen |
| 80 | +- `order` - return to screen defined before the focused screen |
| 81 | +- `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 |
| 82 | +- `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 |
| 83 | +- `none` - do not handle back button |
| 84 | + |
| 85 | +### Options |
| 86 | + |
| 87 | +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`. |
| 88 | + |
| 89 | +#### `title` |
| 90 | + |
| 91 | +Generic title that can be used as a fallback for `headerTitle` and `tabBarLabel`. |
| 92 | + |
| 93 | +#### `tabBarSystemItem` |
| 94 | + |
| 95 | +Uses iOS built-in tab bar items with standard iOS styling and localized titles. |
| 96 | + |
| 97 | +If set to `search`, it's positioned next to the tab bar on iOS 26 and above. |
| 98 | + |
| 99 | +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. |
| 100 | + |
| 101 | +#### `tabBarLabel` |
| 102 | + |
| 103 | +Title string of a tab displayed in the tab bar. |
| 104 | + |
| 105 | +Overrides the label provided by [`tabBarSystemItem`](#tabbarsystemitem) on iOS. |
| 106 | + |
| 107 | +If not provided, or set to `undefined`: |
| 108 | + |
| 109 | +- The system values are used if [`tabBarSystemItem`](#tabbarsystemitem) is set on iOS. |
| 110 | +- Otherwise, it falls back to the [`title`](#title) or route name. |
| 111 | + |
| 112 | +#### `tabBarLabelVisibilityMode` |
| 113 | + |
| 114 | +The label visibility mode for the tab bar items. Supported values: |
| 115 | + |
| 116 | +- `auto` - the system decides when to show or hide labels |
| 117 | +- `selected` - labels are shown only for the selected tab |
| 118 | +- `labeled` - labels are always shown |
| 119 | +- `unlabeled` - labels are never shown |
| 120 | + |
| 121 | +Only supported on Android. |
| 122 | + |
| 123 | +#### `tabBarLabelStyle` |
| 124 | + |
| 125 | +Style object for the tab label. Supported properties: |
| 126 | + |
| 127 | +- `fontFamily` |
| 128 | +- `fontSize` |
| 129 | +- `fontWeight` |
| 130 | +- `fontStyle` |
| 131 | + |
| 132 | +Example: |
| 133 | + |
| 134 | +```js |
| 135 | +tabBarLabelStyle: { |
| 136 | + fontSize: 16, |
| 137 | + fontFamily: 'Georgia', |
| 138 | + fontWeight: 300, |
| 139 | +}, |
| 140 | +``` |
| 141 | + |
| 142 | +#### `tabBarIcon` |
| 143 | + |
| 144 | +Icon to display for the tab. It overrides the icon provided by [`tabBarSystemItem`](#tabbarsystemitem) on iOS. |
| 145 | + |
| 146 | +It can be an icon object or a function that given `{ focused: boolean, color: string, size: number }` returns an icon object. |
| 147 | + |
| 148 | +The icon can be of following types: |
| 149 | + |
| 150 | +- Local image - Supported on iOS and Android |
| 151 | + |
| 152 | + ```js |
| 153 | + tabBarIcon: { |
| 154 | + type: 'image', |
| 155 | + source: require('./path/to/icon.png'), |
| 156 | + } |
| 157 | + ``` |
| 158 | + |
| 159 | + On iOS, you can additionally pass a `tinted` property to control whether the icon should be tinted with the active/inactive color: |
| 160 | + |
| 161 | + ```js |
| 162 | + tabBarIcon: { |
| 163 | + type: 'image', |
| 164 | + source: require('./path/to/icon.png'), |
| 165 | + tinted: false, // defaults to false |
| 166 | + } |
| 167 | + ``` |
| 168 | + |
| 169 | + The image is tinted by default. |
| 170 | + |
| 171 | +- [SF Symbols](https://developer.apple.com/sf-symbols/) name - Supported on iOS |
| 172 | + |
| 173 | + ```js |
| 174 | + tabBarIcon: { |
| 175 | + type: 'sfSymbol', |
| 176 | + name: 'heart', |
| 177 | + } |
| 178 | + ``` |
| 179 | + |
| 180 | +- Drawable resource name - Supported on Android |
| 181 | + |
| 182 | + ```js |
| 183 | + tabBarIcon: { |
| 184 | + type: 'drawable', |
| 185 | + name: 'sunny', |
| 186 | + } |
| 187 | + ``` |
| 188 | + |
| 189 | +To render different icons for active and inactive states, you can use a function: |
| 190 | + |
| 191 | +```js |
| 192 | +tabBarIcon: ({ focused }) => { |
| 193 | + return { |
| 194 | + type: 'sfSymbol', |
| 195 | + name: focused ? 'heart' : 'heart-outline', |
| 196 | + }; |
| 197 | +}, |
| 198 | +``` |
| 199 | + |
| 200 | +This is only supported on iOS. On Android, the icon specified for inactive state will be used for both active and inactive states. |
| 201 | + |
| 202 | +To provide different icons for different platforms, you can use [`Platform.select`](https://reactnative.dev/docs/platform-specific-code): |
| 203 | + |
| 204 | +```js |
| 205 | +tabBarIcon: Platform.select({ |
| 206 | + ios: { |
| 207 | + type: 'sfSymbol', |
| 208 | + name: 'heart', |
| 209 | + }, |
| 210 | + android: { |
| 211 | + type: 'drawable', |
| 212 | + name: 'heart_icon', |
| 213 | + }, |
| 214 | +}); |
| 215 | +``` |
| 216 | + |
| 217 | +#### `tabBarBadge` |
| 218 | + |
| 219 | +Text to show in a badge on the tab icon. Accepts a `string` or a `number`. |
| 220 | + |
| 221 | +#### `tabBarBadgeStyle` |
| 222 | + |
| 223 | +Style for the badge on the tab icon. Supported properties: |
| 224 | + |
| 225 | +- `backgroundColor` |
| 226 | +- `color` |
| 227 | + |
| 228 | +Example: |
| 229 | + |
| 230 | +```js |
| 231 | +tabBarBadgeStyle: { |
| 232 | + backgroundColor: 'yellow', |
| 233 | + color: 'black', |
| 234 | +}, |
| 235 | +``` |
| 236 | + |
| 237 | +#### `tabBarActiveTintColor` |
| 238 | + |
| 239 | +Color for the icon and label in the active tab. |
| 240 | + |
| 241 | +#### `tabBarInactiveTintColor` |
| 242 | + |
| 243 | +Color for the icon and label in the inactive tabs. |
| 244 | + |
| 245 | +Only supported on Android. |
| 246 | + |
| 247 | +#### `tabBarActiveIndicatorColor` |
| 248 | + |
| 249 | +Background color of the active indicator. |
| 250 | + |
| 251 | +Only supported on Android. |
| 252 | + |
| 253 | +#### `tabBarActiveIndicatorEnabled` |
| 254 | + |
| 255 | +Whether the active indicator should be used. Defaults to `true`. |
| 256 | + |
| 257 | +Only supported on Android. |
| 258 | + |
| 259 | +#### `tabBarRippleColor` |
| 260 | + |
| 261 | +Color of the ripple effect when pressing a tab. |
| 262 | + |
| 263 | +Only supported on Android. |
| 264 | + |
| 265 | +#### `tabBarStyle` |
| 266 | + |
| 267 | +Style object for the tab bar. Supported properties: |
| 268 | + |
| 269 | +- `backgroundColor` - Only supported on Android and iOS 18 and below. |
| 270 | +- `shadowColor` - Only supported on iOS 18 and below. |
| 271 | + |
| 272 | +#### `tabBarControllerMode` |
| 273 | + |
| 274 | +The display mode for the tab bar. Supported values: |
| 275 | + |
| 276 | +- `auto` - the system sets the display mode based on the tab’s content |
| 277 | +- `tabBar` - the system displays the content only as a tab bar |
| 278 | +- `tabSidebar` - the tab bar is displayed as a sidebar |
| 279 | + |
| 280 | +Only supported on iOS 18 and above. Not supported on tvOS. |
| 281 | + |
| 282 | +#### `tabBarMinimizeBehavior` |
| 283 | + |
| 284 | +The minimize behavior for the tab bar. Supported values: |
| 285 | + |
| 286 | +- `auto` - resolves to the system default minimize behavior |
| 287 | +- `never` - the tab bar does not minimize |
| 288 | +- `onScrollDown` - the tab bar minimizes when scrolling down and |
| 289 | + expands when scrolling back up |
| 290 | +- `onScrollUp` - the tab bar minimizes when scrolling up and expands |
| 291 | + when scrolling back down |
| 292 | + |
| 293 | +Only supported on iOS 26 and above. |
| 294 | + |
| 295 | +#### `lazy` |
| 296 | + |
| 297 | +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. |
| 298 | + |
| 299 | +#### `popToTopOnBlur` |
| 300 | + |
| 301 | +Boolean indicating whether any nested stack should be popped to the top of the stack when navigating away from this tab. Defaults to `false`. |
| 302 | + |
| 303 | +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. |
| 304 | + |
| 305 | +### Header related options |
| 306 | + |
| 307 | +The navigator renders a native stack header. It supports most of the [header related options supported in `@react-navigation/native-stack`](native-stack-navigator.md#headerrelatedoptions) apart from the options related to the back button (prefixed with `headerBack`). |
| 308 | + |
| 309 | +### Events |
| 310 | + |
| 311 | +The navigator can [emit events](navigation-events.md) on certain actions. Supported events are: |
| 312 | + |
| 313 | +#### `tabPress` |
| 314 | + |
| 315 | +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: |
| 316 | + |
| 317 | +- If the tab is not focused, tab press will focus that tab |
| 318 | +- If the tab is already focused: |
| 319 | + - If the screen for the tab renders a scroll view, you can use [`useScrollToTop`](use-scroll-to-top.md) to scroll it to top |
| 320 | + - If the screen for the tab renders a stack navigator, a `popToTop` action is performed on the stack |
| 321 | + |
| 322 | +To prevent the default behavior, you can call `event.preventDefault`: |
| 323 | + |
| 324 | +```js |
| 325 | +React.useEffect(() => { |
| 326 | + const unsubscribe = navigation.addListener('tabPress', (e) => { |
| 327 | + // Prevent default behavior |
| 328 | + e.preventDefault(); |
| 329 | + |
| 330 | + // Do something manually |
| 331 | + // ... |
| 332 | + }); |
| 333 | + |
| 334 | + return unsubscribe; |
| 335 | +}, [navigation]); |
| 336 | +``` |
| 337 | + |
| 338 | +#### `transitionStart` |
| 339 | + |
| 340 | +This event is fired when the transition animation starts for the current screen. |
| 341 | + |
| 342 | +Example: |
| 343 | + |
| 344 | +```js |
| 345 | +React.useEffect(() => { |
| 346 | + const unsubscribe = navigation.addListener('transitionStart', (e) => { |
| 347 | + // Do something |
| 348 | + }); |
| 349 | + |
| 350 | + return unsubscribe; |
| 351 | +}, [navigation]); |
| 352 | +``` |
| 353 | + |
| 354 | +#### `transitionEnd` |
| 355 | + |
| 356 | +This event is fired when the transition animation ends for the current screen. |
| 357 | + |
| 358 | +Example: |
| 359 | + |
| 360 | +```js |
| 361 | +React.useEffect(() => { |
| 362 | + const unsubscribe = navigation.addListener('transitionEnd', (e) => { |
| 363 | + // Do something |
| 364 | + }); |
| 365 | + |
| 366 | + return unsubscribe; |
| 367 | +}, [navigation]); |
| 368 | +``` |
| 369 | + |
| 370 | +### Helpers |
| 371 | + |
| 372 | +The tab navigator adds the following methods to the navigation object: |
| 373 | + |
| 374 | +#### `jumpTo` |
| 375 | + |
| 376 | +Navigates to an existing screen in the tab navigator. The method accepts following arguments: |
| 377 | + |
| 378 | +- `name` - _string_ - Name of the route to jump to. |
| 379 | +- `params` - _object_ - Screen params to use for the destination route. |
| 380 | + |
| 381 | +```js |
| 382 | +navigation.jumpTo('Profile', { owner: 'Michaś' }); |
| 383 | +``` |
0 commit comments