In the previous chapter, we established a solid foundation by setting up authentication and integrating Replyke into our project. Now, it’s time to build on that foundation by implementing the tab navigation structure that will serve as the backbone for your app’s user experience.
Tab navigation allows users to easily switch between key sections of your app, such as the feed, profile, and notifications. In this chapter, we’ll set up the pages for each tab and link them together with a navigation bar. By the end, your app will have a clean and intuitive navigation structure, making it easy for users to explore and interact with its features.
Expanding the App Directory
To begin, let’s create all the new screens needed for the app. After adding these screens, your app directory structure should look like this:
- app
- (tabs)
- _layout.tsx
- create.tsx
- index.tsx
- lists.tsx
- notifications.tsx
- profile
- _layout.tsx
- edit-bio.tsx
- edit-name.tsx
- edit-profile.tsx
- edit-username.tsx
- index.tsx
- settings.tsx
- account
- [accountId].tsx
- post
- [postId].tsx
- sign-in.tsx
- sign-up.tsx
- _layout.tsx
Creating Placeholder Screens
For now, all the new screens should contain placeholder content. Use the following template for each new file:
import { View, Text } from "react-native";
import React from "react";
const ScreenName = () => {
return (
<View>
<Text>ScreenName</Text>
</View>
);
};
export default ScreenName;
Replace ScreenName
with the name of each screen you’re creating, such as ListsScreen
for lists.tsx
. This ensures each screen is set up and ready to be populated later in the guide.
Understanding Dynamic Routes
You’ll notice files named [accountId].tsx
and [postId].tsx
. These are dynamic routes. In these cases, the brackets []
denote that the file name is a placeholder for a dynamic value. For example:
account/[accountId].tsx
: This screen is used when navigating to another user’s profile. TheaccountId
represents the specific user ID.post/[postId].tsx
: This screen is used when navigating to a specific post. ThepostId
represents the unique ID of the post.
Ensure that you write these file names exactly as shown, including the brackets, as they are case-sensitive and integral to the routing system.
Organizing the Profile Folder
The profile
folder serves as the base of the user’s profile stack. From the profile screen, users can navigate to various settings and editing options. Structuring it this way keeps all profile-related screens neatly organized and encapsulated within the profile
folder.
Adding the Tab Navigation Layout
Update the app/(tabs)/_layout.tsx
file to include the new tab screens with unique icons. Here is the complete code:
import { Tabs } from "expo-router";
import React from "react";
import Entypo from "@expo/vector-icons/Entypo";
import FontAwesome from "@expo/vector-icons/FontAwesome";
import AntDesign from '@expo/vector-icons/AntDesign';
import FontAwesome5 from "@expo/vector-icons/FontAwesome5";
export default function TabsLayout() {
return (
<Tabs
screenOptions={{
headerShown: false,
}}
>
<Tabs.Screen
name="index"
options={{
title: "Home",
tabBarIcon: ({ color }) => (
<Entypo name="home" size={28} color={color} />
),
}}
/>
<Tabs.Screen
name="lists"
options={{
title: "Lists",
tabBarIcon: ({ color }) => (
<FontAwesome name="bookmark" size={28} color={color} />
),
}}
/>
<Tabs.Screen
name="create"
options={{
title: "Create",
tabBarIcon: ({ color }) => (
<AntDesign name="pluscircleo" size={28} color={color} />
),
}}
/>
<Tabs.Screen
name="notifications"
options={{
title: "Notifications",
tabBarIcon: ({ color }) => (
<Entypo name="bell" size={28} color={color} />
),
}}
/>
<Tabs.Screen
name="profile"
options={{
title: "Profile",
tabBarIcon: ({ color }) => (
<FontAwesome5 name="user-alt" size={28} color={color} />
),
}}
/>
</Tabs>
);
}
Setting Up the Profile Stack Layout
The app/(tabs)/profile/_layout.tsx
file defines the stack navigation for profile-related screens. Use the following code:
import { Stack } from "expo-router";
export default function ProfileLayout() {
return (
<Stack
screenOptions={{
gestureEnabled: true, // Enable swipe gestures
animation: "default",
presentation: "transparentModal", // or 'modal', 'card', etc.
headerShown: false,
contentStyle: {
backgroundColor: "#fff",
},
}}
>
<Stack.Screen name="index" />
<Stack.Screen name="settings" />
<Stack.Screen name="edit-profile" />
<Stack.Screen name="edit-bio" />
<Stack.Screen name="edit-name" />
<Stack.Screen name="edit-username" />
</Stack>
);
}
With these layouts in place, your app is now ready for tab navigation and profile management. Soon, we’ll begin populating these screens with interactive and dynamic content!
Setting Up Utility Functions
To prepare for the next chapter and beyond, we will create two utility functions. These will streamline our work as we build additional features.
Creating the "utils" Folder
First, create a new folder named utils
in the root of your app. Inside this folder, add two files: cn.ts
and formatNumber.ts
.
cn.ts
In the cn.ts
file, paste the following code:
import { clsx, type ClassValue } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
This function merges Tailwind class names efficiently and correctly, ensuring that conflicting classes are handled automatically. To use it, install the necessary dependencies:
npm install clsx tailwind-merge
formatNumber.ts
In the formatNumber.ts
file, paste the following code:
export function formatNumber(num: number): string {
if (num < 1000) {
return num.toString();
} else if (num < 1_000_000) {
return `${(num / 1000).toFixed(1).replace(/\.0$/, "")}k`;
} else if (num < 1_000_000_000) {
return `${(num / 1_000_000).toFixed(1).replace(/\.0$/, "")}m`;
} else {
return `${(num / 1_000_000_000).toFixed(1).replace(/\.0$/, "")}b`;
}
}
This function formats numbers in a user-friendly way, such as converting 1500
to 1.5k
or 1,200,000
to 1.2m
. It’s perfect for displaying follower counts or other large numbers.
Wrapping Up
With the new tab navigation, profile stack layout, and utility functions in place, we now have the complete skeleton for our app. This foundation sets us up perfectly for the next chapter, where we will bring the profile to life with editing functionalities and dynamic features. Stay tuned!
Stay Updated
Don’t forget to join the Discord server where I post free boilerplate code repos for different types of social networks. Updates about the next article and additional resources will also be shared there. Lastly, follow me for updates here and on X/Twitter & BlueSky.
Author Of article : Tsabary Read full article