Expo Router v55:ネイティブナビゲーションの強化と、Web 機能のさらなる前進

Expo Router v55 ではナビゲーションが強化され、新しい Stack API、ネイティブタブ、動的なカラーとツールバー、さらに実験的な SSR とデータローダーが導入された。

日本語
コピー
Expo Router v55: more native navigation, more powerful web

Expo Router v55 が登場しました(そう、バージョン番号を一気に 49 個飛ばしました)。これは Expo Router のリリース以来ずっと追いかけてきた目標に向けた大きな一歩です。アプリをよりネイティブに感じさせつつ、より複雑な Web のシナリオにも対応し、本当の意味で一度書けば済むようにする、という目標です。

このバージョンでは同時に二つの大きなことをやりました。

ネイティブ UI をルーティングの一級市民に。 新しい Stack API、ツールバー、ネイティブタブ、動的なプラットフォームカラー、ズームトランジション、Split View 対応により、ナビゲーション UI をアプリの他の部分と同じやり方で構築できます。React コンポーネントを使い、画面のあるべき位置にそのまま組み合わせるだけです。

Web における「一度書けば済む」の意味を広げました。 Expo Router に、より明確なランタイムの選択肢が加わりました——expo-server、設定可能なサーバーレスポンスヘッダー、そして実験的なサーバーサイドレンダリングとデータローダーです。別途 Web スタックを用意しなくても、より高速なページ、より良い SEO、より動的なコンテンツを届けられます。

X の投稿

一部の API はまだ実験段階にあり、最良の体験のために変更される可能性があります。2026/02/xx 時点で、本記事に掲載している API はすべて最新ですが、より新しいサンプルは常に Expo Router のドキュメントを参照してください。

ここからは、新機能、それがなぜ重要なのか、そして今日どこから始められるかを順に見ていきます。

新しい Stack API

新しい Stack API では、ネイティブヘッダーを宣言的に、コンポーネント単位で制御できます。options API を、組み合わせ可能な React コンポーネントに置き換えました。画面の中でそのまま使えます。ヘッダーのスタイル設定、タイトルの設定、検索バーの追加——すべてコンポーネントツリーの中で完結します。

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 の動的カラーを使い、ユーザーが好む色に自動で適応します。iOS の SF Symbols と同様に、NativeTabs.Triggermd 属性を指定することで Material 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.TriggerwithAppleZoom を設定します。

<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 の詳細を確認してください。

サーバーヘッダー

アプリ設定でルートごとにカスタム HTTP ヘッダーを定義できるようになりました。セキュリティヘッダー、キャッシュ戦略、Cookie などに使えます。

{
  "expo": {
    "plugins": [
      ["expo-router", {
        "headers": {
          "X-Frame-Options": "DENY",
          "Cache-Control": "public, max-age=3600"
        }
      }]
    ]
  }
}

ヘッダーは HTML と API ルートのレスポンスに適用されます(静的アセットは対象外です)。expo-sqlite on web のような機能でクロスオリジン分離を実現したいときに特に役立ちます。

詳細はドキュメントをご覧ください。

実験的なサーバーレンダリング

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.outputstatic に設定)では、loaders はビルド時に実行されます。デプロイ前に必要なデータを一度だけ先読みできます。

サーバーレンダリング

サーバーレンダリング(アプリ設定で web.outputserver に設定し、実験的なサーバーレンダリングを有効化)では、loaders はリクエスト時に実行されます。データを取得して表示するだけなら、API ルートを別途書く必要はありません。

実験的な split view

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、サーバーヘッダー、実験的な SSR とデータローダーにより、アプリのアーキテクチャを揃えたまま、パフォーマンス、SEO、動的な体験が向上します。

  • より多くのデバイス形状で、より良い体験を。 split view のような機能によって、Router は iPad やより大きな画面でも「本物のアプリ」の領域にさらに踏み込みます。

アップグレードする場合は、まずネイティブタブの移行ガイドを読み、新しい API を一つ選んで使ってみてください(Stack Header か Native Tabs が良い最初の一歩です)。実験的な Web 機能を試すときは、他の新しいプラットフォーム機能と同じように、まず触ってみて、必要ならフラグの裏でリリースし、問題があれば教えてください。こちらで磨き上げて、ありふれた安定した機能にします。

あなたが何を作っているのかも見せてください! SNS でメンションするか、DM を送るか、Discord で共有してください。どんなものを作ったのか、楽しみにしています。

出典: Expo Blog← ホームへ戻る