Gatsby は、事前に画像ファイルをいくつか生成することによって、様々なフォーマットの画像を GraphQL を使って取得することができます。
ですが SVG に関しては特にそういったものがありません。そのため、GraphQL を使って取得すると無駄にめんどくさいです。
なので、よく使う SVG ファイルをコンポーネント化できるようにします。
GraphQL で SVG 画像を取得するとき
graphql タグで query を定義、useStaticQuery
でクエリを実行します。
export const MyImage = graphql`
query MyImage {
allFile(filter: { name: { eq: "my-image" } }) {
edges {
node {
publicURL
}
}
}
}
`;
....
const image = useStaticQuery<MyImageQuery>(MyImage);
-> image.edges[0].node.publicURL が画像パス
fragment にしたり、別ファイルに分けるなどすればクエリでの取得は少し楽になりますが、これで取得したとしても CSS で色を変更するなど SVG の強みを活かすことができません。
コンポーネント化
Icon/MyIcon/index.tsx
import React from 'react';
export interface IconProps {
className?: string; // svg タグのクラス
fill?: string; // 色
lineWidth?: number; // アウトラインの太さ
width?: number; // 横
height?: number; // 縦
}
export const MyIcon = ({ className, fill = '#aaa', width = 16, height = 16 }: IconProps) => {
return (
<svg
className={className}
version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlnsXlink="http://www.w3.org/1999/xlink"
width={width}
height={height}
>
<g fill={fill}></g>
</svg>
);
};
<svg>
タグの中身は適当ですが、イメージとしてはだいたいこんな感じ。
SVG 画像をそっくりそのまま JSX として returnしているだけです。属性の部分は Props で上書きできるように多少いじっています。
Typescript なので Interface を定義してますが、Interface は全アイコン共通でもいいので、別ファイルに定義しておくと良いと思います。
また Props は必要そうなものを入れています。svg タグに className だけ渡して css で操作するという手もありますが、SVG は構造がそれぞれ異なるので、svg タグにクラスを渡すだけだとスタイリングしにくいです。
アイコンに使う場合、アウトラインの色、背景色、サイズを変更することが多いのでそれらを Props で受け取り、あとは自分でタグ構造を見ながら埋め込んでいくという形です。
使う時
import MyIcon from 'Icon/MyIcon'
<MyIcon fill="red" width="40" height="40" />
一番最初に SVG の HTML を埋め込むところが面倒ではありますが、使うときは楽かなあ…と思います。