svgr icon indicating copy to clipboard operation
svgr copied to clipboard

How to modify next.config.js in next 13.3.0?

Open suenot opened this issue 1 year ago • 11 comments

After install nextjs I have next.config.js:

/** @type {import('next').NextConfig} */

const nextConfig = {
  reactStrictMode: true,
}

module.exports = nextConfig

In svgo docs another format of next.config.js:

module.exports = {
  webpack(config) {
    // Grab the existing rule that handles SVG imports
    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: /\.[jt]sx?$/,
        resourceQuery: { not: /url/ }, // exclude if *.svg?url
        use: ['@svgr/webpack'],
      },
    )

    // Modify the file loader rule to ignore *.svg, since we have it handled now.
    fileLoaderRule.exclude = /\.svg$/i

    return config
  },

  // ...other config
}

As I understand, I need rewrite config in docs in new format. Could you help with that?

suenot avatar Apr 19 '23 13:04 suenot

If someone has the solution it would be nice to update the documentation.

gregberge avatar Apr 20 '23 07:04 gregberge

I'm also facing the kinda same problem for the app dir in Nextjs. But works fine in the pages directory.

arikchakma avatar Apr 24 '23 16:04 arikchakma

This may be related, about the issuer property:

https://github.com/gregberge/svgr/issues/726

But for me I managed to make it work with a different issuer (the same fileLoaderRule uses), and using the loader and options keys, instead of use: ['@svgr/webpack'].

So, what I have is:

/** @type {import('next').NextConfig} */
const nextConfig = {
	experimental: {
		appDir: true,
		typedRoutes: true,
	},
	webpack(config) {
		// Grab the existing rule that handles SVG imports
		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: {not: /\.(css|scss|sass)$/},
				resourceQuery: {not: /url/}, // exclude if *.svg?url
				loader: '@svgr/webpack',
				options: {
					dimensions: false,
					titleProp: true,
				},
			}
		)

		// Modify the file loader rule to ignore *.svg, since we have it handled now.
		fileLoaderRule.exclude = /\.svg$/i

		return config
	},
}

module.exports = nextConfig

Pass your own options to taste, but note loader: '@svgr/webpack': '@svgr/webpack' is a string, not an array as it is in the docs, with use.

jmagrippis avatar May 02 '23 17:05 jmagrippis

Using @jmagrippis's issuer: issuer: {not: /\.(css|scss|sass)$/}, worked for me in next ^13.3.2-canary.12

Before that, I was getting this compilation error: Module parse failed: Unexpected token (1:0) You may need an appropriate loader to handle this fi...

Oddly this error only showed up on a sub-page (/about instead of /)

jamesvclements avatar May 02 '23 18:05 jamesvclements

i solve it like this

    const fileLoaderRule = config.module.rules.find(
      (rule) => rule.test && rule.test instanceof RegExp && rule.test.test(".svg")
    );

yongsk0066 avatar Jun 23 '23 08:06 yongsk0066

Here's how I solved it:

  webpack(config) {
    // Configures webpack to handle SVG files with SVGR. SVGR optimizes and transforms SVG files
    // into React components. See https://react-svgr.com/docs/next/

    // Grab the existing rule that handles SVG imports
    // @ts-ignore - this is a private property that 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: ['@svgr/webpack'],
      }
    );

    // Modify the file loader rule to ignore *.svg, since we have it handled now.
    fileLoaderRule.exclude = /\.svg$/i;

    return config;
  },

IGassmann avatar Jun 23 '23 08:06 IGassmann

Thanks @IGassmann, would be great if this was listed in the Docs as a change in config for v13

ChazUK avatar Jul 23 '23 18:07 ChazUK

Since Next has image-type built in and it sets *.svg type as any, I found that I kept customizing the webpack config and type reference to just let the loader work with the svg file path without query and have a type safe environment.

CleanShot 2023-07-28 at 00 55 31@2x

Then I just think, what about doing this reversely?

const fileLoaderRule = config.module.rules.find((rule) => rule.test?.test?.('.svg'));

I found that what we do in this line is finding the next-image-loader rule, I tried letting it does its job and add another feature to load svg by svgr loader. Here's my config:

/** @type {import('next').NextConfig} */
const nextConfig = {
  webpack: withSVGR,
};

export default nextConfig;

function withSVGR(config) {
  const nextImageLoaderRule = config.module.rules.find((rule) =>
    rule.test?.test?.(".svg"),
  );

  nextImageLoaderRule.resourceQuery = {
    not: [...nextImageLoaderRule.resourceQuery.not, /icon/],
  };

  config.module.rules.push({
    issuer: nextImageLoaderRule.issuer,
    resourceQuery: /icon/, // *.svg?icon
    use: ["@svgr/webpack"],
  });

  return config;
}
// global.d.ts

interface SVGIcon
  extends React.FunctionComponent<React.SVGAttributes<HTMLOrSVGElement>> {}

declare module "*.svg?icon" {
  const content: SVGIcon;
  export default content;
}

CleanShot 2023-07-28 at 01 07 16

It's ok for me to just add the ?icon query for importing the svg icon through svgr-loader, and I can type the imported module easily without other hack. I think this way may reduce conflicts with next.js built-in settings.

judewang avatar Jul 27 '23 16:07 judewang

Here's how I solved it:

  webpack(config) {
    // Configures webpack to handle SVG files with SVGR. SVGR optimizes and transforms SVG files
    // into React components. See https://react-svgr.com/docs/next/

    // Grab the existing rule that handles SVG imports
    // @ts-ignore - this is a private property that 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: ['@svgr/webpack'],
      }
    );

    // Modify the file loader rule to ignore *.svg, since we have it handled now.
    fileLoaderRule.exclude = /\.svg$/i;

    return config;
  },

How can I add options in here? Like I want to keep the viewBox attribute.

curiosbasant avatar Apr 08 '24 06:04 curiosbasant

Here's how I solved it:

  webpack(config) {
    // Configures webpack to handle SVG files with SVGR. SVGR optimizes and transforms SVG files
    // into React components. See https://react-svgr.com/docs/next/

    // Grab the existing rule that handles SVG imports
    // @ts-ignore - this is a private property that 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: ['@svgr/webpack'],
      }
    );

    // Modify the file loader rule to ignore *.svg, since we have it handled now.
    fileLoaderRule.exclude = /\.svg$/i;

    return config;
  },

How can I add options in here? Like I want to keep the viewBox attribute.

You can pass custom options by replacing use: ['@svgr/webpack'], with use: [{ loader: '@svgr/webpack', options: { icon: true } }],. Alternatively, you can use a SVGR configuration file and/or a SVGO configuration file.

Here's an example of a configuration that configs SVGO to not remove the viewBox attribute: https://github.com/IGassmann/web-app-template/blob/6f766e921220145d858dce2fc4c448f162b18d4c/svgo.config.js#L16

IGassmann avatar Apr 09 '24 12:04 IGassmann

Error still in Next.js 14.2.4

 ⨯ ./src/assets/logo.svg
TypeError: Cannot read properties of undefined (reading '0')
    at Array.filter (<anonymous>)
Import trace for requested module:
./src/assets/logo.svg

chumaumenze avatar Jun 15 '24 21:06 chumaumenze