Material-UI v4 から MUI v5 に移行する(実作業編)


こんにちは、Gaji-Labo フロントエンドエンジニアの石垣です。

今回は大規模なプロジェクトで、UIコンポーネントライブラリの MUI のバージョンを上げた話について書いていきます。

当ブログで今まで何度か Material-UI v4 から MUI v5 に移行する話題について書いてきましたが、これまで具体的に移行するために踏んだフローについては書いていませんでした。

自分の携わっているプロジェクトで長らく Material-UI v4 から MUI v5 への移行作業を行っていたのですが、当初想定よりもかなりのコストがかかってしまっていたのが一因です。

今回ひとまずの形で移行作業を完了することができたので、どこにどれくらいのコストが掛かったかも含めてその際に踏んだフローについてまとめてみたいと思います。

プロジェクトの概要

今回移行したプロジェクトは React + TypeScript + Next.js (Pages Router) という技術構成です。そこにUIコンポーネントライブラリとして Material-UI v4 を使用していました。

数年前に開始したプロジェクトのため、当時は v4 が最新バージョンでした。そのまま v4 をベースに数百単位のコンポーネントとそれを組み込んだ画面を実装していたので移行作業は当初からかなりのコストがかかることが見込まれました。

また、v4 では makeStyles や withStyles というスタイルカスタマイズ方法がビルトインで用意されていましたが、v5 では機能としては残っていますが deprecated となっています。
そのため本来であればこれらも v5 のスタイルシステムに置き換える必要がありましたが、今回は最小限の変更で移行することを目指したため、そのまま v4 のスタイルカスタマイズ方法を使い続けることにしました。

MUI v5 への移行手順

1. MUI v5 のインストール

まずプロジェクトに MUI v5 を導入します。以前の記事「MUI v4 から v5 にアップグレードする際に重点的に確認したい点」でも紹介した通り、MUI v5 は Material-UI v4 と別のパッケージ @mui として提供されています。

この時、移行作業を開始した当初は v4 と v5 はそれぞれ同居でき、画面ごとに使い分けられる(=部分的な移行ができる)と考えていたのですが、実際には同居ができませんでした。

理由としては v4 と v5 でそれぞれ生成する CSS があり、v4 の makeStyles が v5 のデフォルトスタイルに詳細度の関係で上書きされてしまうためです。

そのため移行作業は main ブランチと同期を取りつつ feature ブランチで進め、全移行が完了した状態でマージする形となりました。

2. ページごとの移行

プロジェクトの規模が大きいため、pages 配下のディレクトリ単位でパラレルに移行を進めました。

これによりディレクトリ内の画面単位で確認ができ、また進捗確認や崩れたときの原因特定も容易でした。

具体的な移行作業内容は以下の通り、コンポーネントの import 元を @material-ui/core から @mui/material 等に変更するもので作業自体は単純でした。

import { IconButton, withStyles, makeStyles } from "@material-ui/core";

import { IconButton } from "@mui/material";
import { withStyles, makeStyles } from "@mui/styles";

この際、前述した進め方に則って公式の codemods ツールは使用せず、Next.js 上の構造に沿ってディレクトリ単位で置換を行い、手動で確認しました。

3. スタイルの崩れを修正

前述の通り、Material-UI v4 の makeStyles でスタイリングすると v5 のデフォルトスタイルの方が詳細度が高いためスタイルが崩れることがあります。

本来スタイルシステムも移行すれば解決する問題なのですが、今回は以下のように詳細度を上げて対応しました。

const useStyles = makeStyles({
  DownloadButton: {
    paddingLeft: 16,
    paddingRight: 8,
  },
});

const useStyles = makeStyles({
  DownloadButton: {
    ":root &": {
      paddingLeft: 16,
      paddingRight: 8,
    },
  },
});

:root & で対応できることがわかるまで試行錯誤を繰り返したので、この時点でかなりの時間が費やされました……。

