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

## やりたいこと

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

## 技術選定

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

## 実現方法

[SVGR](https://react-svgr.com/)というツールを用いることで簡単に実現できた

## 前提知識

### SVGRとは何か？

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

### SVGR の基本構成

```
svgファイル（.svg） → SVGR → React Component（.jsx | .tsx）
```

---

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


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

```bash
yarn add -D @svgr/cli
```

### 2. CLIコマンドを叩く

```bash
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配下に実行コマンドをエイリアス登録しておくと良い

```json
"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`と記載する

```js
module.exports = {
  typescript: true
}
```

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

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

| svg から tsx に変換できた ↓ |
| --- |
| ![yarn icons.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/508390/e4682e92-0570-2f82-5ded-8bb90dfccb90.png) |

## 補足事項

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

```tsx
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](https://react-svgr.com/docs/options/#template) を編集することで、実現することができた

### @svgr/cli version 5系の場合

```js
// .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`のコンポーネントに変換された

```tsx
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](https://react-svgr.com/docs/migrate/#template) に記載されている通りです。

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

```js
// .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](https://react-svgr.com/docs/options/)
[SVGR | template | Migrate to v6](https://react-svgr.com/docs/migrate/#template)