How to Build a Social Network in 1 Day: Part 4 — Tabs Navigation

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 ( ScreenName ); }; 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. The accountId represents the specific user ID. post/[postId].tsx: This screen is used when navigating to a specific post. The postId 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 ( ( ), }} /> ( ), }} /> ( ), }} /> ( ), }} /> ( ), }} /> ); } 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 ( ); } 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

Jan 19, 2025 - 21:53
How to Build a Social Network in 1 Day: Part 4 — Tabs Navigation

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>ScreenNameText>
    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. The accountId represents the specific user ID.
  • post/[postId].tsx: This screen is used when navigating to a specific post. The postId 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.