**Framework(オープンソースソフトウェア)** Expo Router(エクスポ ルーター)向けの画面遷移とルーティング(画面移動の設定)についてカバーしています。 以下の内容が含まれます: - ファイル構成に基づくルート設定 - ルートグループと動的ルート - フォルダの整理方法 - Link コンポーネント(プレビューとコンテキストメニュー付き) - ネイティブ Stack(画面遷移の履歴管理) - ページタイトル - モーダル(ポップアップ画面)とフォームシート - NativeTabs(画面下部のタブメニュー) - ヘッダーとツールバー - ヘッダー検索バー
Framework (OSS). Navigation and routing for Expo Router. Covers file-based routes, groups and dynamic routes, folder organization, Link with previews and context menus, native Stack, page titles, modals and form sheets, NativeTabs, headers and toolbars, and header search bars.
Expo Router アプリのナビゲーション(画面遷移)とルーティング(経路設定)機能です。画面のスタイリング、色、コントロール、メディア、ビジュアルエフェクトについては expo-native-ui スキルを、モーション(動き)とジェスチャー(タッチ操作)については expo-animation を使用してください。
必要に応じてこれらのリソースを参照してください:
references/
route-structure.md ルート規約、動的ルート、グループ、フォルダ構成
tabs.md ネイティブタブ、JS タブからの移行、iOS 26 の新機能
toolbar-and-headers.md スタックヘッダーとツールバーボタン、メニュー、検索(iOS のみ)
form-sheet.md expo-router のフォームシート:設定、フッター、背景操作
search.md ヘッダー付き検索バー、useSearch フック、フィルタリングパターン
zoom-transitions.md Apple Zoom:Link.AppleZoom による流動的なズーム遷移(iOS 18+)
comment-card.tsx詳細なルート規約については ./references/route-structure.md を参照してください。
app ディレクトリに配置しますexpo-router の Color を使用してください。生の PlatformColor ではなく型安全で、ライト・ダークモードに自動対応します。完全なカラーパレットパターンについては expo-native-ui を参照してください@react-navigation/* から直接インポートしないでください。代わりに expo-router/react-navigation を使用してください(@react-navigation/native、/core、/elements、/routers をカバーしています)@react-navigation/* の代わりに Stack.SearchBar を優先してください'expo-router' の <Link href="/path" /> を使用してルート間を遷移します。
import { Link } from 'expo-router';
// 基本的なリンク
<Link href="/path" />
// カスタムコンポーネントをラップする
<Link href="/path" asChild>
<Pressable>...</Pressable>
</Link>
できるだけ <Link.Preview> を含めて iOS の慣例に従うようにしてください。ナビゲーション体験を向上させるために、コンテキストメニュー(長押しで表示するメニュー)とプレビューを頻繁に追加してください。
_layout.tsx ファイルを使用してスタックを定義します(必須)Stack.Title でページタイトルを設定します:
<Stack.Title>Home</Stack.Title>
Link コンポーネントに長押しメニューを追加します:
import { Link } from "expo-router";
<Link href="/settings" asChild>
<Link.Trigger>
<Pressable>
<Card />
</Pressable>
</Link.Trigger>
<Link.Menu>
<Link.MenuAction
title="Share"
icon="square.and.arrow.up"
onPress={handleSharePress}
/>
<Link.MenuAction
title="Block"
icon="nosign"
destructive
onPress={handleBlockPress}
/>
<Link.Menu title="More" icon="ellipsis">
<Link.MenuAction title="Copy" icon="doc.on.doc" onPress={() => {}} />
<Link.MenuAction
title="Delete"
icon="trash"
destructive
onPress={() => {}}
/>
</Link.Menu>
</Link.Menu>
</Link>;
ナビゲーション体験を向上させるために、リンクプレビューを頻繁に使用します:
<Link href="/settings">
<Link.Trigger>
<Pressable>
<Card />
</Pressable>
</Link.Trigger>
<Link.Preview />
</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>Items</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: "Items", search: "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 スキルを読み込み、上記のコマンドを再利用するのではなく eval-candidate フローに従ってください。
Navigation and routing for Expo Router apps. For screen styling, colors, controls, media, and visual effects, use the expo-native-ui skill; for motion and gestures, use expo-animation.
Consult these resources as needed:
references/
route-structure.md Route conventions, dynamic routes, groups, folder organization
tabs.md NativeTabs, migration from JS tabs, iOS 26 features
toolbar-and-headers.md Stack headers and toolbar buttons, menus, search (iOS only)
form-sheet.md Form sheets in expo-router: configuration, footers and background interaction.
search.md Search bar with headers, useSearch hook, filtering patterns
zoom-transitions.md Apple Zoom: fluid zoom transitions with Link.AppleZoom (iOS 18+)
comment-card.tsxSee ./references/route-structure.md for detailed route conventions.
app directory.Color from expo-router for native semantic colors, not raw PlatformColor (type-safe, auto-adapts to light/dark). See expo-native-ui for the full color palette pattern.@react-navigation/* directly — use expo-router/react-navigation instead (covers @react-navigation/native, /core, /elements, /routers)Stack.SearchBar to add a search bar to a screenUse <Link href="/path" /> from 'expo-router' for navigation between routes.
import { Link } from 'expo-router';
// Basic link
<Link href="/path" />
// Wrapping custom components
<Link href="/path" asChild>
<Pressable>...</Pressable>
</Link>
Whenever possible, include a <Link.Preview> to follow iOS conventions. Add context menus and previews frequently to enhance navigation.
_layout.tsx files to define stacksSet the page title with Stack.Title:
<Stack.Title>Home</Stack.Title>
Add long press context menus to Link components:
import { Link } from "expo-router";
<Link href="/settings" asChild>
<Link.Trigger>
<Pressable>
<Card />
</Pressable>
</Link.Trigger>
<Link.Menu>
<Link.MenuAction
title="Share"
icon="square.and.arrow.up"
onPress={handleSharePress}
/>
<Link.MenuAction
title="Block"
icon="nosign"
destructive
onPress={handleBlockPress}
/>
<Link.Menu title="More" icon="ellipsis">
<Link.MenuAction title="Copy" icon="doc.on.doc" onPress={() => {}} />
<Link.MenuAction
title="Delete"
icon="trash"
destructive
onPress={() => {}}
/>
</Link.Menu>
</Link.Menu>
</Link>;
Use link previews frequently to enhance navigation:
<Link href="/settings">
<Link.Trigger>
<Pressable>
<Card />
</Pressable>
</Link.Trigger>
<Link.Preview />
</Link>
Link preview can be used with context menus.
Present a screen as a modal:
<Stack.Screen name="modal" options={{ presentation: "modal" }} />
Prefer this to building a custom modal component.
Present a screen as a dynamic form sheet:
<Stack.Screen
name="sheet"
options={{
presentation: "formSheet",
sheetGrabberVisible: true,
sheetAllowedDetents: [0.5, 1.0],
contentStyle: { backgroundColor: "transparent" },
}}
/>
contentStyle: { backgroundColor: "transparent" } makes the background liquid glass on iOS 26+.A standard app layout with tabs and stacks inside each tab:
app/
_layout.tsx — <NativeTabs />
(index,search)/
_layout.tsx — <Stack />
index.tsx — Main list
search.tsx — Search view
// 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>Items</NativeTabs.Trigger.Label>
</NativeTabs.Trigger>
<NativeTabs.Trigger name="(search)" role="search" />
</NativeTabs>
</ThemeProvider>
);
}
Create a shared group route so both tabs can push common screens:
// 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: "Items", search: "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 is the SDK 56+ option name; older SDKs use headerLargeTitle, which is deprecated upstream.
If you encounter errors, misleading or outdated information in this skill, report it so Expo can improve:
npx --yes submit-expo-feedback@latest --category skills --subject "expo-router" "<actionable feedback>"
Only submit when you have something specific and actionable to report. Include as much relevant context as possible. If an AI agent repeatedly failed or the user had to take over an Expo task, load the expo-skill-feedback skill and follow its eval-candidate flow instead of reusing the command above.
原文・著作権は Anthropic および各プラグイン作者に帰属します。日本語訳は Claude API による自動翻訳です。