Next.jsでSVGを利用する方法(babel-plugin-inline-react-svgの使い方)

JavaScript

babel-plugin-inline-react-svgというプラグインを利用することでSVGをコンポーネントのように扱えます。
今回はbabel-plugin-inline-react-svgをNext.jsのアプリケーションで利用する方法について紹介します。

検証環境はnext 12.1.4react 18.0.0です。

セットアップ手順

パッケージのインストール

$ yarn add -D babel-plugin-inline-react-svg

.babelの作成と設定追加

babel-plugin-inline-react-svgのREADMEを参考に、.babelrcの設定をします。
またNext.jsの公式ドキュメントのCustomizing Babel Configにも書いてある通り、next/babelのプリセットもあわせて追加します。

.babelrc

{
  "presets": [ "next/babel" ],
  "plugins": [ "inline-react-svg" ]
}

下準備: SVGのダウンロード

今回はGoogle FontsにあるSVGをサンプルとして利用します。

利用方法

SVGの呼び出し方法

SVGを呼び出すサンプルコードは以下の通りです。
コードをみて分かる通り、babel-plugin-inline-react-svgを利用すると通常のコンポーネントのようにSVGもPropsでプロパティを制御できたりCSSを適用できたりします。

src/components/Example.tsx

import { FC } from "react";
import HomeIcon from "../../public/images/home.svg";
import FilledHomeIcon from "../../public/images/filledHome.svg";
import styles from "./Example.module.css";

export const Example: FC = () => {
  return (
    <>
      <HomeIcon width={24} height={24} />
      <HomeIcon className={styles.icon} />
      <HomeIcon height={48} width={48} fill={"red"} />
      <HomeIcon
        height={48}
        width={48}
        fill={"red"}
        stroke={"red"}
        strokeWidth={2}
      />
      <FilledHomeIcon height={48} width={48} fill={"red"} />
    </>
  );
};

src/components/Example.module.css

.icon {
  width: 24px;
  height: 24px;
}

PropsでSVGを指定する方法

PropsでSVGを指定するサンプルコードは以下の通りです。

src/components/Example.tsx

import { FC } from "react";
import { Button } from "./shared/Button";
import FilledHomeIcon from "../../public/images/filledHome.svg";
import FilledSettingsIcon from "../../public/images/filledSettings.svg";
import styles from "./Example.module.css";

export const Example: FC = () => {
  return (
    <>
      <Button label={"ホーム"} Icon={FilledHomeIcon} />
      <br />
      <Button label={"設定"} Icon={FilledSettingsIcon} />
    </>
  );
};

src/components/shared/Button.tsx

import React, { FC } from "react";
import styles from "./Button.module.css";

type Props = {
  label: string;
  Icon?: FC<{width: number, height: number, className: string}>
};

export const Button: FC<Props> = ({
  label,
  Icon,
}) => {
  return (
    <button
      className={styles.button}
    >
      {Icon ? <Icon width={20} height={20} className={styles.icon} /> : null }
      {label}
    </button>
  );
};

src/components/shared/Button.csss

.button {
  font-size: 15px;
  height: 50px;
  min-width: 100px; /* labelの文字列がボタンからはみ出さないようにするため、横幅を確保しつつ可変長にする */
  padding: 0 25px;
  color: white;
  font-weight: bold;
  border-radius: 10px;
  cursor: pointer;
  display: flex;
  justify-content: center;
  align-items: center;
  background: darkblue;
}

.icon {
  fill: #fff;
  margin-right: 5px;
}

覚えておくと良いTips

SVGのサイズがうまく変更できない場合

widthheightがSVGファイルの<svg>タグ内に記述されていると、コンポーネントからのサイズ指定がうまく機能しない場合があります。

サイズ指定が機能しない具体例は以下の通りです。

src/components/Example.tsx

import { FC } from "react";
import HomeIcon from "../../public/images/home.svg"

export const Example: FC = () => <HomeIcon width={24} height={24} />;

呼び出したSVGの詳細は以下の通りです。

home.svg

<svg xmlns="http://www.w3.org/2000/svg" height="48" width="48"><path d="M11 39h7.5V26.5h11V39H37V19.5L24 9.75 11 19.5Zm0 3q-1.25 0-2.125-.875T8 39V19.5q0-.7.325-1.35.325-.65.875-1.05l13-9.75q.4-.3.85-.45.45-.15.95-.15.5 0 .95.15.45.15.85.45l13 9.75q.55.4.875 1.05.325.65.325 1.35V39q0 1.25-.875 2.125T37 42H26.5V29.5h-5V42Zm13-17.65Z"/></svg>

結果は以下の通りです。

サイズ指定がうまくいかない場合は<svg>タグ内のwidthheightの定義を削除し、代わりにviewBoxを追加します。
たとえば以下のように修正します。

- <svg xmlns="http://www.w3.org/2000/svg" height="48" width="48">
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48">

上記のようにするとサイズ指定がうまくいきます。

相対パスでSVGファイルを読み込む方法

babel-plugin-module-resolverを追加すると相対パスでSVGファイルが利用できます。

$ yarn add -D babel-plugin-module-resolver

当該プラグインを利用するとパスのrootやaliasの設定ができます。今回はrootの設定のみ行います。

.babelrc

{
  "presets": [ "next/babel" ],
  "plugins": [
+   ["module-resolver", { "root": ["."] }],
    "inline-react-svg"
  ]
}

上記の設定完了後、SVGファイルは以下のように読み込めます。

- import HomeIcon from "../../public/images/home.svg"
+ import HomeIcon from "public/images/home.svg"

さいごに

Twitter(@nishina555)やってます。フォローしてもらえるとうれしいです!

参考資料