'Getting NextJS Image Component & @svgr/webpack to play nicely together

I have a Next.js site with the @svgr/webpack library installed. I have configured next.config.js to work with @svgr/webpack and now want to import an svg image and use it with the new next/image component.

Here is how I set up my next.config.js file:

module.exports = {
  images: {
    domains: ["images.vexels.com"],
  },
  webpack(config) {
    config.module.rules.push({
      test: /\.svg$/,
      use: ["@svgr/webpack"],
    });

    return config;
  },
};

And here is what I am trying to do:

import Image from 'next/image'
import Logo from '@/svg/logo.svg'

<Image src={Logo} width={174} height={84} />

However, when I do that I get the following error:

Unhandled Runtime Error
TypeError: src.startsWith is not a function

Source
client\image.tsx (278:13) @ Image

  276 | let isLazy =
  277 |   !priority && (loading === 'lazy' || typeof loading === 'undefined')
> 278 | if (src && src.startsWith('data:')) {
      |           ^
  279 |   // https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs
  280 |   unoptimized = true
  281 |   isLazy = false

I thought that perhaps I should include the Logo component as an actual component, like this: <Image src={<Logo />} width={174} height={84} />

However, that also did not work.

Any idea what is wrong and how to fix it?



Solution 1:[1]

With your current webpack config importing @/svg/logo.svg will only import the SVG file as a React component.

To import it as a data URL, you will need the following webpack config in your next.config.js.

module.exports = {
    images: {
        domains: ['images.vexels.com']
    },
    webpack(config) {
        config.module.rules.push({
            test: /\.svg$/,
            use: ['@svgr/webpack', 'url-loader']
        });

        return config;
    }
};

You'll then be able to use it in both ways: as a URL or as a component.

import Image from 'next/image'
import svgUrl, { ReactComponent as Logo } from '@/svg/logo.svg'

<Image src={svgUrl} width={174} height={84} />
// or as a component
<Logo />

Solution 2:[2]

A workaround for this maybe by having a specific pattern for svg file name, and then configuring the default loader to ignore this pattern and svgr/webpack to load matches for this pattern

 webpack(config) {
    const fileLoaderRule = config.module.rules.find(
      (rule) => rule.test && rule.test.test(".svg")
    );
    fileLoaderRule.exclude = /\.icon\.svg$/;
    config.module.rules.push({
      test: /\.icon\.svg$/,
      loader: require.resolve("@svgr/webpack"),
    });
    return config;
  },

here i'm using the pattern *.icon.svg, so any svg image that ends with it can be used like this

import Logo from "whatever/logo.icon.svg

const Whatever = () => <Logo />

and for other icons this'll work

import Image from "next/image";
import Logo from "whatever/logo.svg"

const Whatever = () => <Image src={Logo} alt="logo" width={100} height={100}/>

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 sudofix