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コンポーネントを作成してみてください。

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