Why I Switched to Expo Router: Best Practices and Implementation Guide

After years of using React Navigation for my side projects, I decided to give Expo Router another try. My first experience with expo…

Why I Switched to Expo Router: Best Practices and Implementation Guide
Expo Router Logo

After years of using React Navigation for my side projects, I decided to give Expo Router another try. My first experience with expo router(version 1) was less than ideal. At the time, I couldn’t understand the hype around it. I even thought, “Who wants file-based routing for a mobile app?”

Turns out, I do!

In this article, I’ll walk you through how to implement Expo Router, share best practices I’ve discovered, and show you why it’s worth adopting.


Setting Up Expo Router

Step 1: Install Dependencies

Run the following command to install Expo Router and related packages:

npx expo install expo-router react-native-safe-area-context react-native-screens expo-linking expo-constants expo-status-bar

Step 2: Configure Your Entry Point

Add the following to your package.json to define the entry point for Expo Router:

{   
  "main": "expo-router/entry"   
}

Step 3: Add Deep Linking

In your app configuration (e.g., app.json or app.config.js), specify a custom deep linking scheme:

{   
  "scheme": "your-app-scheme"   
}

Step 4: Update Babel Configuration

Modify your babel.config.js file:

module.exports = function (api) {   
  api.cache(true);   
  return {   
    presets: ['babel-preset-expo'],   
  };   
};

With the configuration complete, let’s dive into an example app.


Building a Tab-Based Navigation

File Structure

Here’s the file structure for a simple app with Two tabs:

app   
  _layout.tsx   
  (tabs)   
    _layout.tsx   
    index.tsx   
    settings.tsx

Implementing the Layouts

Root Layout (app/_layout.tsx)

import { Stack } from 'expo-router/stack';   
 
export default function Layout() {   
  return (   
    <Stack>   
      <Stack.Screen name="(tabs)" options={{ headerShown: false }} />   
    </Stack>   
  );   
}

Tab Layout (app/(tabs)/_layout.tsx)

import FontAwesome from '@expo/vector-icons/FontAwesome';   
import { Tabs } from 'expo-router';   
 
export default function TabLayout() {   
  return (   
    <Tabs screenOptions={{ tabBarActiveTintColor: 'blue' }}>   
      <Tabs.Screen   
        name="index"   
        options={{   
          title: 'Home',   
          tabBarIcon: ({ color }) => <FontAwesome size={28} name="home" color={color} />,   
        }}   
      />   
      <Tabs.Screen   
        name="settings"   
        options={{   
          title: 'Settings',   
          tabBarIcon: ({ color }) => <FontAwesome size={28} name="cog" color={color} />,   
        }}   
      />   
    </Tabs>   
  );   
}

Example Tab Screens

For app/(tabs)/index.tsx and app/(tabs)/settings.tsx:

import { View, Text, StyleSheet } from 'react-native';   
 
export default function Tab() {   
  return (   
    <View style={styles.container}>   
      <Text>Tab [Home|Settings]</Text>   
    </View>   
  );   
}   
 
const styles = StyleSheet.create({   
  container: {   
    flex: 1,   
    justifyContent: 'center',   
    alignItems: 'center',   
  },   
});

Structuring Your Project

To avoid deeply nested navigators, follow these best practices inspired by React Navigation’s documentation:

  1. Reduce Nesting: Deep nesting can cause performance issues on low-end devices and make your code harder to manage.
  2. Meaningful Routes: Use file-based routing to create logical and predictable paths. For example, instead of nesting stacks within stacks, create folder-based routes like settings/account.

Example File Structure

app   
  _layout.tsx   
  settings/   
    account.tsx   
  home/   
    notifications.tsx   
  (tabs)/   
    _layout.tsx   
    index.tsx   
    settings.tsx

Root Layout (app/_layout.tsx)

import { Stack } from 'expo-router/stack';   
 
export default function Layout() {   
  return (   
    <Stack>   
      <Stack.Screen name="(tabs)" options={{ headerShown: false }} />   
      <Stack.Screen name="settings/account" options={{ headerShown: false }} /> 
      <Stack.Screen name="home/notifications" options={{ headerShown: false }} /> 
    </Stack>   
  );   
}

This structure ensures clean, meaningful routes like /settings/account instead of overly nested setups.


Passing Parameters

To pass parameters to screens, use bracket notation in your filenames.

Example 1: Passing a Single Parameter

File: app/[id].tsx

import { useLocalSearchParams } from 'expo-router';   
import { View, Text } from 'react-native';   
 
export default function Route() {   
  const { id } = useLocalSearchParams<{ id: string }>();   
 
  return (   
    <View>   
      <Text>ID: {id}</Text>   
    </View>   
  );   
}

Example 2: Parsing Arrays

Navigate to screen with array param:

router.push({ 
    pathname: "/settings/account", 
    params: { 
        ids: JSON.stringify([1, 2, 3]), 
    }, 
})
import { useLocalSearchParams } from 'expo-router';   
import { View, Text } from 'react-native';   
 
export default function Route() {   
  const { ids } = useLocalSearchParams<{ ids: string }>();   
  const parsedIds = JSON.parse(ids);   
 
  return (   
    <View>   
      <Text>IDs: {parsedIds.join(', ')}</Text>   
    </View>   
  );   
}

Why Expo Router?

Expo Router simplifies navigation by combining React Native’s flexibility with the familiarity of file-based routing. Its structure makes your project easier to maintain, navigate, and scale.

By following the practices outlined above, you’ll enjoy a more organized codebase and a smoother development experience.

Try BanKan Board — The Project Management App Made for Developers, by Developers

If you’re tired of complicated project management tools with story points, sprints, and endless processes, BanKan Board is here to simplify your workflow. Built with developers in mind, BanKan Board lets you manage your projects without the clutter.

Key Features:

  • No complicated processes: Focus on what matters without the overhead of traditional project management systems.
  • Claude AI Assistant: Get smart assistance to streamline your tasks and improve productivity.
  • Free to Use: Start using it without any upfront cost.
  • Premium Features: Upgrade to unlock advanced functionality tailored to your team’s needs.

Whether you’re building a side project, managing a team, or collaborating on open-source software, BanKan Board is designed to make your life easier. Try it today!

Get Started with BanKan Board — It’s Free!