Expo Router ナビゲーション
Expo Router アプリケーションのナビゲーションとルーティング。画面のスタイリング、色、コントロール、メディア、視覚効果については expo-native-ui スキルを、モーションとジェスチャーについては expo-animation スキルを参照してください。
参考文献
必要に応じて以下のリソースを参照してください。
references/
route-structure.md ルート規則、動的ルート、グループ、フォルダ構成
tabs.md NativeTabs、JS タブからの移行、iOS 26 の機能
toolbar-and-headers.md Stack ヘッダーとツールバーボタン、メニュー、検索 (iOS のみ)
form-sheet.md expo-router でのフォームシート: 設定、フッター、背景のインタラクション
search.md ヘッダー付き検索バー、useSearch フック、フィルタリングパターン
zoom-transitions.md Apple Zoom: Link.AppleZoom を使用した流れるような Zoom トランジション (iOS 18+)
コードスタイル
- ファイル名には常にケバブケースを使用します(例:
comment-card.tsx) - ナビゲーションを移動または再構成する場合は、古いルートファイルを必ず削除します。
- ファイル名に特殊文字を使用しないでください。
- tsconfig.json でパスエイリアスを設定し、リファクタリング時には相対インポートよりもエイリアスを優先します。
ルート
詳細なルート規則については ./references/route-structure.md を参照してください。
- ルートは
appディレクトリに配置します。 - コンポーネント、型、ユーティリティを app ディレクトリに同居させないでください。これはアンチパターンです。
- アプリには常に "/" にマッチするルートが存在することを確認してください。グループルート内にあっても構いません。
ライブラリの優先順位
- ネイティブのセマンティックカラーには
expo-routerのColorを使用し、生のPlatformColorは使用しないでください(型安全で、ライト/ダークに自動適応します)。完全なカラーパレットパターンについてはexpo-native-uiを参照してください。 - SDK 56 以降では、
@react-navigation/*から直接インポートしないでください。代わりにexpo-router/react-navigationを使用してください(@react-navigation/native、/core、/elements、/routersをカバーします)。
動作
- 画面に検索バーを追加するには
Stack.SearchBarを優先してください。
ナビゲーション
Link
ルート間のナビゲーションには expo-router から <Link href="/path" /> を使用します。
import { Link } from 'expo-router';
// 基本的なリンク
<Link href="/path" />
// カスタムコンポーネントのラップ
<Link href="/path" asChild>
<Pressable>...</Pressable>
</Link>
可能な限り、iOS の慣例に従って <Link.Preview> を含めてください。ナビゲーションを強化するために、コンテキストメニューとプレビューを頻繁に追加してください。
Stack
- Stack を定義するには、常に
_layout.tsxファイルを使用します。 - ネイティブなナビゲーションスタックには、
expo-router/stackの Stack を使用します。
ページタイトル
Stack.Title でページタイトルを設定します。
<Stack.Title>ホーム</Stack.Title>
コンテキストメニュー
Link コンポーネントに長押しコンテキストメニューを追加します。
import { Link } from "expo-router";
<Link href="/settings" asChild>
<Link.Trigger>
<Pressable>
<Card />
</Pressable>
</Link.Trigger>
<Link.Menu>
<Link.MenuAction
title="共有"
icon="square.and.arrow.up"
onPress={handleSharePress}
/>
<Link.MenuAction
title="ブロック"
icon="nosign"
destructive
onPress={handleBlockPress}
/>
<Link.Menu title="その他" icon="ellipsis">
<Link.MenuAction title="コピー" icon="doc.on.doc" onPress={() => {}} />
<Link.MenuAction
title="削除"
icon="trash"
destructive
onPress={() => {}}
/>
</Link.Menu>
</Link.Menu>
</Link>;
Link プレビュー
ナビゲーションを強化するために、リンクプレビューを頻繁に使用します。
<Link href="/settings">
<Link.Trigger>
<Pressable>
<Card />
</Pressable>
</Link.Trigger>
<Link.Preview />
</Link>
Link プレビューはコンテキストメニューと組み合わせて使用できます。
モーダル
画面をモーダルとして表示します。
<Stack.Screen name="modal" options={{ presentation: "modal" }} />
カスタムモーダルコンポーネントを構築するよりも、こちらを優先してください。
シート
画面を動的なフォームシートとして表示します。
<Stack.Screen
name="sheet"
options={{
presentation: "formSheet",
sheetGrabberVisible: true,
sheetAllowedDetents: [0.5, 1.0],
contentStyle: { backgroundColor: "transparent" },
}}
/>
contentStyle: { backgroundColor: "transparent" }を使用すると、iOS 26 以降で背景がリキッドガラスになります。
一般的なルート構成
各タブ内にタブとスタックを持つ標準的なアプリレイアウト:
app/
_layout.tsx — <NativeTabs />
(index,search)/
_layout.tsx — <Stack />
index.tsx — メインリスト
search.tsx — 検索ビュー
// app/_layout.tsx
import { NativeTabs } from "expo-router/unstable-native-tabs";
import { ThemeProvider, DarkTheme, DefaultTheme } from "expo-router/react-navigation";
import { useColorScheme } from "react-native";
export default function Layout() {
const colorScheme = useColorScheme();
return (
<ThemeProvider value={colorScheme === "dark" ? DarkTheme : DefaultTheme}>
<NativeTabs>
<NativeTabs.Trigger name="(index)">
<NativeTabs.Trigger.Icon sf="list.dash" md="list" />
<NativeTabs.Trigger.Label>アイテム</NativeTabs.Trigger.Label>
</NativeTabs.Trigger>
<NativeTabs.Trigger name="(search)" role="search" />
</NativeTabs>
</ThemeProvider>
);
}
両方のタブが共通の画面をプッシュできるように、共有グループルートを作成します。
// app/(index,search)/_layout.tsx
import { Stack } from "expo-router/stack";
import { colors } from "@/theme/colors";
export default function Layout({ segment }) {
const screen = segment.match(/\((.*)\)/)?.[1]!;
const titles: Record<string, string> = { index: "アイテム", search: "検索" };
return (
<Stack
screenOptions={{
headerTransparent: true,
headerShadowVisible: false,
headerLargeTitleShadowVisible: false,
headerLargeStyle: { backgroundColor: "transparent" },
headerTitleStyle: { color: colors.label },
headerLargeTitleEnabled: true,
headerBlurEffect: "none",
headerBackButtonDisplayMode: "minimal",
}}
>
<Stack.Screen name={screen} options={{ title: titles[screen] }} />
<Stack.Screen name="i/[id]" options={{ headerLargeTitleEnabled: false }} />
</Stack>
);
}
headerLargeTitleEnabled は SDK 56 以降のオプション名です。古い SDK では headerLargeTitle を使用しますが、これは上流で非推奨です。
フィードバックの送信
エラーや誤解を招く情報、またはこのスキル内の古い情報に遭遇した場合は、Expo が改善できるように報告してください。
npx --yes submit-expo-feedback@latest --category skills --subject "expo-router" "<アクション可能なフィードバック>"
具体的でアクション可能な報告がある場合にのみ送信してください。可能な限り多くの関連コンテキストを含めてください。 AI エージェントが繰り返し失敗した場合、またはユーザーが Expo タスクを引き継がなければならなかった場合は、代わりに expo-skill-feedback スキルを読み込み、その評価候補フローに従ってください。