Back to Blog

Customizing Expo Router Tabs: Styling, Icons, and Hiding Screens

Royan Gagas
October 24, 2025
Share
product
mobile application
development
Customizing Expo Router Tabs: Styling, Icons, and Hiding Screens

When moving a web application to React Native, one of the first challenges is recreating a familiar, polished navigation experience. On the web, a bottom navigation bar is common, but how do you build a fully custom, stylish, and functional tab bar for a mobile app?

As I've been transforming my web app into a mobile app, I dived deep into Expo Router and its powerful file-based navigation system. In this guide, I'll walk you through the exact process I used to customize the default tab bar into a "floating" navigation bar with custom icons and specific active states, just like in my video.

We'll cover how to style the tab bar globally, set custom icons for each screen, and even hide specific screens from the tab bar.

The Foundation: Understanding Expo Router's Layouts

First, it's crucial to understand how Expo Router's file-based routing works. It's very similar to Next.js. Your file structure is your navigation.

My app has a protected route setup:

1.app/_layout.tsx: This is the root layout. It manages the main authentication state.
2.app/(app)/_layout.tsx: This is a Stack Navigator layout for all my authenticated screens. I use a Stack here to create a single, persistent custom header that appears on all screens inside this group.
3.app/(app)/(tabs)/_layout.tsx: This is the most important file for this guide. It's a nested layout file that creates the Tab Navigator. Any file inside the tab (tabs) directory (like index.tsx, history.tsx, etc.) will automatically become a tab.

Step 1: Global Tab Bar Styling with screenOptions

To change the look of the entire tab bar, you'll work inside app/(app)/(tabs)/_layout.tsx and add the screenOptions prop to the <Tabs> component.

Here's how I achieved the floating, icon-only look:

headerShown: false : Since I already have a custom header from my (app) Stack layout, I must hide the default header for the Tabs layout. This prevents a "double header" from appearing.
tabBarShowLabel: false : This hides the default text labels under the icons, giving us a cleaner, icon-only navigation bar.
tabBarStyle: This prop lets you pass in a style object, just like in standard React Native. This is where I created the floating effect.

Here's a conceptual look ath the tabBarStyle props:

Step 2: Customizing Individual Tab Screens (Icons & Titles)

Now that the bar is styled, we need to customize each button. You do this by adding the options prop to each <Tabs.Screen> component within your Tabs layout.

The most important option is tabBarIcon. This prop accepts a function that gives you properties like focused, color, and size.

Here's how you can render a custom icon and even change its style when it's focused (active):

By using the focused prop, you get full control over the activate state of your tab icons, allowing for unique designs like a larger button for an "add" screen, as shown in the video.

Step 3: How to Hide a Screen from the Tab Bar

What if you have a screen inside your (tabs) directory that you need to navigate to, but you don't want it to appear in the tab bar? (For example, an "Edit Habit" screen).

Expo Router makes this simple. On the <Tabs.Screen> component for that file, you need to set the href option to null.

Now, the edit-habit screen exists and can be navigated to (e.g., router.push('/edit-habit')), but it is completely hidden from the bottom tab bar.

Conclusion

Expo Router's file-based system is incredibly powerful once you understand its layout components. By using the screenOptions prop on <Tabs> for global styling and the options prop on <Tabs.Screen> for individual icon rendering and visibility, you can transform the default navigation into a fully custom, professional-looking tab bar that perfectly matches your app's design.

youtube_embed_preview