Material-UI で独自のアイコンを表示するコンポーネントを作成する


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

今回は、React 用 UI フレームワークである Material-UI で独自の SVG アイコンを出し分けできるようにするアイコンコンポーネントを作成する方法についてまとめたいと思います。

概要

Material-UI には Icon というコンポーネントが用意されており、Material Design や Font Awesome のアイコンが表示できるようになっています。

Icon コンポーネントは SvgIcon というコンポーネントを元にしており、SvgIcon コンポーネントを使用することで Icon コンポーネントと同様に独自のアイコンを表示することが出来ます。

今回はこの SvgIcon を使用して、独自のアイコンを表示するコンポーネントを作成する方法についてまとめます。

SvgIcon で独自のアイコンを表示する

SvgIcon で独自のアイコンを表示するには、 SvgIcon コンポーネントで svg 要素をラップします。

import React from "react";
import SvgIcon, { SvgIconProps } from "@material-ui/core/SvgIcon";

type Props = React.HTMLProps<SvgIconProps>;

export const Icon: React.FC<Props> = (props) => {
  return (
    <SvgIcon>
      <svg width="100" height="100" fill="none" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="50" r="40" stroke="#000" stroke-width="3" fill="red"/></svg>
    </SvgIcon>
  );
};

SvgIcon には、他の Material-UI コンポーネントと同様、 classes, component, fontSize, viewBox などの props がデフォルトで用意されている他、aria-label aria-hidden など、追加でアイコンに必要な属性を props として指定することも可能です。

SvgIcon の props については公式のAPIドキュメントをご参照ください。

import React from "react";
import SvgIcon, { SvgIconProps } from "@material-ui/core/SvgIcon";

interface Props extends React.HTMLProps<SvgIconProps> {
  ariaHidden?: boolean;
  ariaLabel?: string;
  className?: string;
}

export const Icon: React.FC<Props> = (props) => {
  const {
    ariaHidden,
    ariaLabel,
    className,
  } = props;

  return (
    <SvgIcon
      aria-hidden={ariaHidden}
      aria-label={ariaLabel}
      className={className}
      component="span"
      viewBox=""
    >
      <svg width="100" height="100" fill="none" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="50" r="40" stroke="#000" stroke-width="3" fill="red"/></svg>
    </SvgIcon>
  );
};

デフォルトでは svg 要素としてレンダリングされますが、テキストと同時に扱うことを考慮して component props で span 要素に変更しています。

それに伴い viewBox を空にしています。デフォルトでは '0 0 24 24' が指定されます。

スタイルを上書きしたい場合は、以前「React + Material-UIを使用してMaterial Designのコンポーネントを作成する」でご紹介したやり方と同様、 createMuiTheme で上書きすることができます。

...
import { createMuiTheme, ThemeProvider } from "@material-ui/core/styles";
import SvgIcon, { SvgIconProps } from "@material-ui/core/SvgIcon";
...
  const theme = createMuiTheme({
    overrides: {
      MuiSvgIcon: {
        root: {
          color: "inherit",
          fontSize: "inherit",
          height: "auto",
          lineHeight: 0,
          width: "auto",
          "& > svg": {
            height: "1em",
            width: "1em",
          },
        },
      },
    },
  });

  return (
    <ThemeProvider theme={theme}>
      <SvgIcon
        aria-hidden={ariaHidden}
        aria-label={ariaLabel}
        className={className}
        component="span"
        viewBox=""
      >
        <svg width="100" height="100" fill="none" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="50" r="40" stroke="#000" stroke-width="3" fill="red"/></svg>
      </SvgIcon>
    </ThemeProvider>
  );
};
...
<Icon />
<br />
Circle
...
svg 要素を子にした Icon コンポーネントの表示

複数のアイコンを出し分けするコンポーネントを作成する

先程作成したコンポーネントでは1つのアイコンしか表示できないため、出し分けできるように変更します。

import React from "react";
import SvgIcon, { SvgIconProps } from "@material-ui/core/SvgIcon";

import Circle from "../assets/icons/Circle.svg";
import Line from "../assets/icons/Line.svg";

const iconList: { [name: string]: JSX.Element } = {
  circle: <Circle />,
  line: <Line />,
};

interface Props extends React.HTMLProps<SvgIconProps> {
  iconName: "circle" | "line";
}

export const Icon: React.FC<Props> = (props) => {
  const { iconName } = props;

  return <SvgIcon>{iconList[iconName]}</SvgIcon>;
};

iconName props を追加し、渡ってきた iconName で circle が渡ってきた時は <Circle /> コンポーネントを表示させるなどしてそれぞれの svg を表示させています。

svg を React コンポーネントとして扱うため、react-svg-loader を導入する必要があります。

<Icon iconName="circle" />
<br />
Circle
...
<Icon iconName="line" />
<br />
Line
iconName を渡した Icon コンポーネントの表示

これで複数のアイコンを出し分けするコンポーネントを作成することが出来ました。

まとめ

今回は Material-UI で独自の SVG アイコンを表示するコンポーネントの作成方法についてまとめました。

Material-UI を使用している方、興味を持っている方の参考にしていただけたらと思います。

Gaji-Laboでは、JavaScriptフレームワーク経験が豊富なパートナーさんを募集しています

Gaji-Laboでは、開発チームの一員としてプロジェクトに一緒に取り組んでくれる業務委託のパートナーさんを募集しています。

現在は特にJavaScriptフレームワーク実践と業務経験が豊富なWebフロントエンドエンジニアを必要としています。React + TypeScript、Vue.js、Next.js、Nuxt.js など、あなたの得意なフレームワークを教えて下さい!

パートナー契約へのお問い合わせもお仕事へのお問い合わせも、どちらもいつでも大歓迎です。まずはオンラインでのリモート面談からはじめましょう。ぜひお気軽にお問い合わせください!

お問い合わせしてみる!

投稿者 Ishigaki Shotaro

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