MUI v5 で追加されたスタイリング用ユーティリティ styled() を触ってみた


こんにちは、Gaji-Labo アシスタントエンジニアの石垣です。

今回は、MUI v5 で追加された新しいスタイリング用ユーティリティの styled() を触ってみたので、その感触と withStyles からの書き換え方法をまとめたいと思います。

styled() とは?

styled() とは MUI v5 で追加されたコンポーネントのスタイリング用ユーティリティです。

styled-component と同様のやり方で MUI のコンポーネントのスタイルをカスタマイズすることが出来ます。

styled() でスタイル上書きされた Slider コンポーネント
import * as React from 'react';
import Slider from '@mui/material/Slider';
import { alpha, styled } from '@mui/material/styles';

const SuccessSlider = styled(Slider)(({ theme }) => ({
  width: 300,
  color: theme.palette.success.main,
  '& .MuiSlider-thumb': {
    '&:hover, &.Mui-focusVisible': {
      boxShadow: `0px 0px 0px 8px ${alpha(theme.palette.success.main, 0.16)}`,
    },
    '&.Mui-active': {
      boxShadow: `0px 0px 0px 14px ${alpha(theme.palette.success.main, 0.16)}`,
    },
  },
}));

export default function StyledCustomization() {
  return <SuccessSlider defaultValue={30} />;
}

また、styled() ユーティリティは MUI のコンポーネントとは分離しているため、これ単体で MUI と関係のないコンポーネントにも使用することが出来ます。

div 要素等にもスタイルを当てることが出来る
import * as React from 'react';
import { styled } from '@mui/system';

const MyComponent = styled('div')({
  color: 'darkslategray',
  backgroundColor: 'aliceblue',
  padding: 8,
  borderRadius: 4,
});

export default function BasicUsage() {
  return <MyComponent>Styled div</MyComponent>;
}

MUI v5 で styled() が追加された背景として、

  1. React コミュニティでは styled-component が最も人気のある CSS-in-JS API として定着していたのに対し、 MUI v4 では makeStyles() 等の独自のスタイリングソリューションを採用していたため追加の学習コストが掛かっていた
  2. v4 での動的スタイル生成は速度に問題があったため、再実装する必要があった
  3. styled-component に移行することでカスタムスタイリングソリューションの維持をする必要が無くなる

という3つの理由があったようです。(意訳、引用: Introducing MUI Core v5.0 – MUI)

特に2つ目の理由は実際に使っていて速度問題を感じていたため、この点が styled に移行することで解消されるのは嬉しいところです。

withStyles から styled() に置き換える

コンポーネントのスタイルを上書きしたい際、 withStyles というユーティリティで上書きをしていました。

実際に withStyles から styled() に置き換えるとどうなるかを見てみたいと思います。

withStyles で実装した例がこちらです。

CodeSandbox

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

const StyleOutlinedInput = withStyles({
  root: {
    "&$focused": {
      "& $notchedOutline": {
        borderColor: "red",
        borderWidth: "2px"
      }
    }
  },
  notchedOutline: {
    borderColor: "grey"
  },
  multiline: {
    padding: "8px 11px 9px"
  },
  focused: {}
})(OutlinedInput);

export function TextArea(): JSX.Element {
  return <StyleOutlinedInput multiline />;
}
MUI デフォルトの OutlinedInput から focus 時に border 色を赤に変更している

MUI デフォルトの OutlinedInput からスタイルを以下のように上書きしています。

  • border 色を変える
  • multiline (TextArea 化する props) を追加し、その際に padding を変える
  • focus 時に border 色と太さを変える

こちらを styled() で書き換えてみるとこうなります。

CodeSandbox

import { OutlinedInput } from "@material-ui/core/";
import { styled } from "@mui/system";

const StyledOutlinedInput = styled(OutlinedInput)(() => ({
  "&.MuiOutlinedInput-multiline": {
    padding: "8px 11px 9px"
  },
  "& .MuiOutlinedInput-notchedOutline": {
    borderColor: "grey"
  },
  "&.Mui-focused": {
    "& .MuiOutlinedInput-notchedOutline": {
      borderColor: "red",
      borderWidth: "2px"
    }
  }
}));

export function TextArea(): JSX.Element {
  return (
    <StyledOutlinedInput multiline />
  );
}

具体的な相違点は以下の通りです。

  • 自動生成された class 名がコンポーネントの最上位の要素に当たるようになった
  • MUI の class 名を直接指定してスタイルを当てるようになった
  • focused などの class 指定を空で用意する必要が無くなった

個人的な感触として、 withStyles や makeStyles の記法はかなり独特で、特に focused や error 時など MUI コンポーネントが状態を持っている際のスタイルの書き方に慣れが必要でした。(参考: Material-UI で focused / error のスタイルを上書きするのに手間取った)

それが styled-component に近くなったことでかなりスタイルが書きやすくなったと感じました。

公式リファレンスもほぼ styled で統一されており、 withStyles から移行することを推奨しているので今後は全て styled のスタイリングで統一する必要がありそうです。

まとめ

今回はMUI v5 で追加された新しいスタイル記法の styled() を触ってみました。

Material-UI v4 を使っている方で v5 への移行を考えている方の参考になれば幸いです。

Gaji-Laboでは、React経験が豊富なフロントエンドエンジニアを募集しています

弊社ではReactの知見で事業作りに貢献したいフロントエンドエンジニアを募集しています。大きな制作会社や事業会社とはひと味もふた味も違うGaji-Laboを味わいに来ませんか?

もちろん、一緒にお仕事をしてくださるパートナーさんも随時募集中です。まずはお気軽に声をかけてください。お仕事お問い合わせや採用への応募、共に大歓迎です!

求人応募してみる!

タグ


投稿者 Ishigaki Shotaro

アシスタントエンジニアとしてHTML/CSS/JavaScriptの実装やRailsの組み込み、スタイルガイドの構築などを担当しています。 業務の中でさまざまな学びを吸収しながら、文書構造やアクセシビリティに目を向けたマークアップの学習やJavaScriptの学習などを行っています。チームに貢献できるエンジニアとなるために日々奮闘中です。