NRIネットコム Blog

NRIネットコム社員が様々な視点で、日々の気づきやナレッジを発信するメディアです

MUI+Emotion(+ Next.js)でコンポーネントをスタイリングする

本記事は  Reactウィーク  4日目の記事です。
📍  3日目  ▶▶ 本記事 ▶▶  5日目  📱

はじめに

はじめまして。インドア加速中で運動不足気味な、新卒入社4年目の西元です。
普段はフロントエンドエンジニアとして、業務アプリの開発・運用に携わっています。
今日はReactウィークということで、Reactプロジェクト向けのコンポーネントライブラリの開発経験をもとに、MUIのコンポーネントをEmotionでスタイリングする方法についてまとめます。
Next.jsの設定方法についても簡単に触れていますので、既存プロジェクトへの導入の際にも参考になればです。

(簡単に)MUI/Emotionとは

MUI

MUI(Material-UI)はReactコンポーネントライブラリです。
GoogleのMaterial Design(マテリアルデザイン)に基づいてデザインされたUIコンポーネントを提供しています。

Emotion

EmotionはJavaScriptでCSSを記述するためのライブラリです。
ReactをはじめとするJavaScript (TypeScript)ライブラリで、スタイリングに使用することができます。

開発環境の準備

Next.jsのプロジェクト作成

今回はNext.jsのプロジェクトにMUIとEmotionを導入して、コンポーネントをスタイリングしていきます。
まずは、Next.jsのプロジェクトを作成します。

インストール

以下のコマンドを実行して、Next.jsをインストールします。TypeScriptを使用したいので、オプションで指定しています。

npx create-next-app@latest --typescript

コマンドを実行すると設定について質問されるので、必要に応じて回答してください。
今回は以下のように設定しています。 これでNext.jsのプロジェクトが作成されました。

MUI/Emotionのインストール

インストール

続いて、以下のコマンドでMUIとEmotionをインストールします。

npm install @mui/material @emotion/react @emotion/styled
Next.jsの設定

Next.js(TypeScript)のプロジェクトでEmotionを使用するために、tsconfig.jsonに設定を追加します。

{
  "compilerOptions": {
    "jsxImportSource": "@emotion/react", // JSX Pragmaを設定
    "types": ["@emotion/react/types/css-prop"], // Emotionの型定義ファイルを設定
  }
}

これで設定は完了です。

オリジナルアコーディオンコンポーネントを作成する

今回は、MUIのAccordionを元に、オリジナルのアコーディオンコンポーネントを作成していきます。

1.自作コンポーネントを定義する

まずは、MUIのコンポーネントをラップして、自作コンポーネント(以降、CustomAccordion)を作成します。
MUIのアコーディオンは本来、3つのコンポーネント(Accordion/AccordionSummary/AccordionDetails)で構成されていますが、CustomAccordionではこれらを1つのコンポーネントにまとめます。
さらに、AccordionSummary、AccordionDetailsに設定する内容は外から渡せるように、propsを設定します。

CustomAccordion.tsx

import { Accordion, AccordionDetails, AccordionSummary } from "@mui/material";
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
import { ReactNode } from "react";

export type CustomAccordionProps = {
  summaryContent: ReactNode;
  detailContent: ReactNode;
};

export const CustomAccordion = ({summaryContent, detailContent}: CustomAccordionProps) => (
  <Accordion>
    <AccordionSummary expandIcon={<ExpandMoreIcon />}>
      {summaryContent}
    </AccordionSummary>
    <AccordionDetails>{detailContent}</AccordionDetails>
  </Accordion>
);

コンポーネントを呼び出す側のファイル

<CustomAccordion summaryContent={'Accordion 1'} detailContent={'アコーディオン1のコンテンツです。'} />
<CustomAccordion summaryContent={'Accordion 2'} detailContent={'アコーディオン2のコンテンツです。'} />

CustomAccordion(MUIのデフォルトスタイル)

2.MUIのCSSクラスを確認する

