we move beyond stack-based navigation to implement Tab Navigation. This is the standard "bottom bar" navigation seen in most mobile apps, allowing users to switch between top-level views like a Profile, a Feed, or a Creation screen.
While Stack navigation handles screens that sit on top of each other, Tabs provide a bottom-navigation bar that allows users to switch between top-level sections of an app quickly.
To organize pages visible only to logged-in users, we use a Route Group (folder name in parentheses). This keeps the URL clean while grouping files.
Just like our (auth) group, we wrap the folder name in parentheses so it we don’t have to include the “foldername in url” so we can direct just write the name of the file_0r_component name in the URL
/app/(dashboard)/profile.jsx: User details and logout.books.jsx: List of all books.create.jsx: Form to add new books.To implement tab navigation, create a _layout.jsx inside the (dashboard) folder.
File: ./app/(dashboard)/_layout.jsx
import { Tabs } from "expo-router";
import { useColorScheme } from "react-native";
import { Colors } from "../../constants/Colors";
export default function DashboardLayout() {
const colorScheme = useColorScheme();
const theme = Colors[colorScheme] ?? Colors.light;
return (
<Tabs
screenOptions={{
headerShown: false, // Hide the top header bar
tabBarStyle: {
backgroundColor: theme.navBackground,
height: 90,
paddingTop: 10,
},
tabBarActiveTintColor: theme.iconColorFocused,
tabBarInactiveTintColor: theme.iconColor,
}}
>
<Tabs.Screen name="books" options={{ title: "Books" }} />
<Tabs.Screen name="create" options={{ title: "Create" }} />
<Tabs.Screen name="profile" options={{ title: "Profile" }} />
</Tabs>
);
}
| Property | Description |
|---|---|
screenOptions |
Global settings applied to every tab in this layout. |
tabBarActiveTintColor |
The color of the label/icon when the tab is currently selected. |
tabBarStyle |
Customizes the physical bar at the bottom (height, background color). |
Tabs.Screen |
Used inside <Tabs> to override settings for specific pages (like changing the label to Uppercase). |
To prevent double headers (one from the root Stack and one from the Tabs), ensure the (dashboard) group is hidden in your main layout.
File: ./app/_layout.jsx
<Stack>
<Stack.Screen name="(auth)" options={{ headerShown: false }} />
<Stack.Screen name="(dashboard)" options={{ headerShown: false }} />
</Stack>
Pro Tip: If your navigation changes don't show up immediately, reload the app manually (shake the phone or press 'r' in the terminal). Live reload sometimes misses structural navigation updates.