Skip to main content

createBottomTabNavigator

A simple tab bar on the bottom of the screen that lets you switch between different routes.

github
View source code
import { createBottomTabNavigator } from '@uhg-abyss/mobile';
Using React Navigation v7

On React Navigation v7, the v6 navigator types no longer satisfy TypedNavigator. Import the drop‑in v7 cast instead — it's runtime‑identical, only the types differ:

import { createBottomTabNavigatorV7 } from '@uhg-abyss/mobile';
const Tab = createBottomTabNavigatorV7();

To render the native (iOS 26 Liquid Glass) tab bar on iOS while keeping this JS tab bar on Android, see Liquid Glass Tabs.

The most common style of navigation in mobile apps is tab-based navigation. A simple tab bar on the bottom of the screen lets you switch between different routes. Routes are lazily initialized — their screen components are not mounted until they are first focused.

Calling the createBottomTabNavigator function creates a Tab object with Screen & Navigator properties. All tab screens must be wrapped around a Tab.Navigator object component. All screens must have a name and component passed in as props.

Example​

const Tab = createBottomTabNavigator();
const Screen = ({ route }) => {
return (
<ScreenWrapper>
<Text>This is the {route.name} page</Text>
</ScreenWrapper>
);
};
export default function App() {
return (
<NavigationContainer>
<Tab.Navigator initialRouteName="Home">
<Tab.Screen
name="Home"
component={Screen}
options={{
tabBarIcon: ({ active }) => (
<IconCustom
icon="home"
variant={active ? 'darkactive' : 'dark'}
maxFontSizeMultiplier={1.3}
/>
),
}}
/>
<Tab.Screen
name="My Plan"
component={Screen}
options={{
tabBarIcon: ({ active }) => (
<IconCustom
icon="my_plan"
variant={active ? 'darkactive' : 'dark'}
maxFontSizeMultiplier={1.3}
/>
),
}}
/>
<Tab.Screen
name="Find Care"
component={Screen}
options={{
tabBarIcon: ({ active }) => (
<IconCustom
icon="find_care"
variant={active ? 'darkactive' : 'dark'}
maxFontSizeMultiplier={1.3}
/>
),
}}
/>
<Tab.Screen
name="Menu"
component={Screen}
options={{
tabBarIcon: ({ active }) => (
<IconCustom
icon="menu"
variant={active ? 'darkactive' : 'dark'}
maxFontSizeMultiplier={1.3}
/>
),
}}
/>
</Tab.Navigator>
</NavigationContainer>
);
}
() => {
const [page, setPage] = useState('home');
const Container = styled('View', {
flexGrow: 1,
});
const Header = styled('View', {
backgroundColor: '$app-bar.color.surface.container',
width: '100%',
paddingTop: 30,
});
const HeaderContent = styled('View', {
margin: '$semantic.spacing.sm',
flexDirection: 'row',
justifyContent: 'center',
});
const Title = styled('Text', {
color: '$app-bar.color.text.heading',
typography: '$semantic.typography.h.xxs-bold',
textTransform: 'capitalize',
});
const ContentArea = styled('View', {
flexGrow: 1,
alignItems: 'center',
justifyContent: 'center',
});
const TabBar = styled('View', {
backgroundColor: '$tab-bar.color.surface.container',
display: 'flex',
paddingBottom: 24,
paddingTop: 8,
flexDirection: 'row',
borderTopLeftRadius: 12,
borderTopRightRadius: 12,
});
const TabBarItem = styled('Pressable', {
flex: 1,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
});
const ContentText = styled('Text', {});
const TabLabel = styled('Text', {
typography: '$semantic.typography.p.xs-med',
color: '$tab-bar.color.text.label.default',
variants: {
active: {
true: { fontWeight: '$core.font-weight.bold' },
},
},
});
return (
<Container>
<Header>
<HeaderContent>
<Title>{page.split('_').join(' ')}</Title>
</HeaderContent>
</Header>
<ContentArea>
{page === 'home' && <ContentText>This is the Home page</ContentText>}
{page === 'my_plan' && (
<ContentText>This is the My Plan page</ContentText>
)}
{page === 'find_care' && (
<ContentText>This is the Find Care page</ContentText>
)}
{page === 'menu' && <ContentText>This is the Menu page</ContentText>}
</ContentArea>
<TabBar>
<TabBarItem
role="button"
aria-current={page === 'home' ? 'page' : false}
onPressIn={() => {
setPage('home');
}}
>
{({ pressed }) => {
const active = page === 'home';
return (
<React.Fragment>
<IconCustom
icon="home"
variant={`dark${pressed || active ? 'active' : ''}`}
/>
<TabLabel active={active}>Home</TabLabel>
</React.Fragment>
);
}}
</TabBarItem>
<TabBarItem
role="button"
aria-current={page === 'my_plan' ? 'page' : false}
onPressIn={() => {
setPage('my_plan');
}}
>
{({ pressed }) => {
const active = page === 'my_plan';
return (
<React.Fragment>
<IconCustom
icon="my_plan"
variant={`dark${pressed || active ? 'active' : ''}`}
/>
<TabLabel active={active}>My Plan</TabLabel>
</React.Fragment>
);
}}
</TabBarItem>
<TabBarItem
role="button"
aria-current={page === 'find_care' ? 'page' : false}
onPressIn={() => {
setPage('find_care');
}}
>
{({ pressed }) => {
const active = page === 'find_care';
return (
<React.Fragment>
<IconCustom
icon="find_care"
variant={`dark${pressed || active ? 'active' : ''}`}
/>
<TabLabel active={active}>Find Care</TabLabel>
</React.Fragment>
);
}}
</TabBarItem>
<TabBarItem
role="button"
aria-current={page === 'menu' ? 'page' : false}
onPressIn={() => {
setPage('menu');
}}
>
{({ pressed }) => {
const active = page === 'menu';
return (
<React.Fragment>
<IconCustom
icon="menu"
variant={`dark${pressed || active ? 'active' : ''}`}
/>
<TabLabel active={active}>Menu</TabLabel>
</React.Fragment>
);
}}
</TabBarItem>
</TabBar>
</Container>
);
};