次に、MUIのコンポーネントをスタイリングするための準備を行います。
MUIには各コンポーネントの機能に関する情報がAPIリファレンスドキュメントにまとめられており、使用方法や設定可能なPropsについて記載されています。
例えば、Accordionに関しては以下のページを参照してください。 mui.com デフォルトのCSSを上書きする場合は、CSS classesの項を参照し、スタイリングしたい箇所に適用されているCSSクラスを確認します。

3.EmotionでMUIのデフォルトCSSを上書きする

いよいよ、MUIのコンポーネントのスタイルを上書きしてスタイリングしていきます。

Emotionのスタイル記法

props経由でスタイルを渡す場合は、css propsを使用します。スタイル記法は、ストリングスタイルとオブジェクトスタイルの2種類があります。
TypeScriptのプロジェクトの場合、Emotionがオブジェクトスタイルを推奨しています。
詳しくは以下を参照してください。 emotion.sh

自作コンポーネントをスタイリングする

今回は、#20B2AAをベースカラーにしたスタイルを適用し、最小幅を指定できるようにpropsを追加します。
スタイルの上書きについては、MUIのCSSクラスを指定し、スタイルを設定していくだけで問題ありません。
ただ、CSSクラスの指定だけではスタイルが上書きできない場合は、親セレクタを参照するなどしてCSSの詳細度を調整してください。

また、cssを関数として定義し、propsを引数として動的な値でスタイルを変更することもできます。
今回はprops minWidthに設定された値をminWidthプロパティに設定することで、動的に最小幅を指定できるようにしています。

CustomAccordion.tsx

import { Accordion, AccordionDetails, AccordionSummary } from "@mui/material";
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
import { ReactNode } from "react";
import { css } from '@emotion/react'

export type CustomAccordionProps = {
  summaryContent: ReactNode;
  detailContent: ReactNode;
  minWidth?: string, // 新しく追加したprops
};

export const CustomAccordion = ({ summaryContent, detailContent, minWidth = '500px' }: CustomAccordionProps) => (
  <Accordion css={styles.accordion(minWidth)} > // minWidthに設定された値を引数としてセット
    <AccordionSummary expandIcon={<ExpandMoreIcon />} css={styles.summary}>
      {summaryContent}
    </AccordionSummary>
    <AccordionDetails css={styles.detail}>{detailContent}</AccordionDetails>
  </Accordion>
);

const styles = {
  accordion: (minWidth: CustomAccordionProps['minWidth']) => css({
    minWidth: minWidth, // propsから渡ってきた値をcssの値として適用
  }),
  summary: css({
    '&.MuiAccordionSummary-root ': { // 親セレクタを参照
      backgroundColor: '#20B2AA',
      borderTop: 'solid 2px #20B2AA',
    },
    '.MuiAccordionSummary-content': {
      color: '#FFF',
      fontWeight: 'bold'
    },
    '.MuiAccordionSummary-expandIconWrapper': {
      color: '#FFF',
    }
  }),
  detail: css({
    '&.MuiAccordionDetails-root' : {
      backgroundColor: `rgba(32, 178, 170, 0.1)`,
      border: 'solid 2px #20B2AA',
      padding: '32px'
    }
  })
}

コンポーネントを呼び出す側のファイル

<CustomAccordion summaryContent={'Accordion 1'} detailContent={'アコーディオン1のコンテンツです。'} minWidth={'700px'} />
<CustomAccordion summaryContent={'Accordion 2'} detailContent={'アコーディオン2のコンテンツです。'} minWidth={'700px'}/>

CustomAccordion(Emotionを使ってスタイリング)

これで、MUIのコンポーネントをEmotionでスタイリングしたCustomAccordionの完成です。

おわりに

今回はMUIのコンポーネントをEmotionでスタイリングする方法をご紹介しました。
MUIとEmotionを合わせることで、カスタマイズ性が高まり、柔軟なコンポーネント開発が可能になります。
ぜひ、MUIとEmotionを使って、自分好みのReactコンポーネントを作成してみてください。

執筆者:西元遥香 フロントエンドエンジニア