Svgr: Dynamically load SVGs with Parcel and promise

Created on 4 Apr 2019  ·  6Comments  ·  Source: gregberge/svgr

💬 Questions and Help

SVGR works beautifully with a single static SVG file with Parcel
https://www.smooth-code.com/open-source/svgr/docs/parcel/
However, when I try to load SVG dynamically according to user's interaction I face some difficulites.
I also checked the doc of Parcel https://parceljs.org/code_splitting.html, so I mimicked the syntax and did something like this in a class component:

const floors = {
  '111': import('./assets/svg/floor_plan.svg'),
  '222': import('./assets/svg/floor_plan_2.svg')
};

  renderFloor = async (name) => {
      const Floor = await floors[name];
      console.log(Floor);
      return <Floor />;
  };

  render() {
    return <React.Fragment>{this.renderFloor('222')}</React.Fragment>;
  }

console.log(Floor); returns something like this

{default: ƒ, __esModule: true}
default: ƒ SvgFloorPlan2(props)
__esModule: true
__proto__: Object

If I just do a easyimport Floor from './assets/svg/floor_plan_2.svg' like the doc andconsole.log(Floor), I got this

ƒ SvgFloorPlan2(props) {
  return _react.default.createElement("svg", _extends({
    className: "floor_plan_2_svg__floor_plan",
    viewBox: "-1944.66 -1944.66 159462 126395"
  }, props), _ref, _ref2);…

I wonder how to dynamically load SVG at run-time, thank you very much!

All 6 comments

Hello @chloesun, with dynamic import, you have to specify that you want the default export.

const floors = {
  '111': import('./assets/svg/floor_plan.svg'),
  '222': import('./assets/svg/floor_plan_2.svg')
};

  renderFloor = async (name) => {
      const Floor = (await floors[name]).default; // the fix
      console.log(Floor);
      return <Floor />;
  };

  render() {
    return <React.Fragment>{this.renderFloor('222')}</React.Fragment>;
  }

I am wondering if dynamic imports really work with the @svgr/webpack setup. I tried it and it says You may need an appropriate loader to handle this file type. After searching about it, I found this issue where it should work and this other issue where it doesn't work.

My code:

const Item = ({ iconString }) => {
  const Icon = import(`../../icons/${iconString}.svg`).then(
    module => module.default,
  );
  return (
    <div>
      <Icon height="60px" width="60px" />
    </div>
  );
};

Can I get this to work with dynamic imports or is it not yet supported? :)

If this can be done with dynamic imports, I think I need to import it somehow with { ReactComponent } though, but not sure how it would work then. Maybe someone can help here ❤️

@rwieruch

Sorry to bother, but any chance you have figured out to dynamic import SVG in CRA with { ReactComponent } in mind? 😌

@adriandmitroca Unfortunately not. TBH I didn't pursue this any further after I didn't get to any solution.

@rwieruch

I figured out a reasonable solution, which is basically using normal dynamic import that resolves to SVG url and then use react-inlinesvg afterwards, like this:

import SVG from 'react-inlinesvg';

useEffect(() => {
  async function loadIcon() {
    if (icon) {
      setIconComponent(<SVG src={(await icon).default} />);
    }
  }

  loadIcon();
}, [icon]);

Doing like this.

import * as React from "react";
import {SvgIcon, SvgIconProps} from "@material-ui/core";

const importAll = (r: any) => {
    const images: any = {};
    r.keys().forEach((item: any) => { images[item.replace('./', '').replace(/\.(svg)$/, "")] = r(item).default; });
    return images;
};

export const dynamicIcons = importAll(require.context('!@svgr/webpack!../../assets/icons', false, /\.(svg)$/));

type CustomSvgIconProps = Omit<SvgIconProps, "component"> & { icon: string; };

const CustomSvgIcon = ({icon, ...rest}: CustomSvgIconProps) => (
    <SvgIcon component={dynamicIcons[`${icon}`]} {...rest}/>
);

Or

import * as React from "react";
import {SvgIcon, SvgIconProps} from "@material-ui/core";

const importAll = (r: any) => {
    const images: any = {};

    r.keys().forEach((key: any) => {
        const svgFile = key.replace('./', '');
        const svgName = svgFile.replace(/\.(svg)$/, "");

        import(/* webpackMode: "eager" */ `!@svgr/webpack!../../assets/icons/${svgFile}`).then(module => {
            images[svgName] = module.default;
        });
    });

    return images;
};

dynamicIcons = importAll(require.context('../../assets/icons', false, /\.(svg)$/));

type CustomSvgIconProps = Omit<SvgIconProps, "component"> & { icon: string; };

const CustomSvgIcon = ({icon, ...rest}: CustomSvgIconProps) => (
    <SvgIcon component={dynamicIcons[`${icon}`]} {...rest}/>
);

export default CustomSvgIcon;
Was this page helpful?
0 / 5 - 0 ratings

Related issues

kg-currenxie picture kg-currenxie  ·  4Comments

Vadorequest picture Vadorequest  ·  4Comments

lm93547 picture lm93547  ·  3Comments

alamothe picture alamothe  ·  6Comments

troch picture troch  ·  5Comments