Search code examples
reactjswebpackstorybook

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.


Solution

  • 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;
    };