vite-plugin-svgr icon indicating copy to clipboard operation
vite-plugin-svgr copied to clipboard

How to dynamically introduce it after version 4.0

Open qiaogaolong opened this issue 1 year ago • 17 comments

How to dynamically introduce it after version 4.0?

qiaogaolong avatar Sep 20 '23 08:09 qiaogaolong

I am also getting unexpected behaviour when trying to dynamically import. The import returns a path string instead of a React component.

I was playing with a stack blitz from this article that was demoing how to work with vite/svgr and dynamic imports. It references an outdated version of this plugin so I forked the dynamic import stack blitz and updated all the packages. As you can see in this minimal repro the plugin appears to be returning a path string to the component instead of a React component which causes React to blow up.

Stack blitz

alexbaulch avatar Oct 04 '23 14:10 alexbaulch

I'm also having errors since version 4. My test with vitest returns this error on all my tests that contains a component SVG :

'Warning: React.jsx: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: %s.%s%s'

No problem on version 3.

brazillierjo avatar Oct 10 '23 09:10 brazillierjo

Same issue here. Dynamic imports do not work as described by @alexbaulch

SetupCoding avatar Oct 25 '23 10:10 SetupCoding

I am also getting unexpected behaviour when trying to dynamically import. The import returns a path string instead of a React component.

It seems caused by the iconName variable, which can't be determined at compile time.

Is this kind of usage working at v3.0?

pd4d10 avatar Oct 25 '23 15:10 pd4d10

I fixed the above problem by:

vite.config.ts image

useDynamicSvg.ts image

I hope my way will help you!!!

trungpham71198 avatar Oct 26 '23 14:10 trungpham71198

@pd4d10 Yes, it's working before v4. The v4 change breaks the dynamic import. The imported value is the path handled by vite instead of the React component by this plugin, even ?react is added at the end of the string.

Regarding @trungpham71198 's solution, I can't take it since I sometimes still use the normal .svg import to avoid the React component wrapper by this plugin.

This critical is preventing me from upgrading to Vite 5, since it's only supported in v4.2.0 of this plugin.

Please have a look into it, cheers. 💚

xsjcTony avatar Nov 21 '23 04:11 xsjcTony

Just to add my 2 cents it' no solutions seems to work in vitev5 and it's outputing error about missing loader

like here https://stackblitz.com/edit/vitejs-vite-fz5lgu?file=src%2FSvgIcon.tsx,package.json

eMerzh avatar Nov 22 '23 09:11 eMerzh

Still a work-around, but if you still want to import normal svg's you can change the file name of svg's you do want to be picked up by svgr and change the include option accordingly:

  • icons/name.svgr.svg (added .svgr to the name)

In vite config:

svgr({
  include: '**/*.svgr.svg'
})

Then your import should also append .svgr so, something like:

await import(`path/to/${iconName}.svgr.svg`)

publicJorn avatar Nov 29 '23 09:11 publicJorn

Any official fix on this please? @pd4d10

xsjcTony avatar Dec 19 '23 02:12 xsjcTony

This issue is preventing me from upgrading to Vite5 😭

xsjcTony avatar Dec 19 '23 23:12 xsjcTony

Same issue for me, can't dynamically import SVGs after upgrading to version 4 :(

arielmints avatar Dec 20 '23 12:12 arielmints

我通过以下方式解决了上述问题:

vite.config.ts 图像

useDynamicSvg.ts 图像

希望我的方法对你有帮助!!!

Invalid in versions after 3.3.0

qinhua avatar Jan 06 '24 09:01 qinhua

Any updates?

xsjcTony avatar Feb 08 '24 23:02 xsjcTony

@trungpham71198

I hope my way will help you!!!

Thanks for your advice. For me it worked partially. I have dynamic SVGs and usual SVGs. Issue was solved with usual SVGs when import with "?react". But it was not worked for dynamic. Then I found, that after some configuration changes dynamic imports started to work, but usual SVG started to show errors about props.

So, I went to vite.config.ts and added 2 types of imports:

