I am trying to set up dynamic svg imports since my app uses many icons and I don't want to impact startup time to load all icons i.e. load them only when used.
I have been following other SO questions and implemented something similar to this answer in a React + Webpack project.
I am trying to implement the same hook in Preact + Vite:
dynamic import hook
import { FunctionComponent } from 'preact';
import { useEffect, useRef, useState } from 'preact/hooks';
type Props = {
name: string;
};
export const useDynamicSVGImport = ({ name }: Props) => {
const ImportedIconRef = useRef<
FunctionComponent<SVGSVGElement> | undefined
>();
const [loading, setLoading] = useState(false);
const [error, setError] = useState<Error>();
useEffect(() => {
setLoading(true);
const importIcon = async (): Promise<void> => {
try {
const imported = await import(`../../assets/icons/${name}.svg`).then(
module => module.default,
);
// logs: /src/assets/icons/spinner.svg if I pass name = spinner
console.log('imported', imported);
ImportedIconRef.current = imported;
} catch (err) {
console.log(err);
setError(err);
} finally {
setLoading(false);
}
};
importIcon();
}, [name]);
return {
loading,
error,
SvgIcon: ImportedIconRef.current,
};
};
Note: If I pass SvgIcon
to the src
of an img
tag it renders the icon properly so it is being imported.
icon.tsx
import { FunctionComponent } from 'preact';
import { useDynamicSVGImport } from '~/hooks/icons/use-dynamic-import';
type Props = {
name: string;
className?: string;
isOutlineIcon?: boolean;
};
export const Icon: FunctionComponent<Props> = ({
name,
className,
isOutlineIcon = false,
...restProps
}) => {
const { error, loading, SvgIcon } = useDynamicSVGImport({ name });
if (error || !SvgIcon || loading) {
return null;
}
return (
<SvgIcon
className={`
fill-slate-400
${isOutlineIcon ? '' : 'fill-current'}
${className ? className : 'h-5 w-5 text-lightBlack'}`}
{...restProps}
/>
);
};
When I render my <Icon name="spinner" />
I get the following error:
Uncaught (in promise) DOMException: Failed to execute 'createElement' on 'Document':
The tag name provided ('/src/assets/icons/spinner.svg') is not a valid name.
What am I missing to properly render my svg
component?