svgr/webpack Not Working With Webpack 5 and SingleSpa

1.4k views Asked by At

I been struggling with this for a week, I been reading another Stackoverflow questions and the SVGR/WEBPACK documentation but I'm unable to solve this.

I wanted to upgrade an old react single-spa application but it had a lot of conflicting dependencies so I simply went and created a new one using the Single Spa Playground web application, everything works great but I can't get my SVG'S as React Components with SVGR/WEBPACK.

The error:

enter image description here

webpack.config.js

const webpackMerge = require("webpack-merge").merge;
const singleSpaDefaults = require("webpack-config-single-spa-react-ts");

module.exports = (webpackConfigEnv, argv) => {
  const defaultConfig = singleSpaDefaults({
    orgName: "testorg",
    projectName: "testproj",
    webpackConfigEnv,
    argv,
  });

  return webpackMerge(defaultConfig, {
    module: {
      rules: [
        {
          test: /\.s[ac]ss$/i,
          use: ["style-loader", "css-loader", "sass-loader"],
        },
        {
          test: /\.svg$/,
          use: ["@svgr/webpack"],
        },
      ],
    },
  });
};

How I'm importing the svg

import TestImg from "../../assets/icons/testimage.svg";

How I'm using the SVG

<TestImg /> 

declarations.d.ts

I'm not entirely sure how does this works but I also removed this and it's the same.

declare module "*.svg" {
  const content: React.FC<React.SVGAttributes<SVGElement>>;
  export default content;
}

package.json

{
  "name": "@testorg/testproj",
  "scripts": {
    "start": "webpack serve",
    "start:standalone": "webpack serve --env standalone",
    "build": "concurrently npm:build:*",
    "build:webpack": "webpack --mode=production",
    "analyze": "webpack --mode=production --env analyze",
    "lint": "eslint src --ext js,ts,tsx",
    "format": "prettier --write .",
    "check-format": "prettier --check .",
    "test": "cross-env BABEL_ENV=test jest",
    "watch-tests": "cross-env BABEL_ENV=test jest --watch",
    "prepare": "husky install",
    "coverage": "cross-env BABEL_ENV=test jest --coverage",
    "build:types": "tsc"
  },
  "devDependencies": {
    "@babel/core": "^7.15.0",
    "@babel/eslint-parser": "^7.15.0",
    "@babel/plugin-transform-runtime": "^7.15.0",
    "@babel/preset-env": "^7.15.0",
    "@babel/preset-react": "^7.14.5",
    "@babel/preset-typescript": "^7.15.0",
    "@babel/runtime": "^7.15.3",
    "@testing-library/jest-dom": "^5.14.1",
    "@testing-library/react": "^12.0.0",
    "@types/testing-library__jest-dom": "^5.14.1",
    "babel-jest": "^27.0.6",
    "concurrently": "^6.2.1",
    "cross-env": "^7.0.3",
    "eslint": "^7.32.0",
    "eslint-config-prettier": "^8.3.0",
    "eslint-config-ts-react-important-stuff": "^3.0.0",
    "eslint-plugin-prettier": "^3.4.1",
    "husky": "^7.0.2",
    "identity-obj-proxy": "^3.0.0",
    "jest": "^27.0.6",
    "jest-cli": "^27.0.6",
    "prettier": "^2.3.2",
    "pretty-quick": "^3.1.1",
    "sass": "^1.56.2",
    "sass-loader": "^13.2.0",
    "ts-config-single-spa": "^3.0.0",
    "typescript": "^4.3.5",
    "webpack": "^5.75.0",
    "webpack-cli": "^4.8.0",
    "webpack-config-single-spa-react": "^4.0.0",
    "webpack-config-single-spa-react-ts": "^4.0.0",
    "webpack-config-single-spa-ts": "^4.0.0",
    "webpack-dev-server": "^4.0.0",
    "webpack-merge": "^5.8.0"
  },
  "dependencies": {
    "@datepicker-react/hooks": "^2.8.4",
    "@svgr/webpack": "^6.5.1",
    "@types/jest": "^27.0.1",
    "@types/react": "^17.0.19",
    "@types/react-dom": "^17.0.9",
    "@types/systemjs": "^6.1.1",
    "@types/webpack-env": "^1.16.2",
    "date-fns": "^2.29.3",
    "prop-types": "^15.8.1",
    "react": "^17.0.2",
    "react-datepicker": "^4.8.0",
    "react-dom": "^17.0.2",
    "react-modal": "^3.16.1",
    "react-number-format": "^5.1.2",
    "react-toastify": "^6.2.0",
    "single-spa": "^5.9.3",
    "single-spa-react": "^4.3.1",
    "styled-components": "^5.3.6"
  },
  "types": "dist/testorg-testproj.d.ts"
}

The only way it works

The only way I got it working is uninstalling @svgr/webpack and using my imported svg inside of an img tag, as a source.

<img src={TestImg} alt='' width='200' height='200' />

But is not what I want, I need to be able modify the svg fill by props and that's why I need it as a React Component.

Also, I have a LOT of icons and logos in this project, so manually creating a React Component in my project for each image/icon it's not the solution I'm looking for.

Additional information 1

I put a console log for the TestImg and it shows an url, clearly it's not importing it as a component

If I go to the url this is what it shows: enter image description here

Additional information 2

import { ReactComponent as TestImg } from "../../assets/icons/testimage.svg"

Doesn't work either, it says:

Can't import the named export 'ReactComponent' (imported as 'TestImg') from default-exporting module (only default export is available)

2

There are 2 answers

0
Ifedayo Prince Oni On

As at the time of posting, Webpack 5 uses 'Asset Resource', so your config would need to be updated to:

return webpackMerge(defaultConfig, {
  module: {
    rules: [{
        test: /\.s[ac]ss$/i,
        use: ["style-loader", "css-loader", "sass-loader"],
      },
      {
        test: /\.svg$/,
        oneOf: [{
            issuer: /\.[jt]sx?$/,
            resourceQuery: /react/, // *.svg?react
            use: [{
              loader: '@svgr/webpack',
              options: {
                icon: true
              }
            }]
          },
          {
            type: 'asset',
            parser: {
              dataUrlCondition: {
                maxSize: 200
              }
            }
          },
        ],
      },
    ],
  },
});

Then you import your SVGs like so:

import GreenLogo from '@/svgs/logo-full.svg?react';

Your declarations.d.ts file would be:

declare module '*.svg?react' {
  const content: React.FC < React.SVGAttributes < SVGElement >> ;
  export default content;
}
0
myverdict On

The below worked for me:

Firstly, add the package url-loader in your project:

npm install url-loader --save-dev

Secondly, update your webpack.config.js file to the below:

{
   test: /\.svg$/i,
   issuer: /\.[jt]sx?$/,
   use: ['@svgr/webpack', 'url-loader'],
},

Then in the actual component import it like this:

import { ReactComponent as TestImg } from "../../assets/icons/testimage.svg"

This should fix it and then you can use it like this:

<TestImg />