Bottom tab navigation in Expo Router with authentication

Fredrik Burmester
4 min readJun 12, 2023

--

⚠️ Check out the new and improved Expo router v3 version: https://medium.com/@fredrik.burmester/bottom-tab-navigation-in-expo-router-3-sdk-50-with-authentication-b8d4529b2e0f

Introduction

In this tutorial, I will guide you through the process of setting up a bottom tab router using Expo Router, incorporating authentication routes and stack navigation. By the end, you’ll understand how to structure your files, create a tab router, and integrate authentication and stack routes. Check out the bottom of the article for a link to my git repo with a fully working example.

Demo

Step 1: File Structure

To get started, let’s organize our project’s file structure. Within the “app” folder, we will create two main groups: “auth” and “tabs”. Additionally, we’ll create two layout files and include all the desired tabs within the “tabs” group.

Inside the auth group, we create a single login file.

The other file routes can be whatever you want and will act as stacks, stacking on top of our tab navigation. If you instead want to navigate within the tabs navigation, you can nest navigations within the “tabs” folder.

- app/
- (auth)/
- login.tsx
- (tabs)/
- _layout.tsx
- home.tsx
- account.tsx
- other.tsx
- other/
- _layout.tsx

Step 2: Layouts

Expo provides various layout options that you can use to create different navigation patterns and user interfaces. Some of the layout options are tabs, stacks, drawers etc.

Below I’ve created a tab router. The “initialRouteName” prop is set to “home”, indicating that the “home” screen is the initial screen displayed when the tab navigation is rendered. The “Tabs” component has multiple “Tabs.Screen” components. Each “Tabs.Screen” represents a tab screen. It defines the screen’s name and various options. This should correlate with the files in the file structure above.

// app/(tabs)/_layout.tsx

export default function TabsLayout() {
return (
<Tabs
initialRouteName="home"
screenOptions={{
...
}}
tabBar={(props) =>
Platform.OS === "ios" ? (
<BlurView
style={{ position: "absolute", bottom: 0, left: 0, right: 0 }}
tint={colorScheme == "dark" ? "dark" : "light"}
intensity={95}
>
<BottomTabBar {...props} />
</BlurView>
) : (
<BottomTabBar {...props} />
)
}
>
<Tabs.Screen
name="home"
options={{
href: "/home",
tabBarIcon: ({ color }) => (
...
),
}}
/>
<Tabs.Screen
name="account"
options={{
href: {
pathname: "/account",
},
tabBarIcon: ({ color }) => (
...
),
}}
/>
</Tabs>
);
}

Next is the main layout:

Defining the stack is not strictly necessary, but allows for more flexibility, allows for nice animations and allows us to differentiate between our tabs and other stack screens. Feel free to try replacing the <Stack /> (and everything in it) with <Slot /> and see what happens.

// app/_layout.tsx

import { AuthProvider } from "../context/AuthProvider";

export default function RootLayout() {
return (
<Stack
screenOptions={{
...
}}
>
<Stack.Screen
name="(tabs)"
options={{
...
}}
/>
{/* <Stack.Screen
name="other"
options={{
...
}}
/> */}
</Stack>
);
}

Inside each tab, make sure to export the function as such:

// app/(tabs)/account.tsx

export default function Account() {
return ...
}

Step 3: Stack screens

All screens defined outside of the tab group will act as stack screens and stack on top of the tab navigation with a nice animation (if you define the stack layout above), allowing you to go back by pressing the back button in the upper left-hand corner.

Step 4: Authentication

I decided to include this part since many want to integrate authentication into their app and keep the rest of the app secure. Luckily it’s really simple!

Go back to the main _layout.tsx file and add your auth provider like so:

// app/_layout.tsx

export default function RootLayout() {
return (
<AuthProvider>
<Stack>
...
</Stack>
</AuthProvider>
);
}

The only extra code we need to add is within our AuthProvider. This is inspired by the auth tutorial in the Expo Router docs.

export function AuthProvider({ children }: { children: JSX.Element }): JSX.Element {

...
useProtectedRoute(user);
...

return <AuthContext.Provider value={authContext}>{children}</AuthContext.Provider>;
}

function useProtectedRoute(user: any) {
const segments = useSegments();
const router = useRouter();

useEffect(() => {
const inAuthGroup = segments[0] === "(auth)";

if (
// If the user is not signed in and the initial segment is not anything in the auth group.
!user &&
!inAuthGroup
) {
// Redirect to the sign-in page.
router.replace("/login");
} else if (user && inAuthGroup) {
// Redirect away from the sign-in page.
router.replace("/home");
}
}, [user, segments]);
}

Conclusion

Here’s a link to my repo with a working example!

Thank you for following along with this tutorial. I hope you found it helpful in implementing bottom tab routers with auth and stacks in your Expo project. Happy coding!

--

--