Navigation
File-based routing with Expo Router — tab navigation, stack navigation, and auth-protected routes.
Navigation
ScaleRocket Mobile uses Expo Router for file-based routing, similar to Next.js. Files in the app/ directory automatically become routes.
Expo Router Conventions
_layout.tsx— Defines the navigator for a directory (Stack, Tabs, Drawer)(group)/— Route groups (parentheses). Groups routes without adding a URL segmentindex.tsx— Default route for a directory- Files = routes —
login.tsxbecomes the/loginroute within its group
Navigation Structure
Root Stack (_layout.tsx)
├── (auth) Stack
│ ├── login
│ ├── register
│ └── forgot-password
└── (tabs) Tab Navigator
├── index (Home)
├── profile (Profile)
└── settings (Settings)The root layout renders a Stack with headerShown: false. Each group defines its own navigator type.
Route Groups
| Group | Navigator | Purpose |
|---|---|---|
(auth) | Stack | Login, register, forgot-password. No header. |
(tabs) | Tabs | Home, Profile, Settings. Tab bar at bottom. |
Groups with parentheses do not add path segments. Both (auth) and (tabs) are at the root level.
Tab Navigation
The tab navigator is defined in app/(tabs)/_layout.tsx:
import { Tabs } from "expo-router";
import { Ionicons } from "@expo/vector-icons";
export default function TabLayout() {
return (
<Tabs screenOptions={{ tabBarActiveTintColor: "#2563EB" }}>
<Tabs.Screen
name="index"
options={{
title: "Home",
tabBarIcon: ({ color }) => (
<Ionicons name="home" size={24} color={color} />
),
}}
/>
<Tabs.Screen
name="profile"
options={{
title: "Profile",
tabBarIcon: ({ color }) => (
<Ionicons name="person" size={24} color={color} />
),
}}
/>
<Tabs.Screen
name="settings"
options={{
title: "Settings",
tabBarIcon: ({ color }) => (
<Ionicons name="settings" size={24} color={color} />
),
}}
/>
</Tabs>
);
}Stack Navigation
The auth group uses a stack navigator in app/(auth)/_layout.tsx:
import { Stack } from "expo-router";
export default function AuthLayout() {
return (
<Stack screenOptions={{ headerShown: false }} />
);
}Navigate between screens using the router or Link:
import { router } from "expo-router";
// Programmatic navigation
router.push("/(auth)/register");
// Or use the Link component
import { Link } from "expo-router";
<Link href="/(auth)/register">Create account</Link>Auth-Protected Routes
The auth guard in app/_layout.tsx uses useSession() to check the user's authentication state:
import { useSession } from "../lib/auth";
import { Redirect } from "expo-router";
export default function RootLayout() {
const { session, loading } = useSession();
if (loading) return <ActivityIndicator />;
if (!session) return <Redirect href="/(auth)/login" />;
return <Stack screenOptions={{ headerShown: false }} />;
}The tabs layout also has a secondary guard: if the session is lost while on a tab screen, it redirects to login.
Adding New Screens
To add a new screen:
- Create a file in the appropriate group (e.g.,
app/(tabs)/analytics.tsx) - Export a default React component
- The route is available immediately — no router configuration needed
- Add it to the tab navigator in
_layout.tsxif it should appear as a tab
// app/(tabs)/analytics.tsx
export default function AnalyticsScreen() {
return (
<View>
<Text>Analytics</Text>
</View>
);
}