前提
- 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
import Hoge from '... .svg'
...
<div dangerouslySetInnerHTML={{ __html: Hoge }} />
typescriptならパスを通したglobal型置き場に次のように書く
global.d.ts
declare module '*.svg' {
const content: string;
export default content;
}
利点
- 変換も何もされないので詰まったり失敗しない
欠点
- 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
import Hoge from '... .svg'
...
<Hoge />
typescriptならパスを通したglobal型置き場に次のように書く
global.d.ts
declare module '*.svg' {
const content: React.ComponentType;
export default content;
}
利点
- 巨大ツールでない
- 噛ませる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 CSS
moveElemsAttrsToGroup: 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