RemixでUncaught Error: Hydration failedにハマった件について

2024/05/22に公開

RemixでSPAモードテンプレートを使用してアプリケーションを作成し、起動した直後に以下のエラーが発生しました。本記事では、このエラーの対処方法をメモしておきます。誰かの時短になれば幸いです。

package.json の依存関係

{
  "dependencies": {
    "@remix-run/node": "^2.9.2",
    "@remix-run/react": "^2.9.2",
    "react": "^18.2.0",
    "react-dom": "^18.2.0"
  }
}

エラー内容

Warning: Expected server HTML to contain a matching <head> in <html>.
Warning: Expected server HTML to contain a matching <head> in <html>.
    at head
    at html
    at Layout (http://localhost:5173/app/root.tsx:4:3)
    at RenderedRoute (http://localhost:5173/node_modules/.vite/deps/@remix-run_react.js?v=9f5cdcd6:3826:5)
    at RenderErrorBoundary (http://localhost:5173/node_modules/.vite/deps/@remix-run_react.js?v=9f5cdcd6:3786:5)
    at DataRoutes (http://localhost:5173/node_modules/.vite/deps/@remix-run_react.js?v=9f5cdcd6:4813:5)
    at Router (http://localhost:5173/node_modules/.vite/deps/@remix-run_react.js?v=9f5cdcd6:4167:15)
    at RouterProvider (http://localhost:5173/node_modules/.vite/deps/@remix-run_react.js?v=9f5cdcd6:4630:5)
    at RemixErrorBoundary (http://localhost:5173/node_modules/.vite/deps/@remix-run_react.js?v=9f5cdcd6:7187:5)
    at RemixBrowser (http://localhost:5173/node_modules/.vite/deps/@remix-run_react.js?v=9f5cdcd6:7842:46)
Uncaught Error: Hydration failed because the initial UI does not match what was rendered on the server.
Uncaught Error: Hydration failed because the initial UI does not match what was rendered on the server.
    at throwOnHydrationMismatch (chunk-NEIDWZIT.js?v=7203cc1d:9489:17)
    at tryToClaimNextHydratableInstance (chunk-NEIDWZIT.js?v=7203cc1d:9510:15)
    at updateHostComponent (chunk-NEIDWZIT.js?v=7203cc1d:14812:13)
    at beginWork (chunk-NEIDWZIT.js?v=7203cc1d:15953:22)
    at HTMLUnknownElement.callCallback2 (chunk-NEIDWZIT.js?v=7203cc1d:3672:22)
    at Object.invokeGuardedCallbackDev (chunk-NEIDWZIT.js?v=7203cc1d:3697:24)
    at invokeGuardedCallback (chunk-NEIDWZIT.js?v=7203cc1d:3731:39)
    at beginWork$1 (chunk-NEIDWZIT.js?v=7203cc1d:19791:15)
    at performUnitOfWork (chunk-NEIDWZIT.js?v=7203cc1d:19224:20)
    at workLoopConcurrent (chunk-NEIDWZIT.js?v=7203cc1d:19215:13)
Error: Hydration failed because the initial UI does not match what was rendered on the server.
Error: Hydration failed because the initial UI does not match what was rendered on the server.
    at throwOnHydrationMismatch (chunk-NEIDWZIT.js?v=7203cc1d:9489:17)
    at tryToClaimNextHydratableInstance (chunk-NEIDWZIT.js?v=7203cc1d:9510:15)
    at updateHostComponent (chunk-NEIDWZIT.js?v=7203cc1d:14812:13)
    at beginWork (chunk-NEIDWZIT.js?v=7203cc1d:15953:22)
    at beginWork$1 (chunk-NEIDWZIT.js?v=7203cc1d:19779:22)
    at performUnitOfWork (chunk-NEIDWZIT.js?v=7203cc1d:19224:20)
    at workLoopConcurrent (chunk-NEIDWZIT.js?v=7203cc1d:19215:13)
    at renderRootConcurrent (chunk-NEIDWZIT.js?v=7203cc1d:19190:15)
    at performConcurrentWorkOnRoot (chunk-NEIDWZIT.js?v=7203cc1d:18704:46)
    at workLoop (chunk-NEIDWZIT.js?v=7203cc1d:195:42)
Uncaught Error: There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.
Uncaught Error: There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.
    at updateHostRoot (chunk-NEIDWZIT.js?v=7203cc1d:14778:65)
    at beginWork (chunk-NEIDWZIT.js?v=7203cc1d:15951:22)
    at beginWork$1 (chunk-NEIDWZIT.js?v=7203cc1d:19779:22)
    at performUnitOfWork (chunk-NEIDWZIT.js?v=7203cc1d:19224:20)
    at workLoopSync (chunk-NEIDWZIT.js?v=7203cc1d:19163:13)
    at renderRootSync (chunk-NEIDWZIT.js?v=7203cc1d:19142:15)
    at recoverFromConcurrentError (chunk-NEIDWZIT.js?v=7203cc1d:18762:28)
    at performConcurrentWorkOnRoot (chunk-NEIDWZIT.js?v=7203cc1d:18710:30)
    at workLoop (chunk-NEIDWZIT.js?v=7203cc1d:195:42)
    at flushWork (chunk-NEIDWZIT.js?v=7203cc1d:174:22)

エラーのキャプチャ

公式Issue

Remix Issue #2947

対処法

対処1: Reactのバージョンアップ (v19)

このエラーはReact側の問題として報告されており、v19ではエラーが解消されています。

package.json の修正

{
  "dependencies": {
    "@remix-run/node": "^2.9.2",
    "@remix-run/react": "^2.9.2",
-    "react": "^18.2.0",
-    "react-dom": "^18.2.0"
+    "react": "19.0.0-beta-26f2496093-20240514",
+    "react-dom": "19.0.0-beta-26f2496093-20240514"
  }
}

エラー解消後のキャプチャ:

参考コメント

対処2: ハイドレーション適応範囲の修正

以下の手順で対処します:

  1. ハイドレーション適応範囲を修正
  2. HTMLのメタ情報を書き換えるためのOSSをインストール (react-helmet-async)

1. ハイドレーション適応範囲の修正 (full documentbody の一部)

公式ガイドに従い、ハイドレーションの対象を body の一部に修正します。

Remix Docs: Hydrating a div instead of the full document

2. HTMLのメタ情報を書き換えるためのOSSをインストール(react-helmet-async

react-helmet-async をインストールして、HTMLのメタ情報を動的に変更します。

npm i react-helmet-async
app/entry.client.tsx の修正

HelmetProvider でアプリケーション全体をラップします。

app/entry.client.tsx
+ import { HelmetProvider } from 'react-helmet-async';

startTransition(() => {
  hydrateRoot(
    document.querySelector("#app")!,
    <StrictMode>
+     <HelmetProvider>
        <RemixBrowser />
+     </HelmetProvider>
    </StrictMode>
  );
});
app/root.tsx の修正

任意のルートファイルで Helmet コンポーネントを利用し、HTMLのメタ情報を書き換えます。

app/root.tsx
+ import * as HelmetAsync from "react-helmet-async"; // デフォルトエクスポートとしてインポート
+ const { Helmet } = HelmetAsync; // 必要なコンポーネントを取得

export default function Component() {
  return (
    <>
+     <Helmet>
+       <title>Remix App</title>
+       <meta name="description" content="Welcome to Remix!" />
+     </Helmet>
      <Outlet />
      <Scripts />
    </>
  );
}

公式リファレンス

Remix Docs: Hydrating a div instead of the full document

参考コメント


以上が、RemixでSPAモードテンプレートを使用する際のエラー対処方法です。
この記事がどなたかの助けになれば幸いです。

Discussion