【Next.js】Next.jsとMantineで発生する「hydration mismatch」エラーの解決方法

# はじめに

Next.jsとMantineを組み合わせて開発を行っている際、コンソールログに以下のようなエラーが発生したことはありませんか？

```

A tree hydrated but some attributes of the server rendered HTML didn't match the client properties. This won't be patched up.
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318188/576d882b-a29b-499e-9098-e10e2b6a8768.png)


このエラーは、サーバーサイドレンダリング（SSR）とクライアントサイドのレンダリング結果が一致しない場合に発生します。特に、Mantineのカラースキーム（ライトモード/ダークモード）を使用している場合に注意が必要です。

この記事では、「hydration mismatch」エラーの原因と、その解決方法について記載します。

# エラーの原因

Mantine では、カラースキームを管理するために `ColorSchemeScript` コンポーネントを使用します。このコンポーネントは、クライアントサイドで `<html>` タグに `data-mantine-color-scheme` 属性を追加します。しかし、サーバーサイドではこの属性が存在しないため、クライアントとサーバーでHTMLの差異が生じ、React の hydration プロセスでエラーが発生します。

この問題は、Mantine の公式ドキュメントでも言及されています。

> Hydration mismatch error can happen in two cases:
>
> 1. Server-rendered html does not match client-rendered html
> 2. Some code is executed on the client before React hydration starts and changes the html generated by the server
>    ([Mantine Help Center][2])

## 解決方法

このエラーを解消するためには、Mantine が提供する `mantineHtmlProps` を使用して、サーバーサイドでも `<html>` タグに `data-mantine-color-scheme` 属性を追加する必要があります。

## 修正前の`layout.tsx`

```layout.tsx
import "@mantine/core/styles.css";
import "@mantine/charts/styles.css";
import { ColorSchemeScript, MantineProvider } from "@mantine/core";
import { theme } from "@/libs/mantine";

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <head>
        <ColorSchemeScript />
      </head>
      <body>
        <MantineProvider theme={theme}>{children}</MantineProvider>
      </body>
    </html>
  );
}
```



## 修正後の`layout.tsx`

```layout.tsx
import "@mantine/core/styles.css";
import "@mantine/charts/styles.css";
import {
  ColorSchemeScript,
  MantineProvider,
  mantineHtmlProps,
} from "@mantine/core";
import { theme } from "@/libs/mantine";

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    // {...mantineHtmlProps} を追加
    <html lang="en" {...mantineHtmlProps}> 
      <head>
        <ColorSchemeScript />
      </head>
      <body>
        <MantineProvider theme={theme}>{children}</MantineProvider>
      </body>
    </html>
  );
}
```

`mantineHtmlProps`をスプレッド演算子で`<html>`タグに適用することで、サーバーサイドでも`data-mantine-color-scheme`属性が追加され、クライアントとのHTMLの差異が解消されます。

:::note
補足：カラースキームに関する注意点

* **クライアントサイド専用のロジックを避ける**
`window` や `localStorage` など、クライアントサイドでのみ利用可能なAPIを使用する場合は、`useEffect` 内で使用するなど、SSRとの整合性に注意してください。

* **`lightHidden`と`darkHidden`の活用**
Mantine のコンポーネントには、カラースキームに応じて表示/非表示を制御するための `lightHidden` と `darkHidden` プロパティがあります。これらを活用することで、SSRとの不整合を防ぐことができます。

* **カラースキームの管理方法の見直し**
デフォルトでは、Mantine はカラースキームの状態を `localStorage` に保存しますが、SSR との整合性を保つために、カラースキームの状態を Cookie などのサーバーサイドでもアクセス可能なストレージに保存する方法も検討してください。
:::

# まとめ

Next.js と Mantine を組み合わせて使用する際に発生する「hydration mismatch」エラーは、`<html>` タグに `data-mantine-color-scheme` 属性がサーバーサイドで存在しないことが原因です。この問題は、`mantineHtmlProps` を使用して解決できます。また、カラースキームに関連するロジックを実装する際は、SSRとの整合性に注意し、適切な方法で管理することが重要です。

MantineとNext.jsを組み合わせた開発を行う際の参考になれば幸いです。

# 参考
- [hydrateRoot](https://react.dev/reference/react-dom/client/hydrateRoot#hydrating-server-rendered-html)
- [I get hydration warning about data-mantine-color-scheme attribute, what does it mean?](https://help.mantine.dev/q/color-scheme-hydration-warning?utm_source=chatgpt.com)