React Router v7 は、Remix v2 の後のメジャーバージョンです(詳細は、"React 19への段階的な移行"ブログ投稿 を参照してください)。
すべてのRemix v2 の将来的なフラグを有効にしている場合、Remix v2 から React Router v7 へのアップグレードは、主に依存関係の更新を伴います。
手順 2~8 の大部分は、コミュニティメンバーのJames Restall が作成したcodemod を使用して自動的に更新できます。
👉 将来的なフラグを採用する
Remix v2 アプリケーションですべての既存の将来的なフラグを採用します。
以前はランタイム固有のパッケージ(`@remix-run/node`、`@remix-run/cloudflare`など)を通じて再エクスポートされていた「共有」API のほとんどは、v7 では `react-router` に統合されました。そのため、`@react-router/node` または `@react-router/cloudflare` からインポートする代わりに、`react-router` から直接インポートします。
-import { redirect } from "@remix-run/node";
+import { redirect } from "react-router";
v7 でランタイム固有のパッケージからインポートする必要がある API は、ノードの `createFileSessionStorage` や Cloudflare の `createWorkersKVSessionStorage` など、そのランタイムに固有の API のみです。
👉 codemod を実行する(自動化)
次のcodemod を使用して、パッケージとインポートを自動的に更新できます。この codemod は、すべてのパッケージとインポートを更新します。元に戻す必要がある場合に備え、codemod を実行する前に、保留中の変更をコミットしてください。
npx codemod remix/2/react-router/upgrade
👉 新しい依存関係をインストールする
codemod で依存関係が更新された後、Remix パッケージを削除し、新しい React Router パッケージを追加するために、依存関係をインストールする必要があります。
npm install
👉 依存関係を更新する(手動)
codemod を使用しない場合は、手動で依存関係を更新できます。
Remix v2 パッケージ | React Router v7 パッケージ | |
---|---|---|
@remix-run/architect |
➡️ | @react-router/architect |
@remix-run/cloudflare |
➡️ | @react-router/cloudflare |
@remix-run/dev |
➡️ | @react-router/dev |
@remix-run/express |
➡️ | @react-router/express |
@remix-run/fs-routes |
➡️ | @react-router/fs-routes |
@remix-run/node |
➡️ | @react-router/node |
@remix-run/react |
➡️ | react-router |
@remix-run/route-config |
➡️ | @react-router/dev |
@remix-run/routes-option-adapter |
➡️ | @react-router/remix-routes-option-adapter |
@remix-run/serve |
➡️ | @react-router/serve |
@remix-run/server-runtime |
➡️ | react-router |
@remix-run/testing |
➡️ | react-router |
codemod を使用した場合は、自動的に完了しているため、この手順をスキップできます。
👉 `package.json` のスクリプトを更新する
スクリプト | Remix v2 | React Router v7 | |
---|---|---|---|
dev |
remix vite:dev |
➡️ | react-router dev |
build |
remix vite:build |
➡️ | react-router build |
start |
remix-serve build/server/index.js |
➡️ | react-router-serve build/server/index.js |
typecheck |
tsc |
➡️ | react-router typegen && tsc |
codemod を使用しており、Remix v2 の `v3_routeConfig` フラグを使用している場合は、自動的に完了しているため、この手順をスキップできます。
React Router v7 では、`app/routes.ts` ファイルを使用してルートを定義します。詳細はルーティングドキュメントを参照してください。
👉 依存関係を更新する(Remix v2 の `v3_routeConfig` フラグを使用している場合)
// app/routes.ts
-import { type RouteConfig } from "@remix-run/route-config";
-import { flatRoutes } from "@remix-run/fs-routes";
-import { remixRoutesOptionAdapter } from "@remix-run/routes-option-adapter";
+import { type RouteConfig } from "@react-router/dev/routes";
+import { flatRoutes } from "@react-router/fs-routes";
+import { remixRoutesOptionAdapter } from "@react-router/remix-routes-option-adapter";
export default [
// however your routes are defined
] satisfies RouteConfig;
👉 `routes.ts` ファイルを追加する(Remix v2 の `v3_routeConfig` フラグを使用していない場合)
touch app/routes.ts
下位互換性のため、そしてファイルベースの規約を好む人のために、新しい `@react-router/fs-routes` パッケージを介して、Remix v2 で使用しているのと同じ「フラットルート」規約を選択できます。
import { type RouteConfig } from "@react-router/dev/routes";
import { flatRoutes } from "@react-router/fs-routes";
export default flatRoutes() satisfies RouteConfig;
または、`routes` オプションを使用して設定ベースのルートを定義していた場合
import { type RouteConfig } from "@react-router/dev/routes";
import { remixRoutesOptionAdapter } from "@react-router/remix-routes-option-adapter";
export default remixRoutesOptionAdapter((defineRoutes) => {
return defineRoutes((route) => {
route("/", "home/route.tsx", { index: true });
route("about", "about/route.tsx");
route("", "concerts/layout.tsx", () => {
route("trending", "concerts/trending.tsx");
route(":city", "concerts/city.tsx");
});
});
}) satisfies RouteConfig;
`vite.config.ts` で `routes` オプションを使用していた場合は、削除してください。
export default defineConfig({
plugins: [
remix({
ssr: true,
- ignoredRouteFiles: ['**/*'],
- routes(defineRoutes) {
- return defineRoutes((route) => {
- route("/somewhere/cool/*", "catchall.tsx");
- });
- },
})
tsconfigPaths(),
],
});
👉 プロジェクトに `react-router.config.ts` を追加する
以前 `vite.config.ts` の `remix` プラグインに渡されていた設定は、現在 `react-router.config.ts` からエクスポートされます。
注:この時点で、手順 1 で追加した v3 の将来的なフラグを削除する必要があります。
touch react-router.config.ts
// vite.config.ts
export default defineConfig({
plugins: [
- remix({
- ssr: true,
- future: {/* all the v3 flags */}
- }),
+ remix(),
tsconfigPaths(),
],
});
// react-router.config.ts
+import type { Config } from "@react-router/dev/config";
+export default {
+ ssr: true,
+} satisfies Config;
codemod を使用した場合は、自動的に完了しているため、この手順をスキップできます。
👉 `vite.config` に `reactRouter` プラグインを追加する
`vite.config.ts` を変更して、`@react-router/dev/vite` から新しい `reactRouter` プラグインをインポートして使用します。
-import { vitePlugin as remix } from "@remix-run/dev";
+import { reactRouter } from "@react-router/dev/vite";
import { defineConfig } from "vite";
import tsconfigPaths from "vite-tsconfig-paths";
export default defineConfig({
plugins: [
- remix(),
+ reactRouter(),
tsconfigPaths(),
],
});
TypeScript を使用していない場合は、この手順をスキップできます。
React Router は、ルートモジュールの型をアプリのルートにある `.react-router/` ディレクトリに自動的に生成します。このディレクトリは React Router によって完全に管理されており、`.gitignore` に追加する必要があります。新しい型安全性の機能の詳細については、こちらを参照してください。
👉 `.gitignore` に `.react-router/` を追加する
.react-router/
👉 `tsconfig.json` を更新する
`tsconfig.json` の `types` フィールドを更新して、以下を含めます。
{
"include": [
/* ... */
+ ".react-router/types/**/*"
],
"compilerOptions": {
- "types": ["@remix-run/node", "vite/client"],
+ "types": ["@react-router/node", "vite/client"],
/* ... */
+ "rootDirs": [".", "./.react-router/types"]
}
}
codemod を使用した場合は、自動的に完了しているため、この手順をスキップできます。
アプリケーションに `entry.server.tsx` および/または `entry.client.tsx` ファイルがある場合は、これらのファイルのメインコンポーネントを更新する必要があります。
-import { RemixServer } from "@remix-run/react";
+import { ServerRouter } from "react-router";
-<RemixServer context={remixContext} url={request.url} />,
+<ServerRouter context={remixContext} url={request.url} />,
-import { RemixBrowser } from "@remix-run/react";
+import { HydratedRouter } from "react-router/dom";
hydrateRoot(
document,
<StrictMode>
- <RemixBrowser />
+ <HydratedRouter />
</StrictMode>,
);
`remix-serve` を使用していた場合は、この手順をスキップできます。これは、Remix v2 でカスタムサーバーを使用していた場合にのみ適用されます。
React Router は React フレームワークとスタンドアロンのルーティングライブラリの両方として使用できるため、`LoaderFunctionArgs` と `ActionFunctionArgs` の `context` 引数は、デフォルトではオプションで `any` として型指定されます。ローダーとアクションの型安全性を確保するために、ロードコンテキストの型を登録できます。
👉 **ロードコンテキストの型を登録する**
新しい `Route.LoaderArgs` と `Route.ActionArgs` 型に移行する前に、移行を容易にするために、`LoaderFunctionArgs` と `ActionFunctionArgs` をロードコンテキスト型で一時的に拡張できます。
declare module "react-router" {
// Your AppLoadContext used in v2
interface AppLoadContext {
whatever: string;
}
// TODO: remove this once we've migrated to `Route.LoaderArgs` instead for our loaders
interface LoaderFunctionArgs {
context: AppLoadContext;
}
// TODO: remove this once we've migrated to `Route.ActionArgs` instead for our actions
interface ActionFunctionArgs {
context: AppLoadContext;
}
}
export {}; // necessary for TS to treat this as a module
`declare module` を使用して型を登録することは、モジュール拡張と呼ばれる標準的な TypeScript の手法です。これは、`tsconfig.json` の `include` フィールドで対象となる任意の TypeScript ファイルで行うことができますが、アプリディレクトリ内の専用の `env.ts` をお勧めします。
👉 **新しい型を使用する**
新しい型生成を採用したら、`LoaderFunctionArgs`/`ActionFunctionArgs` の拡張を削除し、代わりに`Route.LoaderArgs` と`Route.ActionArgs` から `context` 引数を使用できます。
declare module "react-router" {
// Your AppLoadContext used in v2
interface AppLoadContext {
whatever: string;
}
}
export {}; // necessary for TS to treat this as a module
import type { Route } from "./+types/my-route";
export function loader({ context }: Route.LoaderArgs) {}
// { whatever: string } ^^^^^^^
export function action({ context }: Route.ActionArgs) {}
// { whatever: string } ^^^^^^^
おめでとうございます!これで React Router v7 への移行が完了しました。アプリケーションを実行して、すべてが期待通りに動作することを確認してください。