かもメモ

自分の落ちた落とし穴に何度も落ちる人のメモ帳

Vite React (TypeScript) vite-plugin-svgr で SVG をコンポーネントとして扱う

vite-plugin-svgr プラグインがアップデートされ設定などが変わっていたので改めてメモ

環境
  • vite-plugin-svgr 4.2.0
  • Vite 4.4.9
  • TypeScript 5.1.6

vite-plugin-svgr v3 → v4

結論は最下部にあります。お急ぎの方は途中スキップしてください!

Before

プラグインの設定

vite.config.ts

import svgr from 'vite-plugin-svgr';
export default {
  // ...
  plugins: [svgr()],
}
TypeScript に SVG を React Component として解釈させるための型ファイルを作成

@/types/svg.d.ts

declare module '*.svg' {
  import React = require('react');
  export const ReactComponent: React.FC<React.SVGProps<SVGSVGElement>>;
}

cf. reactjs - Module '"*.svg"' has no exported member 'ReactComponent' - Stack Overflow

App.tsx

import { ReactComponent as Logo } from './logo.svg';
return App: FC = () => {
  return <div><Logo /></div>;
};

After

svg は ReactComponent as を使わず .svg?react でインポートする

公式のドキュメントに import Logo from "./logo.svg?react"; のようにインポートするように書かれている

1. {ReactComponent as Component} を default import に修正する

App.tsx

import Logo from './logo.svg';
return App: FC = () => {
  return <div><Logo /></div>;
};

この状態だとフロントで Failed to execute 'createElement' on 'Document': The tag name provided ('/src/logo.svg') is not a valid name. のようなエラーが出る

2. .svg?react で import するようにする

どうやら SVG は .svg?react の形でインポートしなければプラグインが動作しないようになっているっぽい
tsconfig.json を変更して .svg?react を許容するようにする

{
  "compilerOptions": {
  },
  "include": ["src", "**/*.svg?react"],
}

App.tsx

- import Logo from './logo.svg';
+ import Logo from './logo.svg?react';
return App: FC = () => {
  return <div><Logo /></div>;
};
3. vite-env.d に <reference types="vite-plugin-svgr/client" /> を追加する

この状態だと import Logo from './logo.svg?react の箇所が Cannot find module '@/logo.svg?react' or its corresponding type declarations.ts (2307) という Type Error が発生してしまう。
公式ドキュメントにあるように vite-env.d に /// <reference types="vite-plugin-svgr/client" /> という一文を追加するとこのエラーは解消される

vite-env.d

/// <reference types="vite/client" />
/// <reference types="vite-plugin-svgr/client" />

これで vite-plugin-svgr v4 で SVG を React Component として扱えるようになりました!

.svg?react で import させるのがイケてない…

プラグインが .svg?react でのインポートを矯正しているがゆえに tsconfig を修正したり、SVG を使いたい時に都度 ?react を追加するのが面倒でイケてないので ?react 無しでプラグインが動作するようにしたい

プラグインの設定で ?react 無しにできた!

プラグインのコードを見ていると下記のようにプラグインの初期化部分で拡張子がオプションになっていました

export default function vitePluginSvgr({
  svgrOptions,
  esbuildOptions,
  include = "**/*.svg?react",
  exclude,
}: VitePluginSvgrOptions = {}): Plugin {

cf. vite-plugin-svgr/src/index.ts#L14-L19

vite.config.ts でプラグインを実行させる箇所で include オプションを使って ?react 無しの svg を対象にしてしまえば ?react 無しで動作させることができました

vite.config.ts

import svgr from 'vite-plugin-svgr';
export default {
  // ...
- plugins: [svgr()],
+ plugins: [svgr({ include: "**/*.svg" })],
}

.svg?react を使用しないので tsconfig で *.svg?react を有効化する必要がありません

tsconfig.json

{
  "compilerOptions": {
  },
- "include": ["src", "**/*.svg?react"],
+ "include": ["src"],
}

同様に /// <reference types="vite-plugin-svgr/client" /> の設定も .svg?react という拡張子の型エラーに対応するものだったの不要

vite-env.d

/// <reference types="vite/client" />
- /// <reference types="vite-plugin-svgr/client" />
+

これで import Logo from './logo.svg' のような形で SVG を React Componentn として扱えるようになりました!
?react のオプション要らなくない?って思ってしまった…

結論 vite-plugin-svgr v4 はデフォルトでは .svg?react 拡張子を対象にしているが、オプションで .svg を対象に変更すると簡単!

vite.config.ts

import svgr from 'vite-plugin-svgr';
export default {
  // ...
  plugins: [svgr({ include: "**/*.svg" })],
}

App.tsx

import Logo from './logo.svg';
return App: FC = () => {
  return <div><Logo /></div>;
};

フロントエンド スナック感覚で仕様が変わるからアップデートでハマりがち…
おわり!


[参考]