一般社団法人 全国個人事業主支援協会

COLUMN コラム

  • Expo Routerで実現するファイルベースルーティングのReact Nativeアプリ

Expo Routerとは何か

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を導入して感じたメリットをまとめます。

  • 開発速度の向上:ルーティング設定のボイラープレートが大幅に削減される
  • ディープリンクの自動対応:ファイル構造がそのままURLスキームになるため、ディープリンクの設定が不要
  • Web対応:同じコードベースでWebアプリとしても動作するユニバーサルリンクが実現できます
  • TypeScriptとの相性:型安全なルーティングが自動生成される

一方、注意すべき点もあります。既存のReact Navigationプロジェクトからの移行はディレクトリ構造の再設計が必要です。また、非常に複雑なナビゲーション構造の場合、ファイルベースのアプローチが逆に制約になることもあります。

とはいえ、新規プロジェクトであればExpo Routerを第一選択とすることを強くお勧めします。Web開発で培われたファイルベースルーティングの利便性を、モバイル開発でも享受できる素晴らしいツールです。

この記事をシェアする

  • Twitterでシェア
  • Facebookでシェア
  • LINEでシェア