Custom SVG Icons via SVGR — Implementation Plan
Обновлён 1 апр. 2026 г., 12:41 · 0 комментариев
Custom SVG Icons via SVGR — Implementation Plan
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: Set up SVGR so SVG files from Figma can be imported as React components with currentColor and flexible sizing.
Architecture: @svgr/webpack transforms .svg files into React components at build time. Turbopack config handles dev, webpack config handles production. Icons live in apps/web/src/shared/icons/ with a barrel re-export.
Tech Stack: Next.js 16, @svgr/webpack@^8, TypeScript, Tailwind CSS 4
Spec: docs/superpowers/specs/2026-03-18-svg-icons-design.md
Task 1: Install @svgr/webpack
Files:
-
Modify:
apps/web/package.json -
Step 1: Install the dependency
cd apps/web && pnpm add -D @svgr/webpack@^8
- Step 2: Verify installation
cd apps/web && pnpm list @svgr/webpack
Expected: shows @svgr/webpack 8.x.x
- Step 3: Commit
git add apps/web/package.json pnpm-lock.yaml
git commit -m "chore: add @svgr/webpack dependency"
Task 2: Configure Next.js for SVGR (Turbopack + Webpack)
Files:
-
Modify:
apps/web/next.config.ts -
Step 1: Add Turbopack and Webpack SVG rules to
next.config.ts
Replace the current nextConfig object with:
import type { NextConfig } from 'next';
import { resolve } from 'path';
import createNextIntlPlugin from 'next-intl/plugin';
const withNextIntl = createNextIntlPlugin('./src/shared/i18n/request.ts');
const nextConfig: NextConfig = {
output: 'standalone',
outputFileTracingRoot: resolve(__dirname, '../../'),
reactStrictMode: true,
transpilePackages: ['@repo/shared', '@repo/ui'],
reactCompiler: true,
turbopack: {
rules: {
'*.svg': {
loaders: [
{
loader: '@svgr/webpack',
options: {
svgoConfig: {
plugins: [
{
name: 'preset-default',
params: {
overrides: {
removeViewBox: false,
},
},
},
{
name: 'convertColors',
params: {
currentColor: true,
},
},
],
},
},
},
],
as: '*.js',
},
},
},
webpack(config) {
// Remove Next.js built-in SVG handling to avoid conflicts
const fileLoaderRule = config.module.rules.find(
(rule: { test?: RegExp }) => rule.test?.test?.('.svg'),
);
if (fileLoaderRule) {
fileLoaderRule.exclude = /\.svg$/;
}
config.module.rules.push({
test: /\.svg$/,
use: [
{
loader: '@svgr/webpack',
options: {
svgoConfig: {
plugins: [
{
name: 'preset-default',
params: {
overrides: {
removeViewBox: false,
},
},
},
{
name: 'convertColors',
params: {
currentColor: true,
},
},
],
},
},
},
],
});
return config;
},
...(process.env.NODE_ENV === 'development' && {
async rewrites() {
return [
{
source: '/api/v1/:path*',
destination: `${process.env.API_URL || 'http://localhost:5001'}/api/v1/:path*`,
},
];
},
}),
};
export default withNextIntl(nextConfig);
- Step 2: Commit
git add apps/web/next.config.ts
git commit -m "feat: configure SVGR for Turbopack and Webpack"
Task 3: Add TypeScript declaration for SVG imports
Files:
-
Create:
apps/web/svg.d.ts -
Step 1: Create
svg.d.tsat project root (alongsidenext-env.d.ts)
declare module '*.svg' {
import { FC, SVGProps } from 'react';
const component: FC<SVGProps<SVGSVGElement>>;
export default component;
}
- Step 2: Verify
tsconfig.jsonincludes it
The existing include pattern "**/*.ts" already covers svg.d.ts at the project root. No changes needed.
Run:
cd apps/web && pnpm check-types
Expected: no errors
- Step 3: Commit
git add apps/web/svg.d.ts
git commit -m "feat: add TypeScript declaration for SVG imports"
Task 4: Create icons directory and migrate existing SVG
Files:
-
Create:
apps/web/src/shared/icons/index.ts -
Move:
apps/web/public/icon/map-pin.svg→apps/web/src/shared/icons/map-pin.svg -
Delete:
apps/web/public/icon/(directory) -
Step 1: Create the icons directory
mkdir -p apps/web/src/shared/icons
- Step 2: Move the existing map-pin.svg
mv apps/web/public/icon/map-pin.svg apps/web/src/shared/icons/map-pin.svg
- Step 3: Delete the empty
public/icon/directory
rm -r apps/web/public/icon
- Step 4: Create
index.tsbarrel file
export { default as MapPinIcon } from './map-pin.svg';
- Step 5: Commit
Note: apps/web/public/icon/ was untracked by git, so deleting it requires no git operation. Only the new files need to be staged.
git add apps/web/src/shared/icons/
git commit -m "feat: create shared/icons directory, migrate map-pin.svg"
Task 5: Verify the setup works end-to-end
Files:
-
No new files — uses existing setup
-
Step 1: Run type check
cd apps/web && pnpm check-types
Expected: no errors
- Step 2: Run dev server and verify SVG import works
cd apps/web && pnpm dev
In browser devtools or by temporarily adding to any page component:
import { MapPinIcon } from '@/shared/icons';
// Inside the component JSX:
<MapPinIcon className="w-6 h-6 text-primary-500" />
Expected: the map pin icon renders, colored in primary-500, sized at 24x24px.
- Step 3: Run production build
cd apps/web && pnpm build
Expected: build succeeds without errors
-
Step 4: Clean up test usage (remove temporary import if added)
-
Step 5: Commit any remaining changes
git add -A && git commit -m "feat: verify SVG icons setup works"