Search code examples
javascriptreactjssvgwebpackstorybook

Storybook: Failed to execute 'createElement' on svg files using @svgr/webpack


My error in the first place is exactly the same as the one described here. I'm using the @svgr/webpack package to import my .svg files as a React component like that:

import Shop from './icons/shop.svg'

return <Shop />

It works fine on my app but when I tried to do the same in Storybook I get this error:

Failed to execute 'createElement' on 'Document': The tag name provided ('static/media/shop.61b51e05.svg') is not a valid name.

So I added the loader into my .storybook/main.js file to extends the default Storybook webpack config:

// ...
webpackFinal: async config => {    
    config.module.rules.push({
      test: /\.svg$/,
      enforce: 'pre',
      loader: require.resolve('@svgr/webpack'),
    });

The error still occurred so I tried to override the default Storybook test for .svg files as suggested in the answer of the previous question:

const fileLoaderRule = config.module.rules.find(rule => { rule.test !== undefined ? rule.test.test('.svg') : '' });
fileLoaderRule.exclude = /\.svg$/;

But then I get this error:

TypeError: Cannot set property 'exclude' of undefined

So I decided to make a console.log of the rule.test and strangely the only default tests coming from Storybook config are theses:

{
  test: /\.md$/,
  ...
}
{
  test: /\.(js|mjs|jsx|ts|tsx)$/,
  ...
}
{
  test: /\.js$/,
  ...
}
{
  test: /\.(stories|story).mdx$/,
  ...
}
{
  test: /\.mdx$/,
  ...
}
{
  test: /\.(stories|story)\.[tj]sx?$/,
  ...
}
{
  test: /\.(ts|tsx)$/,
  ...
}

As you can see there is no test that affects a .svg file. So does someone have an idea why my configuration is not working using:

{
  test: /\.svg$/, 
  enforce: 'pre',
  loader: require.resolve('@svgr/webpack'),
}

My storybook version is 6.0.0-beta.3.


Solution

  • Your find callback always returns undefined. Change it to return correct bool:

    const fileLoaderRule = config.module.rules.find(rule => rule.test && rule.test.test('.svg'));
    

    Anyway storybook default config should have a rule for images with this test: /\.(svg|ico|jpg|jpeg|png|gif|eot|otf|webp|ttf|woff|woff2|cur|ani|pdf)(\?.*)?$/. So your code (but with correct find callback) works fine for me.

    Final main.js:

    module.exports = {
        stories: ['../src/**/*.stories.[tj]s'],
        webpackFinal: config => { 
            // Default rule for images /\.(svg|ico|jpg|jpeg|png|gif|eot|otf|webp|ttf|woff|woff2|cur|ani|pdf)(\?.*)?$/
            const fileLoaderRule = config.module.rules.find(rule => rule.test && rule.test.test('.svg'));
            fileLoaderRule.exclude = /\.svg$/;  
    
            config.module.rules.push({
              test: /\.svg$/,
              enforce: 'pre',
              loader: require.resolve('@svgr/webpack'),
            });
    
            return config;
        } 
    };