typescriptwebpackcreate-react-appwebpack-loader

Webpack loaders in require.context on a Typescript create-react-app


I have far too many svg icons to import them all in the recommended way:

import { ReactComponent as Icon } from 'assets/svg/icon.svg'

So in my JS React apps I've been using webpack's require.context to index the entire svg directory and export it as an object, which then allows me to do this:

import { svg } from 'assets'

function SvgIcon({ icon }) {
  const Icon = svg[icon]
  return <Icon />
}

/* ... */

<SvgIcon icon='inbox' />

Which is amazing for my use case because I can load all the icons I need dynamically without even having to individually import them. However, as soon as I switched to Typescript, require.context ceased to use the svg loader. This code, which used to work perfectly before, now only returns { [filename]: "/path/to/filename.39843398.svg" }, which is what would happen if I didn't use a loader at all. That string is supposed to be a ReactComponent instead.

const assets = context =>
  context.keys().reduce((assets: any, path) => {
    const key = path.replace(/^\.\//, '').replace(/\..*$/, '')
    assets[key] = context(path).default || context(path)
    return assets
  }, {})

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

Codesandbox doesn't seem to support require.context, but you can reproduce this very easily by creating a react app with --template typescript, installing @svgr/webpack, adding assets/svg, copying the logo.svg there, making an index.ts and copypasterino the codeblocks above.

Depending on your package manager and sdk you may have to fiddle with the config a bit. I had to install @types/webpack-env and add them to tsconfig.json like so:

{
  /* ... */
  "types": ["node", "webpack-env"]
}

Solution

  • It turns out the culprit is react-scripts@5.0.0. Downgrading to @4.0.3 fixed the problem.

    Incidentally, @5.0.0 had other problems with svg files, like not converting namespace tags to proper JSX. Could be related, could be unrelated.