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.
{
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 thefile-loader
rule so that@svgr/webpack
takes precedence.
Place the @svgr/webpack
loader before existing webpack asset loaders using Array.prototype.unshift()
.
// 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