Expo Router v55:原生导航更强,Web 能力也更进一步
Expo Router v55 升级了导航功能,带来新的 Stack API、原生标签页、动态颜色和工具栏,以及实验性的 SSR 和数据加载器。
中文
复制

Expo Router v55 来了(没错,我们一口气跨了 49 个版本)——这是朝着 Expo Router 发布以来一直追求的目标迈出的重要一步:让应用更贴近原生体验,同时支持更复杂的 Web 场景,让你真正做到只写一次代码。
这个版本同时做了两件大事:
让原生 UI 成为路由的一等公民。 全新的 Stack API、工具栏、原生标签页、动态平台配色、缩放转场和分屏支持,让你用构建应用其余部分的方式来构建导航 UI:用 React 组件,直接组合在屏幕所在的位置。
拓展了「只写一次」在 Web 上的含义。 Expo Router 现在有了更清晰的运行时方案——expo-server、可配置的服务器响应头,以及实验性的服务端渲染和数据加载器,让你无需另建一套 Web 技术栈,就能交付更快的页面、更好的 SEO 和更动态的内容。
注意,其中部分 API 仍处于实验阶段,可能会为了最佳体验而调整。截至 2026/02/xx,本文展示的 API 均为最新,但更新的示例请始终以 Expo Router 文档为准。
下面带你过一遍新内容、它们为什么重要,以及今天可以从哪里开始上手。
新的 Stack API
新的 Stack API 让你以声明式、组件级的方式控制原生头部。我们用可组合的 React 组件取代了 options API,你可以直接在屏幕里使用它们。设置头部样式、设置标题、添加搜索栏——全都在你的组件树里完成。
import { Stack } from "expo-router"
export default function IndexPage() {
...
return (
<>
{/* Screen content */}
<Stack.Header style={{ backgroundColor: 'transparent' }} />
<Stack.Screen.Title>Home</Stack.Screen.Title>
<Stack.SearchBar onChangeText={handleSearchChange} />
</>
);
}
原生标签页
我们加入了对 Native Tabs 组件的支持。我们正在积极推进全平台完整支持,目标是在下个版本将该 API 标记为稳定。

在 Android 上,Native Tabs 默认使用 Material Design 3 动态配色,自动适应用户偏好的颜色。你可以通过在 NativeTabs.Trigger 上指定 md 属性来使用 Material Symbols——类似 iOS 上的 SF Symbols。SafeAreaView 现在已默认集成,内容不会再被标签栏遮挡。
<NativeTabs>
<NativeTabs.Trigger name="(library)">
<NativeTabs.Trigger.Icon
sf="photo.fill.on.rectangle.fill"
md="photo_library"
/>
<NativeTabs.Trigger.Label>Library</NativeTabs.Trigger.Label>
</NativeTabs.Trigger>
...
</NativeTabs>
在 iOS 上,我们新增了对底部附属视图的支持——一个悬浮在标签栏上方的浮动视图,从 iOS 26 开始可用。在 Expo Router 中,只需在 NativeTabs 布局里加入 NativeTabs.BottomAccessory 组件即可使用。
import { NativeTabs } from 'expo-router/unstable-native-tabs';
...
export default function Layout() {
...
return (
<NativeTabs>
...
<NativeTabs.BottomAccessory>
<MiniPlayer isPlaying={isPlaying} setIsPlaying={setIsPlaying} />
</NativeTabs.BottomAccessory>
</NativeTabs>
);
}
在开发这个版本的过程中,我们引入了一些破坏性 API 变更。简短的迁移指南见这里。
原生标签页的全部功能请查阅文档。
动态颜色

