Expo Routerは、React Nativeアプリケーションにファイルベースルーティングを導入するライブラリです。Next.jsやNuxt.jsを使ったことがある方なら、ディレクトリ構造がそのままルーティング定義になるあの開発体験を思い浮かべてください。それをモバイルアプリで実現するのがExpo Routerです。
従来のReact Nativeでは、React Navigationを使ってルーティングを手動で定義する必要がありました。Stack NavigatorやTab Navigatorを組み合わせ、画面遷移のロジックをコードで記述するスタイルです。Expo Routerはこのアプローチを根本から変え、ファイルを配置するだけでルーティングが自動的に構成される仕組みを提供します。
Expo Routerを使うプロジェクトの作成は非常にシンプルです。
npx create-expo-app@latest my-app --template tabs
cd my-app
npx expo start
テンプレートを指定することで、タブナビゲーション付きのプロジェクトが即座に生成されます。生成されたプロジェクトの構造を見てみましょう。
app/
_layout.tsx // ルートレイアウト
index.tsx // ホーム画面 (/)
(tabs)/
_layout.tsx // タブレイアウト
index.tsx // 最初のタブ
explore.tsx // 探索タブ
このディレクトリ構造が、そのままアプリのナビゲーション構造になります。
appディレクトリ内にファイルを配置するだけで、自動的にルートが生成されます。
// app/profile.tsx
import { View, Text } from 'react-native';
export default function ProfileScreen() {
return (
<View>
<Text>プロフィール画面</Text>
</View>
);
}
このファイルを作成するだけで、/profile というルートが利用可能になります。設定ファイルへの登録は一切不要です。
動的なパラメータを含むルートは、ブラケット記法で表現します。
// app/user/[id].tsx
import { useLocalSearchParams } from 'expo-router';
import { View, Text } from 'react-native';
export default function UserScreen() {
const { id } = useLocalSearchParams();
return (
<View>
<Text>ユーザーID: {id}</Text>
</View>
);
}
/user/123 のようなURLでアクセスすると、id パラメータに 123 が渡されます。
Expo Routerの強力な機能の一つが、レイアウトシステムです。_layout.tsx ファイルを使って、ナビゲーション構造を宣言的に定義できます。
// app/_layout.tsx
import { Stack } from 'expo-router';
export default function RootLayout() {
return (
<Stack>
<Stack.Screen name="index" options={{ title: 'ホーム' }} />
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
<Stack.Screen name="modal" options={{ presentation: 'modal' }} />
</Stack>
);
}
グループ化にはカッコ記法を使います。(tabs)のようにカッコで囲んだディレクトリ名はURLには影響せず、純粋にレイアウトのグルーピングとして機能します。
Expo Routerでは、Web標準に近い形で画面遷移を実装できます。
import { Link, useRouter } from 'expo-router';
import { Pressable, Text } from 'react-native';
export default function HomeScreen() {
const router = useRouter();
return (
<>
{/* 宣言的な遷移 */}
<Link href="/profile">プロフィールへ</Link>
{/* 命令的な遷移 */}
<Pressable onPress={() => router.push('/user/42')}>
<Text>ユーザー詳細へ</Text>
</Pressable>
{/* パラメータ付き遷移 */}
<Link href={{ pathname: '/search', params: { q: 'expo' } }}>
検索
</Link>
</>
);
}
Linkコンポーネントによる宣言的な遷移と、useRouterフックによる命令的な遷移の両方をサポートしています。
実際のアプリ開発で頻出する認証フローの実装パターンを紹介します。ルートグループを活用して、認証状態に応じた画面表示を制御します。
// app/_layout.tsx
import { Redirect, Stack } from 'expo-router';
import { useAuth } from '../hooks/useAuth';
export default function RootLayout() {
const { isLoggedIn } = useAuth();
return (
<Stack>
<Stack.Screen name="(auth)" options={{ headerShown: false }} />
<Stack.Screen name="(app)" options={{ headerShown: false }} />
</Stack>
);
}
// app/(app)/_layout.tsx
import { Redirect, Stack } from 'expo-router';
import { useAuth } from '../../hooks/useAuth';
export default function AppLayout() {
const { isLoggedIn } = useAuth();
if (!isLoggedIn) return <Redirect href="/(auth)/login" />;
return <Stack />;
}
この設計により、未認証ユーザーが保護されたルートにアクセスしようとすると、自動的にログイン画面へリダイレクトされます。
実際にExpo Routerを導入して感じたメリットをまとめます。
一方、注意すべき点もあります。既存のReact Navigationプロジェクトからの移行はディレクトリ構造の再設計が必要です。また、非常に複雑なナビゲーション構造の場合、ファイルベースのアプローチが逆に制約になることもあります。
とはいえ、新規プロジェクトであればExpo Routerを第一選択とすることを強くお勧めします。Web開発で培われたファイルベースルーティングの利便性を、モバイル開発でも享受できる素晴らしいツールです。