gregberge / svgr

Transform SVGs into React components 🦁
https://react-svgr.com
MIT License
10.51k stars 416 forks source link

SVGR adds svg code to .js when bundled with webpack 5 #806

Open chetankondawle opened 1 year ago

chetankondawle commented 1 year ago

I have component library which contains multiple svgs which is built using SVGR. This component library is being consumed by my-app. When my-app is bundled with webpack 5 it creates .js file for this component library and it has the svg code in it, there by increasing .js file size in final bundle.

Component Library Config

module.exports = {
  stories: ["../src/**/*.stories.mdx", "../src/**/*.stories.@(js|jsx|ts|tsx)"],
  addons: [
    "@storybook/addon-links",
    "@storybook/addon-essentials",
    "@storybook/addon-interactions",
  ],
  framework: "@storybook/react",
  core: {
    builder: "webpack5",
  },
  features: {
    emotionAlias: false,
  },
  webpackFinal: async (config, { configType }) => {
    const fileLoaderRule = config.module.rules.find(
      (rule) => !Array.isArray(rule.test) && rule.test.test(".svg")
    );
    console.log(config.module.rule);
    fileLoaderRule.exclude = /\.svg$/;
    config.module.rules.push({
      test: /\.svg$/,
      use: ["@svgr/webpack", "url-loader"],
    });
    return config;
  },
};

my-app Webpack Config

module.exports = {
  module: {
    rules: [
      {
        test: /\.svg$/,
        use: [
          {
            loader: require.resolve("@svgr/webpack"),
            options: {
              prettier: false,
              svgo: false,
              svgoConfig: {
                plugins: [{ removeViewBox: false }],
              },
              titleProp: true,
              ref: true,
            },
          },
          {
            loader: require.resolve("file-loader"),
            options: {
              name: "static/media/[name].[hash].[ext]",
            },
          },
        ],
        issuer: {
          and: [/\.(ts|tsx|js|jsx|md|mdx)$/],
        },
      },
      {
        test: /\.(js|mjs|jsx|ts|tsx)$/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader",
          options: {
            presets: [
              [
                "@babel/preset-env",
                {
                  targets: {
                    esmodules: true,
                  },
                },
              ],
              ["@babel/preset-react", { runtime: "automatic" }],
              "@babel/preset-typescript",
            ],
            plugins: [
              "@babel/plugin-proposal-class-properties",
              "@babel/plugin-proposal-object-rest-spread",
            ],
          },
        },
      },
    ],
  },
  plugins: [new ForkTsCheckerWebpackPlugin()],
  optimization: {
    chunkIds: "named",
  },
};

Svg file is consumed as a react component like this.

declare module "*.svg" {
  import * as React from "react";

  export const ReactComponent: React.FunctionComponent<
    React.SVGProps<SVGSVGElement> & { title?: string }
  >;

  const src: string;
  export default src;
}
import { ReactComponent as Logo } from "./logo.svg";

In final bundle svg code is added in the .js file and seperate .svg file is also loaded. How to avoid svg code from being included in .js file?

pepew-le-boss commented 1 year ago

Up

abhishekoza commented 1 year ago

Looking for the same solution.

stale[bot] commented 1 year ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

mikirejf commented 1 year ago

Using ref: true svgr option includes all imported svgs as react components, even if the react component wasn't explicitly imported. I don't think this is intended behavior.

For example, passing ref: true option and having the following import

import svg from './logo.svg';

will still generate a react component of logo.svg and include it in the bundle.

gregberge commented 1 year ago

@mikirejf I don't understand what is exactly the problem.

mikirejf commented 1 year ago

@gregberge

Let's say we have the following react code:

import logo from './logo.svg';

function App() {
  return <img src={logo} />;
}

With default SVGR options, a bundler (in my case it's Vite) produces the following (or similar code):

const logo = "/static/logo-a072c053.svg";

If we then use ref: true option, the bundler, in addition to the url, also produces the JSX, even if it's not used or referenced anywhere in the code:

const SvgLogo = (props, ref) => /* @__PURE__ */ reactExports.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: 91, height: 37, viewBox: "0 0 91 37", ref, ...props }, /* @__PURE__ */ reactExports.createElement("g", null, /* @__PURE__ */ reactExports.createElement("g", null, /* @__PURE__ */ reactExports.createElement("path", { fill: "#fff", d: "M58.27 ... 73.052" }))));
reactExports.forwardRef(SvgLogo);
const logo = "/static/logo-a072c053.svg";

This leads to bundles being heavier than they should be.

Since the OP is using Webpack and I'm using Vite, I'm assuming there's a bug(?) in SVGR. I hope that clarifies it.

gregberge commented 1 year ago

@abhishekoza got it thanks, only with ref option activated?

mikirejf commented 1 year ago

@gregberge It also happens when memo: true.