Next.js + Material UI v5 でフロントエンドアプリケーションを作成する

Next.js + Material UI v5 でフロントエンドアプリケーションを作成する

なぜこの記事を書いたのか

こんにちは。エニグモでサーバサイドエンジニアをしております、寺田(@mterada1228)です。

この記事は Enigmo Advent Calendar 2021 の 20 日目の記事です。

業務では主に Ruby on Rails を使っているのですが、最近新しいチャレンジとして、フロントエンドの勉強をしています。

そこで、Next.js + Material UI(以降 MUI)を使った Web アプリケーションの開発にチャレンジすることにしました。

ただ、この2つは全く異なる開発形態のフレームワークですので、一緒に使うためにはちょっとしたセットアップが必要になります。

参考にするため色々と検索してみましたが、なかなか求めている検索結果が得られなかったことと、なぜこのようなセットアップが必要になるかまで詳しくまとめられているものがなかったので、今回自分で調べて一つの記事を書いてみることにしました。

なお、本記事では Next.js で MUI を使うためのセットアップに焦点を絞っているので、Next.js のプロジェクトを立ち上げる部分は説明しません。

その部分を詳しく知りたい方は、公式チュートリアルを参考にしてもらえると良いかと思います。

Material UI を使うために必要なパッケージをインストールする

MUI v5 のパッケージをインストールしていきます。

ひとまず、コアな機能をインストールすれば十分かと思いますので、 @mui/material というパッケージをインストールしていきます。他のパッケージについては、必要に応じてインストールして下さい。

また、MUI ではスタイリングのために内部で CSS in JS のためのライブラリを使用しています。 こちらは、emotion もしくは styled-component のいずれかを入れる必要があります。

  • emotion を使う場合

    // with npm
    $ npm install @mui/material @emotion/react @emotion/styled
    
    // with yarn
    $ yarn add @mui/material @emotion/react @emotion/styled
    
  • styled-component を使う場合

    // with npm
    npm install @mui/material @mui/styled-engine-sc styled-components
    
    // with yarn
    yarn add @mui/material @mui/styled-engine-sc styled-components
    

next/link, @mui/material/Link の統合

Next.js からは、 next/link、MUI からは @mui/material/Link というコンポーネントが提供されており、名前からお察しの通りどちらもカスタマイズされたリンク(a タグ)を生成します。

この2つには競合する部分もありますが、ある場面では next/link を、また他の場面では @mui/material/Link を用いた方が便利というケースがあります。(どういった場面でどちらを使うべきか、という理由はサンプルコードの後で説明します。)

なので、状況に応じてどちらかのコンポーネントを返してくれるように、独自の Link コンポーネントを作成します。

src/Link.js

ここで重要になってくるのは65行目から86行目の部分で、href に設定された URL が、internalexternal のどちらであるかを判定して、 return するコンポーネントを分けている、ということです。

URL が internal な場合は、Next.js が提供する、next/link を返します。

これは Link がアプリケーション内のページ遷移であるときは、以下のような、Next.js が提供する各種機能を使用することができるからです。

  • Client-Side Navigation
  • Code Splitting and Prefetching

これらの説明は本記事の趣旨から逸れますので省きますが、詳しくはこちらをみて頂くと理解することができると思います。

URL が external な場合は、next/link である理由はあまりないので、豊富なスタイリング用の Props が使用できる @mui/material/Link を使うようにします。

実際にリンクを作成する時は、ここで作成した、src/Link をインポートする形になります。

pages/Index.js

独自 Theme を作成する

こちらは作成せずとも、MUI の default Theme を利用できますが、多くの場合アプリケーション独自の Theme を設定していくことになると思いますので、本記事でも取り上げていきたいと思います。

今回は src 配下に Theme ファイルを配置していきます。例は簡単のために、Typographyfont-size だけを設定したものになります。

src/Theme.js

独自で定義した Theme は、ThemeProvider を使って、アプリケーションに適用させることが可能です。

具体的な実装方法としては、 _app.js にて、Component より外側のコンポーネントThemeProvider を設定してあげれば OK です。

pages/_app.js

app.js, document.js の修正

最後に、Next.js で MUI を使用するために、_app.js_document.js の設定方法についてお話ししていきます。

はじめに、これから行う設定が何のためのものかというのを説明します。

MUI は元々、サーバサイドレンダリングされるものという制約のもと開発されています。 なので Next.js をはじめとした、クライアントサイドでレンダリングする可能性があるフレームワークを使用した場合に不具合が発生してしまいます。

具体的にどのようなことが起きるかというと、いわゆる FOUC(Flash Of Unstyled Content)というもので、画面上に CSS の当たっていないページが一瞬表示されてしまいます。

これはクライアントサイドレンダリング時に、HTML だけ表示されて、後から CSS が注入されるような流れになるため発生します。

なので、大まかに以下のような手順を踏むことでこの問題を回避していきます。

  1. サーバサイドレンダリング時に一度、コンポーネントツリーのレンダリングを行う
  2. そこから CSS を抜き出す(<style> を抜き出す)
  3. 抜き出した CSS をクライアントに渡してあげる

この手順では以下のパッケージが必要となりますので、事前にインストールしていきます。

// with npm
$ npm install @emotion/server @emotion/cache

// with yarn
$ yarn add @emotion/server @emotion/cache

まず、_document.js から説明していきます。_document.js は Next.js においてあらゆるコンポーネントの初期化時に実行されるコンポーネントですが、その特徴として、サーバサードレンダリング時に、サーバサイドで実行されるという特徴があります。

要するに前述した、1, 2 の手順をここで実行していくわけです。

サンプルコードは以下になります。

pages/_document.js

30行目からの処理に注目していきます。

getInitalProps はサーバサイドレンダリング時に、ページにあらかじめサーバから取得した情報を埋め混むことができるメソッドです。

getInitialProps 内の60行目から66行目までの部分で、一度ページコンポーネントレンダリングを行なっていきます(前述した 1. の手順に相当)

以降の処理で、レンダリングされたコンポーネントから CSS<style>)を抜き出し、emotion/cache を使ってキャッシュに保存します。(前述した 2. の手順に相当)

次にクライアントサイドの処理について見ていきます。

_app.js_document.js 同様に、あらゆるコンポーネントの初期化時に実行されるコンポーネントですが、クライアントサイドレンダリング時には、 _document.js は実行されず、_app.js しか実行されないという特徴があります。

ここでは、_app.js でサーバサイドレンダリング時にキャッシュに保存した CSS を受け取るといった方法で、クライアントサイドレンダリング時に発生する FOUC を回避します。以下がサンプルコードです。

11行目でサーバサイドレンダリング時に保存した CSS を取得します。

このキャッシュに保存された CSS は17行目のように、<Component> コンポーネントの外側を <CacheProvider> で囲ってあげることで、各ページコンポーネントに渡されます。

ここでお話しした内容は、MUI の公式ドキュメントでも詳しく説明されていますので、お時間ある方は合わせて見て頂けると理解が深まるかと思います。

おわりに

今回紹介したサンプルコードですが、https://github.com/mui-org/material-ui/tree/master/examples/nextjs で MUI が公式に提供しているものです。今後変更がある可能性もありますので、サクッとコピペして使いたい方は、紹介したリンクから取得いていただくのが確実と思います。

明日の記事の担当は データテクノロジーグループの堀部さんです。お楽しみに。


株式会社エニグモ すべての求人一覧

hrmos.co