4. 全画面の確認

ディレクトリごとの作業が完了したら改めて全画面での確認を行いました。

汎用コンポーネントの場合はあるディレクトリで作業が完了しても他のディレクトリでスタイルが崩れることがあるため、全画面での確認が必要でした。

こちらも今回のプロジェクトでは手動で確認したため、VRTツールがあればより効率的に確認できるかと思います。

5. テストの修正

移行後の画面でのテストも修正します。

今回のプロジェクトでは Cypress を使って E2E テストを行っており、一部のテストはセレクタが v4 が付与する className に依存していたため、 v5 で className が変更されている場合に修正が必要でした。

6. スタイル差分の許容範囲の判断

確認作業の中で明らかに崩れている箇所やデザイン意図と異なる箇所を優先的に修正しました。

一方で MUI 自体のデザイン変更に起因するスタイル差分など判断が難しいものもあったため、デザイナーやプロダクトマネージャーに確認し許容範囲の合意を取りました。

7. @material-ui パッケージを削除しリリース

全画面移行が完了した後、@material-ui/core などのパッケージを削除し、 これで v5 に完全アップデートされました。

ただし前述の通り今回の対応は makeStyles や withStyles の対応は含めていないため、別途 v5 のスタイルシステムへ移行する必要があります。

また移行に伴い追加した :root & も引き続き makeStyles より v5 のデフォルトスタイルの方が詳細度が勝つため、スタイルシステム移行が完了するまでは残しておく必要があります。

まとめ

Material-UI v4 は既にメンテナンスが行われておらず、セキュリティリスクがあるため v5 への移行は必須です。

しかし v4 から v5 への移行は破壊的変更がとても多く、大規模プロジェクトの場合は多大なコストがかかります。

移行に伴うコストは主に以下の3点です。

  1. 移行作業のコスト
  2. 見た目確認のコスト
  3. 移行後のスタイルを新基準とする判断のコスト

移行作業のコストについては今回のようにディレクトリごとに段階的に進めるのが現実的かと思います。

確認作業のコストについては今回手動で進めましたが、 Chromatic や Playwright などのVRTツールを使うことで効率化できます。なるべくプロジェクトの初期に導入しておくとこういった大規模なスタイルに関する作業がスムーズに進むかと思います。

判断のコストについては事前にデザイナーやプロジェクトマネージャーとスケジュールや作業規模を共有しておくとこちらもコミュニケーションがスムーズになります。

技術的な観点から移行作業についてまとめましたが、また別の機会にコミュニケーション周りやスケジュール周りの観点からもまとめてみたいと思います。

この記事が Material-UI v4 から v5 への移行作業に携わる方の参考になれば幸いです。

また、おそらく MUI に限らず他のライブラリのバージョンアップにも共通する部分があるかと思いますので、他のライブラリのバージョンアップの際に今回の知見を活かしていきたいです。

Gaji-Labo は Next.js, React, TypeScript 開発の実績と知見があります

フロントエンド開発の専門家である私たちが御社の開発チームに入ることで、バックエンドも含めた全体の開発効率が上がります。

「既存のサイトを Next.js に移行したい」
「人手が足りず信頼できるエンジニアを探している」
「自分たちで手を付けてみたがいまいち上手くいかない」

フロントエンド開発に関わるお困りごとがあれば、まずは一度お気軽に Gaji-Labo にご相談ください。

オンラインでのヒアリングとフルリモートでのプロセス支援にも対応しています。

Next.js, React, TypeScript の相談をする!

タグ


投稿者 Ishigaki Shotaro

未経験から Gaji-Labo に入社。現在は React/TypeScript/Next.js の案件で MUI を使ったコンポーネント組み込みを担当。プロジェクトチームのリードとして共に組み込み作業をしているメンバーの進行管理も行っています。休日はだいたい家で音楽を聴いており、たまにライブに出かけています。