Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
97 lines (69 sloc) 3.88 KB
date title template thumbnail slug categories tags
2020-01-08
Import SVG Components in Storybook
post
../thumbnails/storybook.png
import-svg-storybook-webpack-loader
Storybook
storybook
emotion
css-in-js
svg
webpack

Storybook Not Importing SVGs

I was running into an issue where SVG imports in Storybook were undefined instead of being imported as React components.

import React from "react";
import { ReactComponent as ArrowUpIcon } from "~/icons/icon-up-arrow.svg";

console.log(ArrowUpIcon); // undefined
Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined.

You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.

Invariant Violation: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined.

Alternative Error: Failed to execute 'createElement' on 'Document'

I also came across this Stack Overflow thread where SVG imports result in an incorrect SVG path: React Storybook SVG Failed to execute 'createElement' on 'Document'

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 fix is the same - setting up a custom Storybook webpack config.

Storybook Default webpack Config Conflict

Storybook has a default webpack config which specifies file-loader for SVG assets which is the cause of the incorrectly imported SVGs.

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

Setting Up .storybook/webpack.config.js

A custom webpack config for Storybook needs to be set up to specify @svgr/webpack as a webpack loader for .svg assets.

The @svgr/webpack loader rule needs to occur before that other loaders like the file-loader rule so that @svgr/webpack takes precedence.

Place the @svgr/webpack loader before existing webpack asset loaders using Array.prototype.unshift().

.storybook/webpack.config.js
// Add SVGR Loader
// ========================================================
const assetRule = config.module.rules.find(({ test }) => test.test(".svg"));

const assetLoader = {
  loader: assetRule.loader,
  options: assetRule.options || assetRule.query
};

// Merge our rule with existing assetLoader rules
config.module.rules.unshift({
  test: /\.svg$/,
  use: ["@svgr/webpack", assetLoader]
});

// ... other configs

Gatsby Starter: TypeScript + Emotion + Storybook

If you want to see the full config in a project, I created a Gatsby Starter that uses Gatsby + TypeScript + Emotion + Storybook + React Intl + SVGR + Jest.

Check it out at: Gatsby Starter: TypeScript + Emotion + Storybook

You can’t perform that action at this time.