前提
- React.js, webpack
- ReactでSVGファイルをimgとして読むとスタイルをCSSで変更できない(サイズはできるが色はできない)のでinlineで読む必要がある
- 複数のサイズや色のために複数のSVGファイルを用意したくない
- SVGファイルを変換したReact Componentのファイルも作りたくない
概要
- inline SVGとして埋め込む方法3つ
- CSSでスタイルを上書きする方法
を紹介
利点としてはSVGをReact Component化せずそのまま使うことで、(デザイナーの用意した)素材を極力そのままにし、ファイル数も増やさずに色を変えることができる
SVGの埋め込み方
1. raw-loader + dangerouslySetInnerHTML の場合
webpack.config.js
test:/\.svg$/i,loader:'raw-loader',
jsx
importHogefrom'... .svg'...<divdangerouslySetInnerHTML={{__html:Hoge}}/>
typescriptならパスを通したglobal型置き場に次のように書く
global.d.ts
declaremodule'*.svg'{constcontent:string;exportdefaultcontent;}
利点
- 変換も何もされないので詰まったり失敗しない
欠点
- dangerously
2. react-svg-loader の場合
https://github.com/boopathi/react-svg-loader/tree/master/packages/react-svg-loaderを使う
webpack.config.js
{test:/\.svg$/,use:["babel-loader",{loader:'react-svg-loader',options:{svgo:{plugins:[{removeViewBox:false},// to enable overwriteing width/height by CSS{moveElemsAttrsToGroup:false},// to prevent attribute destruction for overwriting color by CSS],floatPrecision:2,},},},],},
ここでsvgoのオプションをdisableしているのは自分の扱うsvgが壊れないように設定した項目だが、扱うsvgによって別のdisableが必要かもしれない
jsx
importHogefrom'... .svg'...<Hoge/>
typescriptならパスを通したglobal型置き場に次のように書く
global.d.ts
declaremodule'*.svg'{constcontent:React.ComponentType;exportdefaultcontent;}
利点
- 巨大ツールでない
- 噛ませるbabel-loaderを自由に指定できる
- next.config.js の
defaultLoaders.babel
とか
- next.config.js の
欠点
- 最近メンテされてないっぽくてリリースがない
- svgoを無効化できない
- SVGの中身によっては更にsvgoのオプションをdisableしないとスタイルを上手く上書きできないかもしれない
3. svgr の場合
https://github.com/gregberge/svgrを使う以外のコードの部分はreact-svg-loaderと同じなので差分だけ書く
- https://react-svgr.com/docs/webpack/に従う
svgr.config.js
を設定する
svgr.config.js
module.exports={svgoConfig:{plugins:{removeViewBox:false,// to enable overwriteing width/height by CSSmoveElemsAttrsToGroup:false,// to prevent attribute destruction for overwriting color},},};
最適化不要、またはファイルに直接かけておく方針なら、svgoを無効化してしまう
svgr.config.js
module.exports={svgo:false,};
利点
- メンテ・リリースが続いている
- svgoを無効化できる
- 破壊的変更のせいでうまくいかなかったら無効化で逃げれる
- star多し
- webpack以外にも対応
欠点
- 依存ライブラリがちょっと多そう
スタイルの当て方
埋め込んだSVGやそのラッパーdivに対しCSSで以下のように書けばサイズや色を変えれる
svg{width:70px;height:70px;}svg*:not([stroke='none'i]){stroke:red;}svg*:not([fill='none'i]){fill:red;}
ポイントは、SVGの色指定はstrokeとfillがあるので、それらがnoneでない箇所を上書きする点
所感
- サクッと実現するならraw-loader + dangerouslySetInnerHTML
- キレイにimportして埋め込みたいなら svgr
- 2つのツールがデフォルトで使うsvgoのデフォルト設定がSVGを破壊的に変更するので曲者
@svgr/cli
などを使ってSVG用のReact Componentを作ってPropsで柔軟に中身を変えるのもアリだけど、SVG素材とReact Componentでほぼ重複するのも悲しいし、用意された素材との同期などが少し面倒
参考
- https://blog.kwst.site/201908203549/
- https://github.com/boopathi/react-svg-loader/tree/master/packages/react-svg-loader
- https://github.com/boopathi/react-svg-loader/issues/295
- https://github.com/boopathi/react-svg-loader/issues/261
- https://github.com/boopathi/react-svg-loader/issues/303
- https://github.com/gregberge/svgr
- https://react-svgr.com/docs/webpack/
- https://github.com/svg/svgo