T-CREATOR

Next.jsの開発中に発生する Warning: Prop `className` did not match. Server: Client: ...の解決策

Next.jsの開発中に発生する Warning: Prop `className` did not match. Server:  Client: ...の解決策

Next.js の開発中に 発生する Warning: Prop className did not match. Server: "vsc-initialized" Client: "" Error Component Stack の警告の解決策について紹介いたします。

“Prop className did not match” は、Video Speed Controller(VSC)というブラウザ拡張が <body> へ自動付与する vsc-initialized クラスが原因です。
SSR で生成された HTML にこのクラスが無い一方、クライアント側では拡張が先にクラスを追加するため水和(Hydration)時に差分が発生します。

まずは VSC が混入元であることを確認した上で、開発・運用フェーズ別に現実的な対処を行いましょう。

原因要素の特定

vsc-initialized は Visual Studio Code ではなく Video Speed Controller 拡張が注入する識別子です。本番ユーザーが同拡張を入れていても水和前にクラスが追加されることは稀ですが、開発中はローカルサーバーの応答より拡張スクリプトの実行が速く、警告が顕在化します。

再現条件

#条件説明
1開発コマンド (next dev) 利用ローカルでの開発時に発生する
2VS Code 拡張機能が有効body.classList.add('vsc-initialized') を実行
3<body> に Tailwind などのユーティリティクラスをバインドServer → Client で文字列比較される

エラー内容(全文)

javahook.js:608 Warning: Prop `className` did not match. Server: "vsc-initialized" Client: "" Error Component Stack
    at body (<anonymous>)
    at html (<anonymous>)
    at RedirectErrorBoundary (redirect-boundary.js:73:9)
    at RedirectBoundary (redirect-boundary.js:81:11)
    at NotFoundErrorBoundary (not-found-boundary.js:76:9)
    at NotFoundBoundary (not-found-boundary.js:84:11)
    at DevRootNotFoundBoundary (dev-root-not-found-boundary.js:33:11)
    at ReactDevOverlay (ReactDevOverlay.js:84:9)
    at HotReload (hot-reloader-client.js:307:11)
    at Router (app-router.js:181:11)
    at ErrorBoundaryHandler (error-boundary.js:114:9)
    at ErrorBoundary (error-boundary.js:161:11)
    at AppRouter (app-router.js:536:13)
    at ServerRoot (app-index.js:129:11)
    at RSCComponent (<anonymous>)
    at Root (app-index.js:145:11)

対策一覧

#手順詳細効果
1VSC を無効化Chrome アドレスバー → chrome:​/​​/​extensions​/​ → Video Speed Controller を OFF根本的に警告を除去
2開発専用プロファイル新規ブラウザプロファイルを作成し拡張をインストールしない拡張を残したまま安全に開発
3インライン同期スクリプト_document.tsx<body> 直後に下記を挿入水和前にクラスを同期し警告を防止

インライン同期スクリプト の実装例(Next.js App Router)

tsx// app/_document.tsx
import { Html, Head, Main, NextScript } from 'next/document'

export default function Document() {
  return (
    <Html>
      <Head />
      <body className="bg-on-background">
        {/* dev 環境のみクラス同期 */}
        <script
          dangerouslySetInnerHTML={{
            __html: `(function(){
              try{
                if(location.hostname==='localhost' && !document.body.classList.contains('vsc-initialized')){
                  document.body.classList.add('vsc-initialized');
                }
              }catch(e){}
            })();`,
          }}
        />
        <Main />
        <NextScript />
      </body>
    </Html>
  )
}
  • インラインスクリプトは <NextScript ​/​> より前に置くことで React ハイドレーションより先に実行 されます。
  • localhost 判定で本番ビルドに影響しないよう限定します。
  • Tailwind 使用時にパージ対象外へしたい場合は tailwind.config.jssafelist: ['vsc-initialized'] を追加すればスタイル損失を防げます。

本番環境向けチェックリスト

#確認項目コマンド/設定目的
1本番ビルド検証yarn build && yarn start警告が出ないことを確認
2CSP ヘッダーContent-Security-Policy: script-src 'self'拡張スクリプトの早期実行を抑制
3早期クラス付与先述の _document.tsx スクリプト拡張ありユーザーでも差分ゼロ

まとめ

最適解は 開発ブラウザから Video Speed Controller を切り離す ことです。
どうしても無効化できない事情がある場合は、_document.tsx 内でサーバー・クライアントのクラスを同期させ、水和前の DOM 差分を解消してください。suppressHydrationWarning は緊急避難策に過ぎませんので、恒久対応としては拡張の影響範囲を明確に隔離する設計を推奨いたします。

関連リンク