export default defineConfig({
  ...
  plugins: [
    ...
    // svgr options: https://react-svgr.com/docs/options/
    svgr({
      svgrOptions: { icon: true },
      include: ['**/*.svg', '**/*.svg?react'],
      exclude: [],
    }),
    ...
  ],
  ...
});

This works for me.

VikomiC avatar Feb 26 '24 21:02 VikomiC

None of the solutions are working for me.

But in any case (unrelated solution, but), as an alternative approach. what's stopping you from just defining:

// icons/index.ts
export { default as IconUp } from "./up.svg?react";
export { default as IconDown } from "./down.svg?react";
export { default as IconLeft } from "./left.svg?react";
export { default as IconRight } from "./right.svg?react";

And then just defining a map:

import type { ReactNode } from "react";
import { IconUp, IconDown, IconLeft, IconRight } from "./icons";

const dynamicIcons: Record<string,  ReactNode> = {
   "up": <IconUp />,
   "down": <IconDown />,
   "left": <IconLeft />,
   "right": <IconRight />,
} 


// And then using it in your jsx like:

let myDynamicValue = "up" | "down" | "left" | "right";

{dynamicIcons[myDynamicValue]}

TPLCarloTaleon avatar Sep 12 '24 16:09 TPLCarloTaleon

what's stopping you from just defining:

You have defined list of icons. In my case I'm using a library with tons of icons (as an example, currencies icons). My app can have any currency in the list. I want to get them dynamically, not to list all of them in the imports.

Simple imports works. But dynamic - not :(.

VikomiC avatar Sep 12 '24 18:09 VikomiC

WAIT! I stand corrected! https://github.com/pd4d10/vite-plugin-svgr/issues/90#issuecomment-1965316849 - This indeed works lol.

Also @VikomiC. The reason why static imports import TestIcon from "@/components/icons/test.svg" might not work for you anymore is because you should actually copy the contents of vite-plugin-svgr/client and override the regular .svg imports as well from string to ReactComponent like so:

// icon.d.ts (in your root folder)

/**
 * So import .svg becomes a ReactComponent instead of a string. Enabled by `vite-plugin-svgr`.
 */
declare module "*.svg" {
  import * as React from "react";

  const ReactComponent: React.FunctionComponent<
    React.ComponentProps<"svg"> & { title?: string }
  >;

  export default ReactComponent;
}

✅ Anyway, here's the whole code that works:

// icon.tsx
type IconSVG =
  | "caret-down"
  | "chart"
  | "copy"; // extend as you wish.

export type IconProps = {
  src: IconSVG;
} & React.SVGProps<SVGSVGElement>;

export const Icon = ({ src, ...svgProps }: IconProps) => {
  const {loading, SvgIcon } = useDynamicSvgImport(src);

  if (loading) {
    return null;
  }

  if (!SvgIcon) return null;

  return <SvgIcon width="1em" height="1em" {...svgProps} />
};

/** Reference: https://stackblitz.com/edit/vitejs-vite-fz5lgu?file=src%2FSvgIcon.tsx,package.json */
export function useDynamicSvgImport(iconName: string) {
  const importedIconRef = useRef<React.FC<React.SVGProps<SVGElement>>>();
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<unknown>();

  useEffect(() => {
    setLoading(true);
    // dynamically import the mentioned svg icon name in props
    const importSvgIcon = async (): Promise<void> => {
      // please make sure all your svg icons are placed in the same directory
      // if we want that part to be configurable then instead of iconName we will send iconPath as prop
      try {
        importedIconRef.current = (
          await import(`@/assets/icons/${iconName}.svg?react`)
        ).default; // svgr provides ReactComponent for given svg path
      } catch (err) {
        setError(err);
        console.error(err);
      } finally {
        setLoading(false);
      }
    };

    importSvgIcon();
  }, [iconName]);

  return { error, loading, SvgIcon: importedIconRef.current };
}
// vite.config.ts
export default defineConfig({
  ...
  plugins: [
    svgr({
      svgrOptions: { icon: true },
      include: ['**/*.svg', '**/*.svg?react'],
      exclude: [],
    }),
  ],
  ...
});

TPLCarloTaleon avatar Sep 14 '24 20:09 TPLCarloTaleon