Props​

The Tab.Navigator component accepts following props:

id​

Optional unique ID for the navigator. This can be used with navigation.getParent to refer to this navigator in a child navigator.

initialRouteName​

The name of the route to render on first load of the navigator.

screenOptions​

Default options to use for the screens in the navigator.

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
  • none - do not handle back button

detachInactiveScreens​

Boolean used to indicate whether inactive screens should be detached from the view hierarchy to save memory. This enables integration with react-native-screens. Defaults to true.

sceneContainerStyle​

Style object for the component wrapping the screen content. On React Navigation v7 this is applied through the per‑screen sceneStyle option internally, so it keeps working on both v6 and v7.

tabBar​

Function that returns a React element to display as the tab bar.

Important

You cannot use the useNavigation hook inside the tabBar since useNavigation is only available inside screens. You get a navigation prop for your tabBar which you can use instead.

Options​

The following options 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.

sceneStyle​

Style object for the component wrapping the screen content, applied per screen — the per‑screen equivalent of the navigator's sceneContainerStyle.

tabBarLabel​

Title string of a tab displayed in the tab bar or a function that given { active: boolean, color: string } returns a React.Node, to display in tab bar. When undefined, scene title is used. To hide, see tabBarShowLabel.

tabBarShowLabel​

Whether the tab label should be visible. Defaults to true.

tabBarLabelPosition​

Whether the label is shown below the icon or beside the icon.

  • below-icon: the label is shown below the icon (typical for iPhones)
  • beside-icon the label is shown next to the icon (typical for iPad)

By default, the position is chosen automatically based on device width.

tabBarLabelStyle​

Style object for the tab label.

tabBarIcon​

React.ReactNode or a function that given { active: boolean } returns a React.ReactNode, to display in the tab bar.

showTabBarBadge​

Whether or not the badge should be visible on the tab icon.

showTabBarBadgeOnActive​

Whether or not the badge should be visible on the tab icon when it is active.

tabBarBadgeLabel​

Text to show in a badge on the tab icon. Accepts a string or a number.

tabBarBadgeOffset​

Offset for the badge on the tab icon.

tabBarAccessibilityLabel​

Accessibility label for the tab button. This is read by the screen reader when the user taps the tab. It's recommended to set this if you don't have a label for the tab.

tabBarTestID​

ID to locate this tab button in tests.

tabBarActiveTintColor​

Color for the icon and label in the active tab.

tabBarInactiveTintColor​

Color for the icon and label in the inactive tabs.

tabBarActiveBackgroundColor​

Background color for the active tab.

tabBarInactiveBackgroundColor​

Background color for the inactive tabs.

tabBarActiveLabelWeight​

Font weight for the label in the active tab.

tabBarInactiveLabelWeight​

Font weight for the label in the inactive tabs.

tabBarItemStyle​

Style object for the tab item container.

tabBarStyle​

Style object for the tab bar. You can configure styles such as background color here.

To show your screen under the tab bar, you can set the position style to absolute:

<Tab.Navigator
screenOptions={{
tabBarStyle: { position: 'absolute' },
}}
>

Dynamic Type​

Text and icons scale to 3XL. Any additional text or icon passed in should set maxFontSizeMultiplier={1.3}.

Table of Contents