Tabs
You'll learn to add a bottom tab bar with Expo Router, with a title and an icon for each tab.
Watch & read
Start with the required ones. The others are there when you need them.
Explanation
A stack is for going deeper: list, then details, then edit. Tabs are for sections that are always one tap away, side by side. Think of the bar at the bottom of Facebook or bKash: Home, Search, Inbox. You don't go "back" from one to another. You just switch.
In Expo Router, tabs are a folder with a _layout.jsx that returns <Tabs>:
src/app/_layout.jsx <Stack>: the whole appsrc/app/(tabs)/_layout.jsx <Tabs>: the tab barsrc/app/(tabs)/index.jsx first tab, Today, route "/"src/app/(tabs)/stats.jsx second tab, Stats, route "/stats"src/app/habit/[id].jsx opens on top of the tabs
The brackets in (tabs) make a group. The folder name does not appear in the route, so
(tabs)/stats.jsx is just /stats. Every file in the folder becomes a tab, even ones you don't list.
<Tabs screenOptions={{ tabBarActiveTintColor: "#C6F432" }}><Tabs.Screen name="index" options={{ title: "Today" }} /><Tabs.Screen name="stats" options={{ title: "Stats" }} /></Tabs>
Tabs.Screen sets the order and options for one tab. name is the file name without .jsx.
tabBarIcon draws the icon: a function that gets color (and focused) and returns an icon. The Expo
tutorial uses Ionicons from @expo/vector-icons: <Ionicons name="today" color={color} size={24} />.
Browse the names at icons.expo.fyi. Expo has said it will move to the @react-native-vector-icons
packages later; the way you use an icon stays the same.
The root _layout.jsx is still a Stack, with the tabs as its first screen and their header hidden. That
is why a habit's details open on top of the tab bar and Back returns to the tab you were on.
A brand new Expo project uses native tabs (NativeTabs), which draw the phone's own tab bar. You'll
see them in the template. The ideas are the same; we use Tabs because the tutorial and most videos do.
People often name the file and the screen differently. stats.jsx with <Tabs.Screen name="Stats">
does not match (names are case-sensitive). Expo Router warns that no route has that name, and the tab shows without your title and icon. Use the exact file name.
When you read tabs, list the files in the (tabs) folder: that is your tab bar. Then read _layout.jsx for
the order, titles and icons.
Code tour
Click a line number with a ring to read the note for that line.
Practice
Short drills. Take your time and read the code before you answer.
1// Files in src/app/(tabs):2// _layout.jsx3// index.jsx4// stats.jsx5// settings.jsx67// src/app/(tabs)/_layout.jsx8export default function TabLayout() {9 return (10 <Tabs>11 <Tabs.Screen name="index" options={{ title: "Today" }} />12 <Tabs.Screen name="stats" options={{ title: "Stats" }} />13 </Tabs>14 );15}
1import Ionicons from "@expo/vector-icons/Ionicons";2import { NavigationContainer } from "@react-navigation/native";3import { createBottomTabNavigator } from "@react-navigation/bottom-tabs";4import { StyleSheet, Text, View } from "react-native";56const Tab = createBottomTabNavigator();
Runs in the browser. Choose “My Device” in Snack to try it on your phone with Expo Go.
screenOptions={{ tabBarActiveTintColor: "#C6F432" }} to the navigator.A third tab, Settings, shows its own screen and icon.Quick check
3 questions. Each one explains itself after you answer.
- 1 / 3What is the route of
src/app/(tabs)/stats.jsx? - 2 / 3Which part of a habit tracker fits tabs best?
- 3 / 3In
tabBarIcon: ({ color }) => <Ionicons name="today" color={color} size={24} />, where doescolorcome from?
Explain it back
How confident are you with Tabs?
Done
(tabs) folder and its _layout.jsx and say which tabs appear, in what order, with which icons.