import SVG as React Components with webpack 5
Asked Answered
F

1

8

I want to use SVG as a React Component in my app. I'm using: react 17.0.2, Webpack 5.57.1, @svgr/webpack 6.2.1.

I followed the steps on adding svgr in webpack.config file as in svgr documents svgr-doc but there is an Error in the console dev tools:

react-dom.development.js:67 Warning: <data:image/svg+xml;base64,dmFyIF9wYXRoOwoKZnVuY3Rpb24gX2V4dGVuZHMoKSB7IF9leHRlbmRzID0gT2JqZWN0LmFzc2lnbiB8fCBmdW5jdGlvbiAodGFyZ2V0KSB7IGZvciAodmFyIGkgPSAxOyBpIDwgYXJndW1lbnRzLmxlbmd0aDsgaSsrKSB7IHZhciBzb3VyY2UgPSBhcmd1bWVudHNbaV07IGZvciAodmFyIGtleSBpbiBzb3VyY2UpIHsgaWYgKE9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbChzb3VyY2UsIGtleSkpIHsgdGFyZ2V0W2tleV0gPSBzb3VyY2Vba2V5XTsgfSB9IH0gcmV0dXJuIHRhcmdldDsgfTsgcmV0dXJuIF9leHRlbmRzLmFwcGx5KHRoaXMsIGFyZ3VtZW50cyk7IH0KCmltcG9ydCAqIGFzIFJlYWN0IGZyb20gInJlYWN0IjsKCnZhciBTdmdDYWxlbmRhciA9IGZ1bmN0aW9uIFN2Z0NhbGVuZGFyKHByb3BzKSB7CiAgcmV0dXJuIC8qI19fUFVSRV9fKi9SZWFjdC5jcmVhdGVFbGVtZW50KCJzdmciLCBfZXh0ZW5kcyh7CiAgICB4bWxuczogImh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiwKICAgIHdpZHRoOiAxNiwKICAgIGhlaWdodDogMTYsCiAgICBmaWxsOiAiY3VycmVudENvbG9yIgogIH0sIHByb3BzKSwgX3BhdGggfHwgKF9wYXRoID0gLyojX19QVVJFX18qL1JlYWN0LmNyZWF0ZUVsZW1lbnQoInBhdGgiLCB7CiAgICBkOiAiTTExLjMzMyAyLjY2N0gxNGEuNjY3LjY2NyAwIDAgMSAuNjY3LjY2NlYxNGEuNjY3LjY2NyAwIDAgMS0uNjY3LjY2N0gyQS42NjcuNjY3IDAgMCAxIDEuMzMzIDE0VjMuMzMzQS42NjcuNjY3IDAgMCAxIDIgMi42NjdoMi42NjdWMS4zMzNINnYxLjMzNGg0VjEuMzMzaDEuMzMzdjEuMzM0ek0xMCA0SDZ2MS4zMzNINC42NjdWNGgtMnYyLjY2N2gxMC42NjZWNGgtMnYxLjMzM0gxMFY0em0zLjMzMyA0SDIuNjY3djUuMzMzaDEwLjY2NlY4eiIKICB9KSkpOwp9OwoKZXhwb3J0IHsgU3ZnQ2FsZW5kYXIgYXMgUmVhY3RDb21wb25lbnQgfTsKZXhwb3J0IGRlZmF1bHQgImRhdGE6aW1hZ2Uvc3ZnK3htbDtiYXNlNjQsUEhOMlp3b2dJQ0FnSUNBZ0lIaHRiRzV6UFNKb2RIUndPaTh2ZDNkM0xuY3pMbTl5Wnk4eU1EQXdMM04yWnlJS0lDQWdJQ0FnSUNCM2FXUjBhRDBpTVRZaUNpQWdJQ0FnSUNBZ2FHVnBaMmgwUFNJeE5pSUtJQ0FnSUNBZ0lDQm1hV3hzUFNKamRYSnlaVzUwUTI5c2IzSWlDaUFnSUNBZ0lDQWdkbWxsZDBKdmVEMGlNQ0F3SURFMklERTJJZ28rQ2lBZ0lDQThjR0YwYUFvZ0lDQWdJQ0FnSUNBZ0lDQmtQU0pOTVRFdU16TXpJREl1TmpZM1NERTBZUzQyTmpjdU5qWTNJREFnTURFdU5qWTNMalkyTmxZeE5HRXVOalkzTGpZMk55QXdJREF4TFM0Mk5qY3VOalkzU0RKQkxqWTJOeTQyTmpjZ01DQXdNVEV1TXpNeklERTBWak11TXpNelFTNDJOamN1TmpZM0lEQWdNREV5SURJdU5qWTNhREl1TmpZM1ZqRXVNek16U0RaMk1TNHpNelJvTkZZeExqTXpNMmd4TGpNek0zWXhMak16TkhwTk1UQWdORWcyZGpFdU16TXpTRFF1TmpZM1ZqUm9MVEoyTWk0Mk5qZG9NVEF1TmpZMlZqUm9MVEoyTVM0ek16TklNVEJXTkhwdE15NHpNek1nTkVneUxqWTJOM1kxTGpNek0yZ3hNQzQyTmpaV09Ib2lMejRLUEM5emRtYysiOw== /> is using incorrect casing. Use PascalCase for React components, or lowercase for HTML elements.

and the SVG doesn't show. I can't import it as a React Component.

my App:

import React from 'react';
import ReactDOM from 'react-dom';

import CalendarIcon from './Calendar.svg'

const App = () => (
   <div>
       Test
       <CalendarIcon/>
   </div>
);

ReactDOM.render(<App />, document.getElementById('app'));

webpack file:

const path = require('path');
const HtmlWebPackPlugin = require('html-webpack-plugin');
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');

const deps = require('./package.json').dependencies;

module.exports = {
  output: {
    filename: '[name].[contenthash:20].esm.js',
    chunkFilename: '[name].[chunkhash:20].esm.js',
    hashFunction: 'xxhash64',
    pathinfo: false,
    crossOriginLoading: false,
    clean: true,
    publicPath: 'auto',
  },

  resolve: {
    extensions: ['.tsx', '.ts', '.jsx', '.js', '.json'],
  },

  devServer: {
    port: 3011,
    historyApiFallback: true,
  },

  module: {
    rules: [
      {
        test: /\.m?js/,
        type: 'javascript/auto',
        resolve: {
          fullySpecified: false,
        },
      },
      {
        test: /\.(css|s[ac]ss)$/i,
        use: ['style-loader', 'css-loader', 'postcss-loader'],
      },
      {
        test: /\.(ts|tsx|js|jsx)$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
        },
      },
      {
        test: /\.svg$/i,
        issuer: /\.[jt]sx?$/,
        use: ['@svgr/webpack'],
      },
      {
        test: /\.(?:ico|gif|png|jpg|jpeg)$/i,
        type: 'asset/resource',
      },
      {
        test: /\.(woff(2)?|eot|ttf|otf|svg|)$/,
        type: 'asset/inline',
      },
    ],
  },
  plugins: [
    new ModuleFederationPlugin({
      name: 'sidebar',
      filename: 'remoteEntry.js',
      remotes: {},
      exposes: {
        './SideBar': './src/components/Sidebar/index.tsx',
      },
      shared: {
        ...deps,
        react: {
          singleton: true,
          requiredVersion: deps.react,
        },
        'react-dom': {
          singleton: true,
          requiredVersion: deps['react-dom'],
        },
      },
    }),
    new HtmlWebPackPlugin({
      template: './src/index.html',
    }),
  ],
};

babelrc file:

{
  "presets": ["@babel/preset-typescript", "@babel/preset-react", "@babel/preset-env"],
  "plugins": [
    ["@babel/transform-runtime"],
    "babel-plugin-styled-components"
  ]
}
Fasciate answered 10/4, 2022 at 20:15 Comment(0)
S
6

From your webpack rules configuration, it looks like you’re having a name clash with the last rule with type: "asset/inline", which is handling svg as well according to your test case.

To fix this, you can either remove svg in the last rule so that it becomes

test: /\.(woff(2)?|eot|ttf|otf|)$/,

or you can configure webpack to use both rules with an additional constraint, as described in the SVGR docs. This is recommended if you need the file contents at some other point in your application. For this, add

resourceQuery: /url/, // *.svg?url

To the last rule and

resourceQuery: { not: [/url/] }, // exclude react component if *.svg?url

To your @svgr/webpack rule.

Serica answered 10/4, 2022 at 20:36 Comment(5)
i was about to write the same answer. my guess is that the first option is the right one - removing the svg from the last rule in webpack.config file.Osmosis
@Serica I have another problem. I created a UI Library with CRA. The library is published on npm. Components in the library use SVG, which I use with the syntax of - import {ReactCompoent as Icon} from 'assets / icon' After I install the library and import the component, the app crashes. With an error of: export 'ReactComponent' (imported as 'CloseIcon') was not found in '../../assets/sm/xsmall-icon.svg' (possible exports: default) can you please assist?Fasciate
@Serica i post a question - #71826044Fasciate
Posting a new question is always a good option. There, it is easier to help you and collect all the necessary information in one place.Serica
@bugkiller, I think the "right" option depends on the overall setup of the project. If there is no need to read SVGs as raw data, I totally agree with your point. However, if for some reason project components might depend on SVG raw data (as data URL as seen in the error message), this will break anyway by the introduced changes, and the second option is less intrusive. Since there was not enough evidence given on the usage, I posted both possibilities.Serica

© 2022 - 2024 — McMap. All rights reserved.