Skip to main content
Version: 3.x

Tab navigation

Possibly the most common style of navigation in mobile apps is tab-based navigation. This can be tabs on the bottom of the screen or on the top below the header (or even instead of a header).

This guide covers createBottomTabNavigator. You may also use createMaterialBottomTabNavigator and createMaterialTopTabNavigator to add tabs to your application.

Minimal example of tab-based navigation

import React from 'react';
import { Text, View } from 'react-native';
import { createBottomTabNavigator, createAppContainer } from 'react-navigation';

class HomeScreen extends React.Component {
render() {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Home!</Text>
</View>
);
}
}

class SettingsScreen extends React.Component {
render() {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Settings!</Text>
</View>
);
}
}

const TabNavigator = createBottomTabNavigator({
Home: HomeScreen,
Settings: SettingsScreen,
});

export default createAppContainer(TabNavigator);
→ Run this code

Customizing the appearance

This is similar to how you would customize a stack navigator — there are some properties that are set when you initialize the tab navigator and others that can be customized per-screen in navigationOptions.

// You can import Ionicons from @expo/vector-icons if you use Expo or
// react-native-vector-icons/Ionicons otherwise.
import Ionicons from 'react-native-vector-icons/Ionicons';
import { createBottomTabNavigator, createAppContainer } from 'react-navigation';

export default createBottomTabNavigator(
{
Home: HomeScreen,
Settings: SettingsScreen,
},
{
defaultNavigationOptions: ({ navigation }) => ({
tabBarIcon: ({ focused, horizontal, tintColor }) => {
const { routeName } = navigation.state;
let IconComponent = Ionicons;
let iconName;
if (routeName === 'Home') {
iconName = `ios-information-circle${focused ? '' : '-outline'}`;
// Sometimes we want to add badges to some icons.
// You can check the implementation below.
IconComponent = HomeIconWithBadge;
} else if (routeName === 'Settings') {
iconName = `ios-options`;
}

// You can return any component that you like here!
return <IconComponent name={iconName} size={25} color={tintColor} />;
},
}),
tabBarOptions: {
activeTintColor: 'tomato',
inactiveTintColor: 'gray',
},
}
);
→ Run this code

Let's dissect this:

  • tabBarIcon is a property on navigationOptions, so we know we can use it on our screen components, but in this case chose to put it in the createBottomTabNavigator configuration in order to centralize the icon configuration for convenience.
  • tabBarIcon is a function that is given the focused state, tintColor, and horizontal param, which is a boolean. If you take a peek further down in the configuration you will see tabBarOptions and activeTintColor and inactiveTintColor. These default to the iOS platform defaults, but you can change them here. The tintColor that is passed through to the tabBarIcon is either the active or inactive one, depending on the focused state (focused is active). The orientation state horizontal is true when the device is in landscape, otherwise is false for portrait.
  • Read the full API reference for further information on createBottomTabNavigator configuration options.

Add badges to icons

Sometimes we want to add badges to some icons. A common way is to use an extra view container and style the badge element with absolute positioning.

export default class IconWithBadge extends React.Component {
render() {
const { name, badgeCount, color, size } = this.props;
return (
<View style={{ width: 24, height: 24, margin: 5 }}>
<Ionicons name={name} size={size} color={color} />
{ badgeCount > 0 && (
<View style={{
// If you're using react-native < 0.57 overflow outside of the parent
// will not work on Android, see https://git.io/fhLJ8
position: 'absolute',
right: -6,
top: -3,
backgroundColor: 'red',
borderRadius: 6,
width: 12,
height: 12,
justifyContent: 'center',
alignItems: 'center'
}}>
<Text style={{ color: 'white', fontSize: 10, fontWeight: 'bold' }}>{badgeCount}</Text>
</View>
)}
</View>
);
}
}

From UI perspective this component is ready to use, but you still need to find some way to pass down the badge count properly from somewhere else, like using React Context, Redux, MobX or event emitters.

const HomeIconWithBadge = (props) => {
// You should pass down the badgeCount in some other ways like React Context API, Redux, MobX or event emitters.
return <IconWithBadge {...props} badgeCount={3} />;
}
export default HomeIconWithBadge

Jumping between tabs

Switching from one tab to another has a familiar API — this.props.navigation.navigate.

import { Button, Text, View } from 'react-native';

class HomeScreen extends React.Component {
render() {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Home!</Text>
<Button
title="Go to Settings"
onPress={() => this.props.navigation.navigate('Settings')}
/>
</View>
);
}
}

class SettingsScreen extends React.Component {
render() {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Settings!</Text>
<Button
title="Go to Home"
onPress={() => this.props.navigation.navigate('Home')}
/>
</View>
);
}
}
→ Run this code

A stack navigator for each tab

Usually tabs don't just display one screen — for example, on your Twitter feed, you can tap on a tweet and it brings you to a new screen within that tab with all of the replies. You can think of this as there being separate navigation stacks within each tab, and that's exactly how we will model it in React Navigation.

import {
createBottomTabNavigator,
createStackNavigator,
createAppContainer,
} from 'react-navigation';

class DetailsScreen extends React.Component {
render() {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Details!</Text>
</View>
);
}
}

class HomeScreen extends React.Component {
render() {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
{/* other code from before here */}
<Button
title="Go to Details"
onPress={() => this.props.navigation.navigate('Details')}
/>
</View>
);
}
}

class SettingsScreen extends React.Component {
render() {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
{/* other code from before here */}
<Button
title="Go to Details"
onPress={() => this.props.navigation.navigate('Details')}
/>
</View>
);
}
}

const HomeStack = createStackNavigator({
Home: HomeScreen,
Details: DetailsScreen,
});

const SettingsStack = createStackNavigator({
Settings: SettingsScreen,
Details: DetailsScreen,
});

export default createAppContainer(createBottomTabNavigator(
{
Home: HomeStack,
Settings: SettingsStack,
},
{
/* Other configuration remains unchanged */
}
));
→ Run this code

Why do we need a TabNavigator instead of TabBarIOS or some other component?

It's common to attempt to use a standalone tab bar component without integrating it into the navigation library you use in your app. In some cases, this works fine! You should be warned, however, that you may run into some frustrating unanticipated issues when doing this.

For example, React Navigation's TabNavigator takes care of handling the Android back button for you, while standalone components typically do not. Additionally, it is more difficult for you (as the developer) to perform actions such as "jump to this tab and then go to this screen" if you need to call into two distinct APIs for it. Lastly, mobile user interfaces have numerous small design details that require that certain components are aware of the layout or presence of other components — for example, if you have a translucent tab bar, content should scroll underneath it and the scroll view should have an inset on the bottom equal to the height of the tab bar so you can see all of the content. Double tapping the tab bar should make the active navigation stack pop to the top of the stack, and doing it again should scroll the active scroll view in that stack scroll to the top. While not all of these behaviors are implemented out of the box yet with React Navigation, they will be and you will not get any of this if you use a standalone tab view component.

A tab navigator contains a stack and you want to hide the tab bar on specific screens

See the documentation here

A tab icon that doesn't navigate

If you want a tab icon that never actually opens a stack navigator, simple provide the tabBarOnPress callback for the tab's navigationOptions without invoking the provided defaultHandler method.

createBottomTabNavigator({
...,
Placeholder: {
screen: () => null,
navigationOptions: {
tabBarOnPress: () => {
// do something custom here
},
},
}
...
});