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

https://react-svgr.com/docs/next/

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

https://scrapbox.io/ygkn/@storybook%2Fexperimental-nextjs-vite_で_SVGR_を使うには

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;
GitHubで編集を提案

Discussion