vite-plugin-svgr
vite-plugin-svgr copied to clipboard
How to dynamically introduce it after version 4.0
How to dynamically introduce it after version 4.0?
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.
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.
Same issue here. Dynamic imports do not work as described by @alexbaulch
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?
I fixed the above problem by:
vite.config.ts
useDynamicSvg.ts
I hope my way will help you!!!
@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. 💚
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
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`)
Any official fix on this please? @pd4d10
This issue is preventing me from upgrading to Vite5 😭
Same issue for me, can't dynamically import SVGs after upgrading to version 4 :(
我通过以下方式解决了上述问题:
vite.config.ts
useDynamicSvg.ts
希望我的方法对你有帮助!!!
Invalid in versions after 3.3.0
Any updates?
@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.
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]}
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 :(.
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: [],
}),
],
...
});