🎇

svgファイルをReactコンポーネントに変換する方法

2021/09/24に公開

やりたいこと

.svg.tsx の拡張子に変換したい

技術選定

  • React
  • TypeScript
  • Next.js
  • @svgr/cli

実現方法

SVGRというツールを用いることで簡単に実現できた

前提知識

SVGRとは何か?

svgファイル から Reactコンポーネント にコマンド1つで簡単に変換できる CLI ツール

SVGR の基本構成

svgファイル(.svg) → SVGR → React Component(.jsx | .tsx)

svg を jsx に変換する基本手順

1. 実行ファイルをinstallする

yarn add -D @svgr/cli

2. CLIコマンドを叩く

npx @svgr/cli -d src/components/icons public/icons

※ -d [出力先のディレクトリ名] [svgファイルが格納されているディレクトリ名]
  • ※1 Usage: npx @svgr/cli [-d out-dir] [--ignore-existing] [src-dir]
  • ※2 npx : ローカルインストールしたコマンドを実行するために使用される便利コマンド

この2つのステップでpublic/iconsにあるsvgファイルが
src/components/iconsにjsファイルとして出力される

実際の現場で運用する際はいちいち2のnpxコマンドを叩くのはとても面倒。

package.jsonのscripts配下にエイリアス登録しておくと使い回しやすいので
package.jsonのscripts配下に実行コマンドをエイリアス登録しておくと良い

"scripts": {
  "icons": "npx @svgr/cli -d src/components/icons public/icons"
}

これでyarn iconsと叩くだけでsvgファイルを
コマンド1つでコンポーネントファイル(js)に変換できる

ただし、この設定のままだと svgファイル を jsファイル にしか変換できない。
やりたいことは svg を tsx に変換したいだったので
SVGR の設定ファイルを作成して編集する必要がある。

svg を tsx に変換する手順

  • 設定ファイル(.svgrrc.js)を作成する
  • 設定ファイルにtypescript: trueと記載する
module.exports = {
  typescript: true
}

この設定ファイルをルートディレクトリ
作成した上でもう一度yarn iconsと叩く

そうすると、svgファイルからtsxファイルに変換できる。

svg から tsx に変換できた ↓
yarn icons.png

補足事項

上記、設定のままだと以下のように
default exportのコンポーネントに変換される

import * as React from 'react'

function SvgVectorRight(props: React.SVGProps<SVGSVGElement>) {
  return (
    <svg xmlns='http://www.w3.org/2000/svg' width={32} height={32} {...props}>
      <path d='M19.414 27.414l10-10a2 2 0 000-2.828l-10-10a2 2 0 10-2.828 2.828L23.172 14H4a2 2 0 100 4h19.172l-6.586 6.586c-.39.39-.586.902-.586 1.414s.195 1.024.586 1.414a2 2 0 002.828 0z' />
    </svg>
  )
}

export default SvgVectorRight

自分はdefault exportではなく名前付きexportのコンポーネントに変換したかった
これは、SVGR | template を編集することで、実現することができた

@svgr/cli version 5系の場合

// .svgrrc.js

module.exports = {
  typescript: true,
  template: ({ template }, opts, { imports, componentName, props, jsx }) => {
    const plugins = ['jsx']
    if (opts.typescript) {
      plugins.push('typescript')
    }
    const typeScriptTpl = template.smart({ plugins })
    return typeScriptTpl.ast`${imports}

    export const ${componentName} = (${props}): JSX.Element => {
        return ${jsx};
      }
    `
  },
}

上記、設定ファイルにした上で、もう一度yarn iconsを叩くと
以下のように名前付きexportのコンポーネントに変換された

import * as React from 'react'

export const SvgVectorRight = (props: React.SVGProps<SVGSVGElement>): JSX.Element => {
  return (
    <svg xmlns='http://www.w3.org/2000/svg' width={32} height={32} {...props}>
      <path d='M19.414 27.414l10-10a2 2 0 000-2.828l-10-10a2 2 0 10-2.828 2.828L23.172 14H4a2 2 0 100 4h19.172l-6.586 6.586c-.39.39-.586.902-.586 1.414s.195 1.024.586 1.414a2 2 0 002.828 0z' />
    </svg>
  )
}

@svgr/cli version 6系の場合

SVGR | template | Migrate to v6 に記載されている通りです。

version 6系を install した場合
templateの関数の引数の渡し方が変化しているので注意が必要です。

// .svgrrc.js

module.exports = {
  typescript: true,
  template: (variables, { tpl }) => {
    return tql`
    ${imports}

    export const ${variables.componentName} = (${variables.props}): JSX.Element => {
        return ${variables.jsx};
      }
    `
  },
}

これで自分は「やりたいこと」を実現できました!

最後に

ほとんどコピペで実現可能だと思うんで
Reactのアイコンの管理方法に悩んでいる人はぜひ活用してみてください!

参考文献

SVGR | Options
SVGR | template | Migrate to v6

Discussion