Future Flags

次のフューチャーフラグは安定しており、採用する準備ができています。フューチャーフラグの詳細については、開発戦略を参照してください。

最新のv2.xへのアップデート

最新のフューチャーフラグを利用するには、まずv2.xの最新マイナーバージョンにアップデートしてください。

👉 最新のv2にアップデート

npm install @remix-run/{dev,react,node,etc.}@2

Vite Plugin

背景

Remixは、独自のクローズドコンパイラ(現在は「クラシックコンパイラ」と呼ばれています)を使用せず、Viteを使用するようになりました。Viteは、JavaScriptプロジェクトのための強力で高性能、拡張可能な開発環境です。Viteドキュメントでは、パフォーマンス、トラブルシューティングなどの詳細を確認できます。

これはフューチャーフラグではありませんが、新しい機能と一部のフューチャーフラグはViteプラグインでのみ利用可能であり、クラシックコンパイラはRemixの次のバージョンで削除される予定です。

👉 Viteをインストール

npm install -D vite

コードのアップデート

👉 Remixアプリのルートにあるremix.config.jsvite.config.tsに置き換える

vite.config.ts
import { vitePlugin as remix } from "@remix-run/dev";
import { defineConfig } from "vite";
 
export default defineConfig({
  plugins: [remix()],
});

サポートされているRemix構成オプションのサブセットは、プラグインに直接渡す必要があります。

vite.config.ts
export default defineConfig({
  plugins: [
    remix({
      ignoredRouteFiles: ["**/*.css"],
    }),
  ],
});

👉 <LiveReload/>を削除し、<Scripts />を残す

  import {
-   LiveReload,
    Outlet,
    Scripts,
  }
 
  export default function App() {
    return (
      <html>
        <head>
        </head>
        <body>
          <Outlet />
-         <LiveReload />
          <Scripts />
        </body>
      </html>
    )
  }

👉 tsconfig.jsonをアップデートする

tsconfig.jsontypesフィールドをアップデートし、skipLibCheckmodulemoduleResolutionがすべて正しく設定されていることを確認してください。

tsconfig.json
{
  "compilerOptions": {
    "types": ["@remix-run/node", "vite/client"],
    "skipLibCheck": true,
    "module": "ESNext",
    "moduleResolution": "Bundler"
  }
}

👉 remix.env.d.tsをアップデート/削除する

remix.env.d.ts内の次の型宣言を削除してください。

remix.env.d.ts
- /// <reference types="@remix-run/dev" />
- /// <reference types="@remix-run/node" />

remix.env.d.tsが空になったら、削除してください。

rm remix.env.d.ts

パスエイリアスの設定

Viteはデフォルトでパスエイリアスを提供しません。~appディレクトリのエイリアスとして定義するなど、この機能に依存していた場合は、vite-tsconfig-pathsプラグインをインストールして、Viteのtsconfig.jsonからパスエイリアスを自動的に解決し、Remixコンパイラの動作に合わせることができます。

👉 vite-tsconfig-pathsをインストールする

npm install -D vite-tsconfig-paths

👉 Vite構成にvite-tsconfig-pathsを追加する

vite.config.ts
import { vitePlugin as remix } from "@remix-run/dev";
import { defineConfig } from "vite";
import tsconfigPaths from "vite-tsconfig-paths";
 
export default defineConfig({
  plugins: [remix(), tsconfigPaths()],
});

@remix-run/css-bundleを削除する

Viteは、CSS副作用インポート、PostCSS、CSS Modulesなど、他のCSSバンドル機能に対する組み込みのサポートを提供しています。Remix Viteプラグインは、バンドルされたCSSを関連するルートに自動的にアタッチします。

@remix-run/css-bundleパッケージは、Viteを使用する場合は冗長です。そのcssBundleHrefエクスポートは常にundefinedになるためです。

👉 @remix-run/css-bundleをアンインストールする

npm uninstall @remix-run/css-bundle

👉 cssBundleHrefへの参照を削除する

app/root.tsx
- import { cssBundleHref } from "@remix-run/css-bundle";
  import type { LinksFunction } from "@remix-run/node"; // or cloudflare/deno
 
  export const links: LinksFunction = () => [
-   ...(cssBundleHref
-     ? [{ rel: "stylesheet", href: cssBundleHref }]
-     : []),
    // ...
  ];

links内で参照されているCSSインポートを修正する

CSSをlinks関数内で参照している場合は、対応するCSSインポートをViteの明示的な?urlインポート構文を使用するように更新する必要があります。

