Next.js Discord

Discord Forum

Adding a specialized SVG webpack loader stops the built-in one from working

Answered
Mudi posted this in #help-forum
Open in Discord
MudiOP
I usually want to import SVGs as components, so I'm using @svgr/webpack. I've configured it like this:

    config.module.rules.push({
      test: /\.svg$/,
      resourceQuery: /react/, // *.svg?react
      issuer: { not: /\.(css|scss|sass)$/ },
      use: [
        {
          loader: "@svgr/webpack",
          options: {
            svgoConfig: {
              plugins: [
                {
                  name: "preset-default",
                  params: {
                    overrides: {
                      removeViewBox: false,
                    },
                  },
                },
              ],
            },
          },
        },
      ],
    });


That's been working fine to import SVGs with a ?react suffix.

The default image loader is also working fine when I import eg a PNG or jpeg.

But if I import an SVG without the ?react suffix I expected the default image loader to still work, and give me an object with a URL etc. However, it doesn't. It gives me an error, saying "currently no loaders are configured to process this file".

I know that loader is there, because PNGs etc import properly. And I see it if in next.config.js I log out all the loaders before or after I append mine.

If I remove my SVGR loader, SVGs import with the image loader just fine, so they must be conflicting in some way. But I don't understand how, since the resourceQuery is supposed to keep the two rules separate.

I have tried adding /react/ to the resourceQuery.not list the image loader rule has, in an attempt to separate them even more, but this didn't help.

I have also tried using unshift rather than push so that the rules are in the opposite order, and this made no difference either.

I don't know how to tell whether this is me misconfiguring webpack, or if Next is doing some magic and "helpfully" detecting my SVG loader and getting its own SVG loader out of the way.

Any ideas?
Answered by Mudi
Well with the resourceQuery: /react/ line, the suffix gives a pretty clear indicator when we want a component as far as I'm concerned (import SvgComponent from "./my-svg.svg?react") and it's been working very well for us.

I never figured out why my SVGR loader is stopping the regular image loader from catching SVGs the SVGR loader doesn't, but I worked around the issue by adding another extra loader.

    const imageLoaderRule = config.module.rules.find((rule) => rule.loader === "next-image-loader");
    if (imageLoaderRule == null) {
      throw new Error("Image loader rule not found");
    }
    const svgImageLoaderRule = structuredClone(imageLoaderRule);
    svgImageLoaderRule.test = /\.svg$/;
    svgImageLoaderRule.resourceQuery.not.push(/react/);
    config.module.rules.push(svgImageLoaderRule);


It's all working fine now. I just don't know why this is necessary.
View full answer

6 Replies

Sinhala Hound
We (at the company I work at) changed from using that webpack plugin to using a similar preprocessing step within our editor (VS Code) which turns SVG directly in to React components. This has been a LOT easier to maintain, while also making it clearer when you are importing a React component, vs linking to a .svg file client side (to be loaded through an <img> tag, or next/image). I recommend this approach, vs. trying to maintain a webpack integration - it's just easier.
MudiOP
Well with the resourceQuery: /react/ line, the suffix gives a pretty clear indicator when we want a component as far as I'm concerned (import SvgComponent from "./my-svg.svg?react") and it's been working very well for us.

I never figured out why my SVGR loader is stopping the regular image loader from catching SVGs the SVGR loader doesn't, but I worked around the issue by adding another extra loader.

    const imageLoaderRule = config.module.rules.find((rule) => rule.loader === "next-image-loader");
    if (imageLoaderRule == null) {
      throw new Error("Image loader rule not found");
    }
    const svgImageLoaderRule = structuredClone(imageLoaderRule);
    svgImageLoaderRule.test = /\.svg$/;
    svgImageLoaderRule.resourceQuery.not.push(/react/);
    config.module.rules.push(svgImageLoaderRule);


It's all working fine now. I just don't know why this is necessary.
Answer
Netherland Dwarf
What i usually do is this for svg compnents
import React from 'react';

export const DownArrowSVG: React.FC<React.SVGProps<SVGSVGElement>> = (
  props
) => {
  return (
    <svg
      width="50"
      height="50"
      viewBox="0 0 50 50"
      fill="none"
      xmlns="http://www.w3.org/2000/svg"
      {...props}
    >
      <path
        d="M25 39.584L25 10.4173M25 39.584L12.5 27.084M25 39.584L37.5 27.084"
        stroke="black"
        strokeWidth="2"
        strokeLinecap="round"
        strokeLinejoin="round"
      />
    </svg>
  );
};
export default DownArrowSVG;
MudiOP
Uh huh. That's similar to what CaptainN suggested above. I don't want to have to go to the effort of doing that for every file (we have dozens, and more are added quite frequently), and I don't want the whole team to need to have a particular editor extension installed.