Expo Router 導航
Expo Router 應用程式的導航與路由。如需畫面樣式、顏色、控制項、媒體與視覺效果,請使用 expo-native-ui 技能;如需動畫與手勢,請使用 expo-animation。
參考資料
請視需要參考這些資源:
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+)
程式碼風格
- 檔案名稱一律使用 kebab-case,例如
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>
只要可能,請加入 <Link.Preview> 以遵循 iOS 慣例。頻繁加入上下文選單與預覽以增強導航體驗。
Stack
- 一律使用
_layout.tsx檔案來定義 Stack。 - 使用來自 '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 預覽可與上下文選單搭配使用。
Modal
以 Modal 方式呈現畫面:
<Stack.Screen name="modal" options={{ presentation: "modal" }} />
優先採用此方法而非自行建立自訂 Modal 元件。
Sheet
以動態 Form Sheet 方式呈現畫面:
<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 技能並遵循其 eval-candidate 流程,而非重複使用上述指令。