👉 linksで使用されているCSSインポートに?urlを追加する

-import styles from "~/styles/dashboard.css";
+import styles from "~/styles/dashboard.css?url";
 
export const links = () => {
  return [
    { rel: "stylesheet", href: styles }
  ];
}

Tailwind CSSまたはVanilla Extractの移行

Tailwind CSSまたはVanilla Extractを使用している場合は、完全な移行ガイドを参照してください。

Remix App Serverからの移行

👉 devbuildstartスクリプトをアップデートする

package.json
{
  "scripts": {
    "dev": "remix vite:dev",
    "build": "remix vite:build",
    "start": "remix-serve ./build/server/index.js"
  }
}

👉 Vite構成にグローバルNodeポリフィルをインストールする

vite.config.ts
import { vitePlugin as remix } from "@remix-run/dev";
+import { installGlobals } from "@remix-run/node";
import { defineConfig } from "vite";
 
+installGlobals();
 
export default defineConfig({
  plugins: [remix()],
});

👉 Vite devサーバーポートを設定する(オプション)

vite.config.ts
export default defineConfig({
  server: {
    port: 3000,
  },
  plugins: [remix()],
});

カスタムサーバーの移行

カスタムサーバーまたはCloudflare Functionsを移行している場合は、完全な移行ガイドを参照してください。

MDXルートの移行

MDXを使用している場合は、公式のMDX Rollupプラグインを使用する必要があります。ステップバイステップのチュートリアルについては、完全な移行ガイドを参照してください。

v3_fetcherPersist

背景

フェッチャーのライフサイクルは、所有者コンポーネントのマウント解除時ではなく、アイドル状態に戻るタイミングに基づくようになりました。詳細については、RFCを参照してください。

👉 フラグを有効にする

vite.config.ts
remix({
  future: {
    v3_fetcherPersist: true,
  },
});

コードのアップデート

アプリケーションへの影響はほとんどないと思われます。useFetchersの使用状況を確認し、以前よりも長く持続する可能性があるかどうかを確認する必要があります。実行している処理によっては、以前よりも長くレンダリングされる場合があります。

v3_relativeSplatPath

背景