新的 Color API 让你可以直接访问平台颜色:Color.ios.* 对应 iOS 系统颜色,Color.android.* 对应 Android 系统颜色,Color.android.dynamic.* 对应会随用户壁纸变化的 Material Dynamic 颜色。
import { Color } from "expo-router"
export default function Page() {
// This needs to be declared within React Component
const backgroundColor = Platform.select({
android: Color.android.dynamic.surface,
ios: Color.ios.systemBackground,
default: '#fff'
});
...
return (
<ScrollView style={{ backgroundColor }}>
...
</ScrollView>
);
}
关于 Color API 的更多内容见文档。
工具栏
新的 Toolbar API 把原生 iOS 工具栏带到了 Expo Router,内置动画,并完整支持 liquid glass 效果。位置有三种可选:标题栏左侧、标题栏右侧,或固定在屏幕底部。
import { Stack } from "expo-router";
export default function FoldersScreen() {
...
return (
<>
{/* Screen content */}
<Stack.Header style={{ backgroundColor: 'transparent' }} />
<Stack.Screen.Title large>Folders</Stack.Screen.Title>
<Stack.SearchBar onChangeText={handleSearchChange} />
<Stack.Toolbar placement="right">
<Stack.Toolbar.Button icon="folder.badge.plus" onPress={addFolder} />
<Stack.Toolbar.Button onPress={activateEditMode}>Edit</Stack.Toolbar.Button>
</Stack.Toolbar>
<Stack.Toolbar placement="bottom">
<Stack.Toolbar.SearchBarSlot />
<Stack.Toolbar.Spacer />
<Stack.Toolbar.Button icon="square.and.pencil" onPress={addNew} />
</Stack.Toolbar>
</>
);
}
关于工具栏的更多内容见文档。
缩放转场
新的 Apple zoom API 为 Apple 平台上的任意 <Link /> 带来手势驱动、可中断的原生共享元素转场,其他平台则使用默认行为。我们拆解了底层的 UIKit API,并以 React 优先的方式将其重新设计为分层效果。
从 Link 到路由的完整缩放。
将 Link 内的单个视图缩放到目标路由。
将单个视图缩放到下一个路由中的共享视图。
这让你拥有完全的控制权,同时还有开箱即用的优雅降级。没有新依赖,没有新导入,仍然是你已经在用的那个强大的 Link。
要开始使用,在 Link.Trigger 上设置 withAppleZoom
<Link href="..." asChild>
<Link.Trigger withAppleZoom>
<Pressable style={{ width: '50%', aspectRatio: 1 }}>
<Image source={...} />
</Pressable>
</Link.Trigger>
</Link>
关于缩放转场的更多内容见文档。
expo-server
@expo/server 包已更名为 expo-server,现在作为 Expo SDK 的一部分发布。它为 Expo Router 项目提供运行时服务端 API,并内置了面向 EAS Hosting 以及其他平台的部署适配器。
// app/api/user+api.ts
import { deferTask, setResponseHeaders } from 'expo-server';
import telemetry from '@/telemetry';
export function GET() {
deferTask(async () => {
telemetry.track();
});
setResponseHeaders({ 'Cache-Control': 'max-age=60' });
return Response.json({ user: 'anonymous' });
}
只要是在 Expo Router 中编写服务端代码的地方,运行时 API 都受支持,包括 API 路由(如上所示)、中间件、数据加载器,以及 React Server Components 或函数。在生产环境中,expo-server 不仅能在 EAS Hosting 上运行,也支持 Express、Vercel、Netlify、Cloudflare Workers 和 Bun。
在文档中进一步了解 expo-server。
服务器 headers
现在,你可以直接在应用配置中为路由定义自定义 HTTP headers,适用于安全 headers、缓存策略、cookies 等场景。
{
"expo": {
"plugins": [
["expo-router", {
"headers": {
"X-Frame-Options": "DENY",
"Cache-Control": "public, max-age=3600"
}
}]
]
}
}
Headers 会作用于 HTML 和 API 路由的响应(但不包括静态资源!)。当你需要为 expo-sqlite on web 这类功能实现跨源隔离时,这一点尤其有用。
在文档中进一步了解服务器 headers。
实验性服务器渲染
我们为 Expo 应用新增了服务器端渲染的实验性支持。此前,Expo Router 仅支持静态站点生成(SSG),即在构建时预生成 HTML。有了服务器端渲染(SSR),HTML 会在每次请求时动态生成,非常适合个性化内容、实时数据和更好的 SEO。
{
"expo": {
"web": {
"output": "server"
},
"plugins": [
["expo-router", {
"unstable_useServerRendering": true
}]
]
}
}
在文档中进一步了解实验性服务器渲染。
实验性 data loaders
我们还为 data loaders 新增了实验性支持。Data loaders 让你能在服务器上获取数据,并通过一个新的 useLoaderData hook 在组件中访问,静态渲染和服务器渲染均适用。
这样,你就可以把数据获取和路由组件放在一起,同时又能安全地把敏感数据留在服务器端(比如环境变量),也不必再单独管理 API 路由。客户端代码只会看到 loader 函数的返回结果:
import { useLoaderData } from 'expo-router';
import { setResponseHeaders } from 'expo-server';
// Runs on the server, and has access to `process.env`
export async function loader() {
// You can also use the runtime API from `expo-server`
setResponseHeaders({ 'Cache-Control': 'max-age=60' });
return fetch('https://api.example.com/data', {
headers: { Authorization: `Bearer ${process.env.API_KEY}` }
}).then(r => r.json());
}
export default function Home() {
const data = useLoaderData<typeof loader>();
return <Text>{data.title}</Text>;
}
在文档中进一步了解。
静态渲染
在静态渲染模式下(应用配置中 web.output 设为 static 时),loaders 在构建时执行,你可以在部署前一次性预取所需数据。
服务器渲染
在服务器渲染下(应用配置中 web.output 设为 server,并启用实验性服务器渲染时),loaders 在请求时执行。如果你只是需要获取并展示数据,就不必再单独写 API 路由:
实验性分屏视图
我们与 React Native Screens 团队合作,为原生 Apple UISplitViewController 组件加入了实验性支持。在 iPad 上运行你的应用即可试用。
import { SplitView } from 'expo-router/unstable-split-view';
export default function RootLayout()
return (
<SplitView>
<SplitView.Column>
{/* Column content */}
</SplitView.Column>
<SplitView.Column>
{/* Column content */}
</SplitView.Column>
</SplitView>
);
}
在文档中进一步了解分屏视图。
从 Expo Router v55 开始
Expo Router v55 归根结底只关乎一个承诺:即便代码库是共享的,你的应用默认也应该有原生质感。 到了 SDK 55,这个承诺具体了许多。
-
用写 React 的方式构建原生导航 UI。 声明式 header、搜索、工具栏、标签页、平台配色,以及手势驱动的转场,让你不必再外挂自定义原生代码,就能做出精致的导航。
-
更扎实的 Web 基础。
expo-server、服务端 header,以及实验性的 SSR 和数据加载器,带来更好的性能、更好的 SEO 和更动态的体验,同时保持应用架构一致。 -
在更多形态的设备上提供更好的体验。 分屏视图这类特性让 Router 在 iPad 和更大屏幕上进一步迈入“真正的应用”的范畴。
如果你要升级,先看原生标签页的迁移说明,再挑一个新 API 用起来(Stack Header 或 Native Tabs 都是很好的第一步)。如果你要试实验性的 Web 特性,就像对待任何新平台能力一样:先上手折腾,必要时放在 flag 后面发布,然后把出问题的地方告诉我们,好让我们把它们打磨得平淡无奇、稳定可靠。
也给我们看看你在做什么!在社交平台上 @ 我们,发私信,或者到 Discord 里分享。我们很想看看你做出了什么!