React Storybook SVG Failed to execute 'createElement' on 'Document'

I'm trying to add Storybook to an existing React app but getting errors with imported svg files. The svg is imported and used like:

import Border from './images/border.inline.svg'
...
<Border className="card__border" />

This works when the app is run and built, but I get an error in Storybook. How come?


Failed to execute 'createElement' on 'Document': The tag name provided ('static/media/border.inline.258eb86a.svg') is not a valid name.
Error: Failed to execute 'createElement' on 'Document': The tag name provided ('static/media/border.inline.258eb86a.svg') is not a valid name.

The default webpack.config.js has:

  ...
  {
    test: /\.inline.svg$/,
    loader: 'svg-react-loader'
  },
  ...

Also, the existing code uses webpack 3, and I'm using Storybook V4.


This is happening because Storybook's default webpack config has its own svg config:

{ 
  test: /\.(svg|ico|jpg|jpeg|png|gif|eot|otf|webp|ttf|woff|woff2|cur|ani)(\?.*)?$/,
  loader: 'file-loader',
  query: { name: 'static/media/[name].[hash:8].[ext]' }
},

I'm pretty sure this is the cause, because you can see the path outlined in error message: query: { name: 'static/media/[name].[hash:8].[ext]' } -> static/media/border.inline.258eb86a.svg

The solution can be to find the existing loader & change / or add an exclude rule to it. Here's an example of a custom .storybook/webpack.config.js:

// storybook 4
module.exports = (_, _, config) => {
// storybook 5
module.exports = ({ config }) => {
  const rules = config.module.rules;

  // modify storybook's file-loader rule to avoid conflicts with your inline svg
  const fileLoaderRule = rules.find(rule => rule.test.test('.svg'));
  fileLoaderRule.exclude = /\.inline.svg$/;

  rules.push({
    test: /\.inline.svg$/,
    ...
    }],
  });

  return config;
};

It appears that Storybook V6 they have changed the default Webpack config. I found that the above answers didn't work for me.

They no longer have an SVG rule, therefore testing for SVG will either error or return back undefined.

There is a oneOf rule on the module.rules which contains a loader without a test as the last rule:

{
      loader: '/Users/alexwiley/Work/OneUp/resources/client/node_modules/react-scripts/node_modules/file-loader/dist/cjs.js',
      exclude: [Array],
      options: [Object]
}

This is the culprit, you need to make sure that the file load is excluding all inline SVG file otherwise it will error.

Add the following to your .storybook/main.js file:

webpackFinal: async(config, { configType }) => {
  config.module.rules.forEach((rule) => {
    if (rule.oneOf) {
      // Iterate over the oneOf array and look for the file loader
      rule.oneOf.forEach((oneOfRule) => {
        if (oneOfRule.loader && oneOfRule.loader.test('file-loader')) {
          // Exclude the inline SVGs from the file loader
          oneOfRule.exclude.push(/\.inline\.svg$/);
        }
      });
      // Push your SVG loader onto the end of the oneOf array
      rule.oneOf.push({
        test: /\.inline\.svg$/,
        exclude: /node_modules/,
        loader: 'svg-react-loader', // use whatever SVG loader you need
      });
    }
  });
  return config;
}