dashboard/*(単なる*に対して)のように、マルチセグメントスプラットパスに対する相対パスマッチングとリンクを変更します。詳細については、CHANGELOGを参照してください。

👉 フラグを有効にする

vite.config.ts
remix({
  future: {
    v3_relativeSplatPath: true,
  },
});

コードのアップデート

dashboard.$.tsxまたはroute("dashboard/*")のように、パスとスプラットを持つルートで、その下に"<Link to="relative">"または"<Link to="../relative">"などの相対リンクがある場合は、コードを更新する必要があります。

👉 ルートを2つに分割する

スプラットルートがある場合は、レイアウトルートと、スプラットを含む子ルートに分割します。

 
└── routes
    ├── _index.tsx
+   ├── dashboard.tsx
    └── dashboard.$.tsx
 
// or
routes(defineRoutes) {
  return defineRoutes((route) => {
    route("/", "home/route.tsx", { index: true });
-    route("dashboard/*", "dashboard/route.tsx")
+    route("dashboard", "dashboard/layout.tsx", () => {
+      route("*", "dashboard/route.tsx");
    });
  });
},

👉 相対リンクをアップデートする

そのルートツリー内の相対リンクを持つ"<Link>"要素をすべてアップデートし、同じ場所にリンクし続けるために、余分な..相対セグメントを含めます。

// dashboard.$.tsx or dashboard/route.tsx
function Dashboard() {
  return (
    <div>
      <h2>Dashboard</h2>
      <nav>
-        <Link to="">Dashboard Home</Link>
-        <Link to="team">Team</Link>
-        <Link to="projects">Projects</Link>
+        <Link to="../">Dashboard Home</Link>
+        <Link to="../team">Team</Link>
+        <Link to="../projects">Projects</Link>
      </nav>
    </div>
  );
}

v3_throwAbortReason

背景

ユーザーがローダーが完了する前にページから移動した場合など、サーバー側のリクエストが中止されると、Remixはnew Error("query() call aborted...")などのエラーではなく、request.signal.reasonをスローします。

👉 フラグを有効にする

vite.config.ts
remix({
  future: {
    v3_throwAbortReason: true,
  },
});

コードのアップデート

以前のエラーメッセージに一致させて他のエラーと区別していたhandleError内のカスタムロジックがない限り、コードを調整する必要はありません。

v3_singleFetch

背景

このフラグにより、Remixはアプリ内でSPAナビゲーションを行う場合のデータ要求に対して、「単一フェッチ」アプローチに移行します。詳細については、ドキュメントを参照してください。このアプローチに移行することを選択した主な理由は、シンプルさです。Single Fetchでは、データ要求はドキュメント要求と同じように動作するようになり、開発者はヘッダー、キャッシュなどの管理方法を2つの間でどのように異ならせるかについて考える必要がなくなります。より高度なユースケースの場合、開発者は引き続き細かい再検証を選択できます。

👉 フラグ(および型)を有効にする

vite.config.ts
import { vitePlugin as remix } from "@remix-run/dev";
import { defineConfig } from "vite";
import tsconfigPaths from "vite-tsconfig-paths";
 
declare module "@remix-run/node" {
  // or cloudflare, deno, etc.
  interface Future {
    v3_singleFetch: true;
  }
}
 
export default defineConfig({
  plugins: [
    remix({
      future: {
        v3_singleFetch: true,
      },
    }),
    tsconfigPaths(),
  ],
});

コードのアップデート

このフラグを有効にすると、ほとんどの場合、コードをそのまま使用できます。ただし、次の変更は時間とともに加える必要があり、次のメジャーバージョン以前に行う必要があります。

👉 json()/defer()を生のオブジェクトに置き換える

Single Fetchは、JSONオブジェクトとPromiseをネイティブにサポートしているので、loader/action関数から生のデータを返すことができます。

-import { json } from "@remix-run/node";
 
export async function loader({}: LoaderFunctionArgs) {
  let tasks = await fetchTasks();
- return json(tasks);
+ return tasks;
}
-import { defer } from "@remix-run/node";
 
export async function loader({}: LoaderFunctionArgs) {
  let lazyStuff = fetchLazyStuff();
  let tasks = await fetchTasks();
- return defer({ tasks, lazyStuff });
+ return { tasks, lazyStuff };
}

json/deferの2番目のパラメーターを使用して、レスポンスにカスタムステータスまたはヘッダーを設定していた場合は、新しいdata APIを使用して引き続き行うことができます。

-import { json } from "@remix-run/node";
+import { data } from "@remix-run/node";
 
export async function loader({}: LoaderFunctionArgs) {
  let tasks = await fetchTasks();
-  return json(tasks, {
+  return data(tasks, {
    headers: {
      "Cache-Control": "public, max-age=604800"
    }
  });
}

👉 サーバーの中止遅延を調整する

entry.server.tsxファイルでカスタムABORT_DELAYを使用していた場合は、Single Fetchで利用されている新しいstreamTimeout APIを使用するように変更する必要があります。

entry.server.tsx
-const ABORT_DELAY = 5000;
+// Reject/cancel all pending promises after 5 seconds
+export const streamTimeout = 5000;
 
// ...
 
function handleBrowserRequest(/* ... */) {
  return new Promise((resolve, reject) => {
    const { pipe, abort } = renderToPipeableStream(
      <RemixServer
        context={remixContext}
        url={request.url}
-        abortDelay={ABORT_DELAY}
      />,
      {
        onShellReady() {
          /* ... */
        },
        onShellError(error: unknown) {
          /* ... */
        },
        onError(error: unknown) {
          /* ... */
        },
      }
    );
 
-    setTimeout(abort, ABORT_DELAY);
+   // Automatically timeout the React renderer after 6 seconds, which ensures
+   // React has enough time to flush down the rejected boundary contents
+   setTimeout(abort, streamTimeout + 1000);
  });
}

v3_lazyRouteDiscovery

背景

このフラグを使用すると、Remixは最初のロード時にクライアントに完全なルートマニフェストを送信しなくなります。代わりに、サーバーレンダリングされたルートのみをマニフェストに送信し、ユーザーがアプリケーション内を移動するときに残りのルートを取得します。詳細については、ドキュメントブログ投稿を参照してください。

👉 フラグを有効にする

vite.config.ts
remix({
  future: {
    v3_lazyRouteDiscovery: true,
  },
});

コードのアップデート

この機能を使用するために、アプリケーションコードを変更する必要はありません。

特定のリンクで積極的なルート検出を無効にしたい場合は、新しい<Link discover> APIを使用することができます。

unstable_optimizeDeps

開発中に依存関係の最適化を自動的に行うようにします。このフラグは、React Router v7まで「不安定」な状態のままとなるため、Remix v2アプリでReact Router v7にアップグレードする前に、このフラグを採用する必要はありません。