Skip to content

Instantly share code, notes, and snippets.

@manabuyasuda
Created May 13, 2025 09:23
Show Gist options
  • Save manabuyasuda/b7671e8d5515d49490ad0c679502d3d5 to your computer and use it in GitHub Desktop.
Save manabuyasuda/b7671e8d5515d49490ad0c679502d3d5 to your computer and use it in GitHub Desktop.
SVGファイルをSVGRでコンポーネント化して共通Iconコンポーネントで管理する
module.exports = {
typescript: true, // .tsxファイルを生成する
memo: true, // Reactのメモ化を有効にする
jsxRuntime: 'automatic', // Reactの明示的なインポートをしない
filenameCase: 'kebab', // ファイル名をkebab-caseにする
index: false, // index.tsxファイルを生成しない
svgoConfig: {
plugins: [
{
name: 'preset-default', // 標準的なプリセットを使用する
},
'removeXMLNS', // xmlnsとxmlns:xlinkを削除する
{
name: 'removeAttrs',
params: {
attrs: ['data-name'], // デザインデータから出力される不要な属性を削除する
},
},
],
},
};
// scripts/generate-icons.sh
#!/usr/bin/env bash
# =============================================================================
# SVGアイコンをReactコンポーネントに変換するスクリプト
# =============================================================================
# エラーが発生したら即座に終了し、未定義の変数を使用した場合もエラーにする
set -eu
# 出力先ディレクトリが存在しない場合は作成する
echo "出力先ディレクトリを準備しています..."
mkdir -p src/components/Icon/generated
# 出力先ディレクトリの中身を全て削除して初期化する
echo "前回出力したファイルを削除しています..."
rm -rf src/components/Icon/generated/*
# SVGRはSVGをReactコンポーネントに変換するツール
# 設定ファイルを使用:
# .svgrrc.js: SVGRの設定(typescript, memo, svgoConfig等)
# .prettierrc: コード整形の設定
echo "SVGファイルからReactコンポーネントを生成しています..."
npx svgr \
--out-dir src/components/Icon/generated src/components/Icon/icons
echo "--------------------------------------------------------------"
echo "SVGRによるコンポーネント生成が完了しました"
echo "--------------------------------------------------------------"
echo "生成されたコンポーネント:"
find src/components/Icon/generated -name "*.tsx" | sort
# 手動で更新が必要なファイルの案内
echo ""
echo "--------------------------------------------------------------"
echo "手動で更新が必要なファイル:"
echo "--------------------------------------------------------------"
echo "1. src/components/Icon/types.ts"
echo " - IconName型にアイコン名を追加または削除"
echo ""
echo "2. src/components/Icon/index.tsx"
echo " - 追加したアイコンをインポート"
echo " - iconComponentsマッピングに追加"
echo "--------------------------------------------------------------"
// .
src/components/Icon/index.module.scss
.icon {
display: inline-flex;
align-items: center;
justify-content: center;
width: 1em;
min-width: 1em;
height: 1em;
min-height: 1em;
fill: currentcolor;
stroke: currentcolor;
}
//src/components/Icon/index.tsx
import { ComponentPropsWithoutRef } from 'react';
import { IconProps, IconName } from './types';
import styles from './index.module.scss';
// アイコンが増減したら変更してください
import ArrowRight from './generated/arrow-right';
import Circle from './generated/circle';
import Close from './generated/close';
import Menu from './generated/menu';
// アイコンが増減したら変更してください
const iconComponents: Record<IconName, React.FC<ComponentPropsWithoutRef<'svg'>>> = {
'arrow-right': ArrowRight,
circle: Circle,
close: Close,
menu: Menu,
};
/**
* SVGアイコンを表示するコンポーネントです。
* アイコンを追加する場合は、src/components/Icon/iconsにSVGファイルを追加して、npm run build:iconsを実行してください。
*
* @example
* // 基本的な使用法
* <Icon name="arrow-right" className={styles.arrowRightIcon} />
*
* @example
* // アクセシビリティ対応(スクリーンリーダー向け - JSXスタイル推奨)
* <Icon name="close" className={styles.closeIcon} ariaLabel="閉じる" />
*
* @example
* // アクセシビリティ対応(スクリーンリーダー向け - HTML互換スタイル)
* <Icon name="close" className={styles.closeIcon} aria-label="閉じる" />
*/
export function Icon({
name,
className,
ariaLabel,
'aria-label': ariaLabelHtml,
...props
}: IconProps) {
// 対応するアイコンコンポーネントを取得する
const IconComponent = iconComponents[name];
if (!IconComponent) {
console.warn(`Icon "${name}" not found`);
return null;
}
// ariaLabelとaria-labelの両方が指定された場合はariaLabelを優先
const accessibilityLabel = ariaLabel || ariaLabelHtml;
// クラス名を結合する
const combinedClassName = className ? `${styles.icon} ${className}` : styles.icon;
// アクセシビリティ属性を設定する
const a11yProps = {
'aria-hidden': !accessibilityLabel ? true : undefined,
role: accessibilityLabel ? 'img' : undefined,
'aria-label': accessibilityLabel,
};
return <IconComponent className={combinedClassName} {...a11yProps} {...props} />;
}
//src/components/Icon/types.ts
import { ComponentPropsWithoutRef } from 'react';
// アイコンが増減したら変更してください
export type IconName = 'arrow-right' | 'circle' | 'close' | 'menu';
export type IconProps = ComponentPropsWithoutRef<'svg'> & {
name: IconName;
ariaLabel?: string;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment