とーますメモ

Ruby on Rails / Goなどの学習メモ

【Next.js】Material UIをSSRで使う方法

何も考えずにNext.jsでMaterial UIを使おうとすると以下のエラーが出ます。

Warning: Prop 'className' did not match. Server ...

Material UIではSSRへの対応方法が準備されており、以下がそのサンプルです。
material-ui/examples/ssr at master · mui-org/material-ui · GitHub

上記のサンプルコードを初見で見て思ったのが、どこがポイントなのよ〜ってこと。
余計なコードがいろいろはいってて、どこがポイントなのか初心者にはわかりにくいです。

色々検証してわかったのは、以下のコードを追加すれば直るってこと。

_app.tsx: useEffectのところのみ必要。

import React from 'react';

export default function MyApp({ Component, pageProps }: AppProps) {
  React.useEffect(() => {
    // Remove the server-side injected CSS.
    const jssStyles = document.querySelector('#jss-server-side');
    if (jssStyles) {
      jssStyles.parentElement!.removeChild(jssStyles);
    }
  }, []);
  ....
  return <Component {...pageProps} />;
}

_document.tsx
ServerStyleSheetsをインポートする箇所と、
MyDocument.getInitialPropsを入れればOK

import { ServerStyleSheets } from '@material-ui/core/styles';
...
...
// `getInitialProps` belongs to `_document` (instead of `_app`),
// it's compatible with server-side generation (SSG).
MyDocument.getInitialProps = async (ctx) => {
  // Resolution order
  //
  // On the server:
  // 1. app.getInitialProps
  // 2. page.getInitialProps
  // 3. document.getInitialProps
  // 4. app.render
  // 5. page.render
  // 6. document.render
  //
  // On the server with error:
  // 1. document.getInitialProps
  // 2. app.render
  // 3. page.render
  // 4. document.render
  //
  // On the client
  // 1. app.getInitialProps
  // 2. page.getInitialProps
  // 3. app.render
  // 4. page.render

  // Render app and page and get the context of the page with collected side effects.
  const sheets = new ServerStyleSheets();
  const originalRenderPage = ctx.renderPage;

  ctx.renderPage = () =>
    originalRenderPage({
      enhanceApp: (App) => (props) => sheets.collect(<App {...props} />),
    });

  const initialProps = await Document.getInitialProps(ctx);

  return {
    ...initialProps,
    // Styles fragment is rendered after the app and page rendering finish.
    styles: [...React.Children.toArray(initialProps.styles), sheets.getStyleElement()],
  };
};

ただ自分の場合、next.config.jsでreactStrictMode: trueをしてたためエラーがで出続けていました。これがわかるまでに結構大変でした。
潜在的な問題点を洗い出すためにtureにしたほうが良いようなのですが
Material UIと一緒に使う場合は、無効にしたほうが良さそう。
Next.js 4年目の知見:SSRはもう古い、VercelにAPIサーバを置くな - Qiita

Material UI v5では直ってるっぽいので、もうちょっとreactStricModeを使うのは待つほうが良いかも...
[styles] Strict Mode support · Issue #18018 · mui-org/material-ui · GitHub

[参考]
Material-UIでNext.jsのポイントを紹介する|fumi|note
フロントエンド初学者がNext.js + Typescript + FireStore + Material-UI + SSGでサイト作るまでのメモ
【Next.js】Material-UI導入時に[Warning: Prop `className` did not match. ]が発生した場合の対処法 - Qiita
Server Rendering - Material-UI