SVGスプライトをGatsbyで使う方法
SVGスプライトは複数のデータを1つのファイルにsymbolを使ってまとめたファイルです。 例えばこんなやつ。レスポンシブルデザインでよく見るハンバーガー(三)とxです。
<svg width="0" height="0" class="hidden" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<!-- hamburger(三) -->
<symbol id="icon-hamburger" viewBox="0 0 24 24">
<path fill-rule="evenodd" d="M4 5h16a1 1 0 0 1 0 2H4a1 1 0 1 1 0-2zm0 6h16a1 1 0 0 1 0 2H4a1 1 0 0 1 0-2zm0 6h16a1 1 0 0 1 0 2H4a1 1 0 0 1 0-2z" />
</symbol>
<!-- times(x) -->
<symbol id="icon-times" viewBox="0 0 24 24">
<path fill-rule="evenodd" d="M18.278 16.864a1 1 0 0 1-1.414 1.414l-4.829-4.828-4.828 4.828a1 1 0 0 1-1.414-1.414l4.828-4.829-4.828-4.828a1 1 0 0 1 1.414-1.414l4.829 4.828 4.828-4.828a1 1 0 1 1 1.414 1.414l-4.828 4.829 4.828 4.828z" />
</symbol>
</defs>
</svg>
このように定義したやつをGatsbyで読み込んでuseタグを使ってこんな風に利用する方法を説明します。
<svg>
<use xmlns="http://www.w3.org/1999/xlink" xlink:href="#svg-sprites_icon-hamburger"></use>
</svg>
SVGスプライトをheadで読み込む方法
Gatsbyでは自分でテンプレートを指定しなければビルド時に自動で生成された .cache/default-html.js
(開発時)がテンプレートして使われるようになってますが、このファイルを参考にしてsrc/html.js
と上書きすることでテンプレートを変更することができます。
これを利用してbodyでSVGスプライトを読み込むようにした例が以下です。
TypeScriptでない例は、.cache/default-html.js
をコピーすればいいだけなので、TypeScriptの例を用意しました。
// [ref]
// https://github.com/chitoku-k/chitoku.jp/blob/master/src/html.tsx
import React, { DetailedHTMLProps, ReactNode } from 'react'
import svgSprites from '@/assets/svg-sprites.svg'
const HTML: React.FC<HTMLProps> = ({
htmlAttributes,
headComponents,
bodyAttributes,
preBodyComponents,
body: __html,
postBodyComponents,
}) => {
return (
<html {...htmlAttributes}>
<head>
<meta charSet="UTF-8" />
<meta httpEquiv="X-UA-Compatible" content="IE=Edge" />
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1.0,user-scalable=yes" />
{headComponents}
</head>
<body {...bodyAttributes}>
{preBodyComponents}
<svg style={{ display: 'none', width: '0', height: '0' }} xmlns={"http://www.w3.org/2000/svg"} xmlnsXlink={"http://www.w3.org/1999/xlink"} dangerouslySetInnerHTML={{ __html: svgSprites.content }} />
<div key="body" id="___gatsby" dangerouslySetInnerHTML={{ __html }} />
{postBodyComponents}
</body>
</html>
)
}
interface HTMLProps {
htmlAttributes: DetailedHTMLProps<React.HtmlHTMLAttributes<HTMLHtmlElement>, HTMLHtmlElement>
headComponents: ReactNode[]
bodyAttributes: DetailedHTMLProps<React.HTMLAttributes<HTMLBodyElement>, HTMLBodyElement>
preBodyComponents: ReactNode[]
body: string
postBodyComponents: ReactNode[]
}
export default HTML
やっていることは、src/assets/svg-sprites.svg
ファイルをモジュールとして読み込んで {preBodyComponent}
直下でdangerouslySetInnerHTML
を使って読み込んでいるだけです。
注意点があって、src/assets/svg-sprites.svg
ファイルをモジュールとして読み込むためのローダーが必要です。
Gatsbyなのでプラグインとして用意してあって gatsby-plugin-svg-sprite を利用することでモジュールとして読み込むことができるようになります。コード読めばわかるのですが、Gatsbyの replaceWebpackConfig API
を使ってwebpackの設定にローダーを設定しているだけですね。
{
test: /\.svg$/,
use: [
{
loader: require.resolve('svg-sprite-loader'),
options,
},
],
},
このプラグインをgatsby-config.js
に設定してから
import svgSprites from '@/assets/svg-sprites.svg'
として読み込むとsvgSpritesは以下のようなオブジェクトに変換されています。
svgSprites SpriteSymbol {
id: 'svg-sprites',
viewBox: '0 0 0 0',
content: '...長いので省略...'
}
注意点は、モジュールとして解決後、id
(SVGスプライトのファイル名)がprefixとしてついてしまうという点です。
なのでicon-hamburger
がsvg-sprites_icon-hamburger
になってしまってます。
content: '<symbol class="hidden" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 0 0" id="svg-sprites">\n' +
' <defs>\n' +
' <!-- hamburger -->\n' +
' <symbol id="svg-sprites_icon-hamburger" viewBox="0 0 24 24">\n' +
' <path fill-rule="evenodd" d="M4 5h16a1 1 0 0 1 0 2H4a1 1 0 1 1 0-2zm0 6h16a1 1 0 0 1 0 2H4a1 1 0 0 1 0-2zm0 6h16a1 1 0 0 1 0 2H4a1 1 0 0 1 0-2z" />\n' +
' </symbol>\n' +
必要なのはcontent
なのでsvgSprites.content
をdangerouslySetInnerHTML
で渡せばいいとなります。
SVGスプライトを利用するためのコンポーネント作成
SVGスプライトを利用するための専用コンポーネントを作っておいたがいいです。
毎回、<svg ... ><use ... /></svg>
って書くのは辛いですから。
これは私のブログで使っているコンポーネントです。
import React from 'react'
type Props = {
className?: string
style?: any
xlinkHref: string
}
const SvgIcon: React.FC<Props> = (props) => {
const { className, style, xlinkHref } = props
return (
<svg className={className} style={style}>
<use xmlns={"http://www.w3.org/1999/xlink"} xlinkHref={`#${xlinkHref}`} />
</svg >
)
}
export default SvgIcon
使い方はこうです。
<SvgIcon xlinkHref="svg-sprites_icon-hamburger" />
まとめ
- SVGスプライトをGatsbyで読み込む方法を説明しました。
- SVGスプライトをモジュールとして読み込むためには
gatsby-plugin-svg-sprite
が必要だと説明しました。 - SVGスプライトを利用するためのコンポーネントの作例を紹介しました。
以上です。