iTranslated by AI
The content below is an AI-generated translation. This is an experimental feature, and may contain errors. View original article
🤘
How to Set Up SVGR in Next.js 15 & Storybook v8.6
SVGR, which allows you to treat SVGs as React components, is an extremely useful tool for UI development. In this article, I will explain how to set up SVGR in both Next.js 15 and Storybook v8.6 to handle SVGs smoothly.
Setting up SVGR in Next.js
Installing dependency packages
pnpm add -D @svgr/webpack
Configuring next.config.ts
Following the official SVGR documentation, add the following configuration to next.config.ts.
next.config.ts
import type { NextConfig } from 'next';
const nextConfig: NextConfig = {
/* config options here */
webpack(config) {
// Grab the existing rule that handles SVG imports
// @ts-expect-error config.module.rules is not typed
const fileLoaderRule = config.module.rules.find((rule) =>
rule.test?.test?.('.svg'),
);
config.module.rules.push(
// Reapply the existing rule, but only for svg imports ending in ?url
{
...fileLoaderRule,
test: /\\.svg$/i,
resourceQuery: /url/, // *.svg?url
},
// Convert all other *.svg imports to React components
{
test: /\\.svg$/i,
issuer: fileLoaderRule.issuer,
resourceQuery: { not: [...fileLoaderRule.resourceQuery.not, /url/] }, // exclude if *.svg?url
use: [{ loader: '@svgr/webpack', options: { icon: true } }],
},
);
// Modify the file loader rule to ignore *.svg, since we have it handled now.
fileLoaderRule.exclude = /\\.svg$/i;
return config;
},
};
export default nextConfig;
Adding type definition files
In a TypeScript environment, let's add type definitions for SVG imports.
svgr.d.ts
declare module '*.svg' {
import type { FC, SVGProps } from 'react';
const content: FC<SVGProps<SVGElement>>;
export default content;
}
declare module '*.svg?url' {
// biome-ignore lint/suspicious/noExplicitAny: any is required here
const content: any;
export default content;
}
Setting up SVGR in Storybook
To handle SVGs as React components in Storybook as well, use vite-plugin-svgr.
Installing dependency packages
pnpm add -D vite-plugin-svgr
Configuring .storybook/main.ts
Following this article, add the following configuration to .storybook/main.ts.
.storybook/main.ts
import type { StorybookConfig } from '@storybook/experimental-nextjs-vite';
import svgr from 'vite-plugin-svgr';
import tsconfigPaths from 'vite-tsconfig-paths';
const config: StorybookConfig = {
stories: ['../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
addons: [
'@storybook/addon-essentials',
'@storybook/addon-onboarding',
'@chromatic-com/storybook',
'@storybook/experimental-addon-test',
],
framework: {
name: '@storybook/experimental-nextjs-vite',
options: {},
},
staticDirs: ['../public'],
viteFinal(config) {
config.plugins?.push(tsconfigPaths());
config.plugins?.push(
svgr({
include: /\.svg$/,
}),
);
config.plugins = config.plugins?.flat().map((plugin) => {
if (
typeof plugin === 'object' &&
plugin !== null &&
'name' in plugin &&
plugin.name === 'vite-plugin-storybook-nextjs-image'
) {
return {
...plugin,
resolveId(id, importer) {
if (id.endsWith('.svg')) {
return null;
}
// @ts-expect-error `resolveId` hook of vite-plugin-storybook-nextjs-image is a function
return plugin.resolveId(id, importer);
},
};
}
return plugin;
});
return config;
},
};
export default config;
Discussion