1

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?

Walter Monecke
  • 2,386
  • 1
  • 19
  • 52

0 Answers0