I have been struggling to get Storybook v5, Gatsby working with SVG loader and TS to work nicely together.
Install the gatsby-plugin-svgr plugin.
Import an SVG into a .tsx file. Gatsby should load this fine.
Run Storybook.
Storybook should run and import the SVG.
Storybook does not load. The error is:
[tsl] ERROR in ...../src/components/Header/Header.tsx(3,30)
TS2307: Cannot find module './header-background.svg'.
Child HtmlWebpackCompiler:
Asset Size Chunks Chunk Names
__child-HtmlWebpackPlugin_0 559 KiB HtmlWebpackPlugin_0 HtmlWebpackPlugin_0
Entrypoint HtmlWebpackPlugin_0 = __child-HtmlWebpackPlugin_0
[./node_modules/@storybook/core/node_modules/html-webpack-plugin/lib/loader.js!./node_modules/@storybook/core/dist/server/templates/index.ejs] 1.69 KiB {HtmlWebpackPlugin_0} [built]
[./node_modules/@storybook/core/node_modules/webpack/buildin/global.js] (webpack)/buildin/global.js 472 bytes {HtmlWebpackPlugin_0} [built]
[./node_modules/@storybook/core/node_modules/webpack/buildin/module.js] (webpack)/buildin/module.js 497 bytes {HtmlWebpackPlugin_0} [built]
[./node_modules/lodash/lodash.js] 527 KiB {HtmlWebpackPlugin_0} [built]
System:
OS: macOS 10.14.3
CPU: (8) x64 Intel(R) Core(TM) i7-4870HQ CPU @ 2.50GHz
Shell: 3.2.57 - /bin/bash
Binaries:
Node: 10.1.0 - ~/.nvm/versions/node/v10.1.0/bin/node
Yarn: 1.13.0 - ~/.nvm/versions/node/v10.1.0/bin/yarn
npm: 6.4.1 - ~/.nvm/versions/node/v10.1.0/bin/npm
Languages:
Python: 2.7.10 - /usr/local/bin/python
Browsers:
Chrome: 72.0.3626.121
Firefox: 64.0.2
Safari: 12.0.3
npmPackages:
gatsby: ^2.1.30 => 2.1.30
gatsby-link: ^2.0.15 => 2.0.15
gatsby-plugin-emotion: ^4.0.6 => 4.0.6
gatsby-plugin-glamor: ^2.0.9 => 2.0.9
gatsby-plugin-google-analytics: ^2.0.17 => 2.0.17
gatsby-plugin-manifest: 2.0.23 => 2.0.23
gatsby-plugin-offline: ^2.0.25 => 2.0.25
gatsby-plugin-sharp: ^2.0.27 => 2.0.27
gatsby-plugin-svgr: ^2.0.1 => 2.0.1
gatsby-plugin-typescript: ^2.0.0-rc.4 => 2.0.11
gatsby-remark-autolink-headers: ^2.0.16 => 2.0.16
gatsby-remark-copy-linked-files: ^2.0.10 => 2.0.10
gatsby-remark-images: ^3.0.9 => 3.0.9
gatsby-remark-prismjs: ^3.2.5 => 3.2.5
gatsby-source-filesystem: ^2.0.24 => 2.0.24
gatsby-transformer-json: ^2.1.10 => 2.1.10
gatsby-transformer-remark: ^2.3.2 => 2.3.2
gatsby-transformer-sharp: ^2.1.16 => 2.1.16
npmGlobalPackages:
gatsby-cli: 2.4.15
I have tried to update the Storybook webpack.config.js file in lots of different ways, but have not had any luck getting it working.
I have tried both the Storybook v4 and v5 notes in the Gatsby readme.
Attempt with Gatsby Storybook v5 instructions:
const path = require("path");
module.exports = ({ config }) => {
// Transpile Gatsby module because Gatsby includes un-transpiled ES6 code.
config.module.rules[0].exclude = [/node_modules\/(?!(gatsby)\/)/]
// use installed babel-loader which is v8.0-beta (which is meant to work with @babel/core@7)
config.module.rules[0].use[0].loader = require.resolve("babel-loader")
// use @babel/preset-react for JSX and env (instead of staged presets)
config.module.rules[0].use[0].options.presets = [
require.resolve("@babel/preset-react"),
require.resolve("@babel/preset-env"),
]
// use @babel/plugin-proposal-class-properties for class arrow functions
config.module.rules[0].use[0].options.plugins = [
require.resolve("@babel/plugin-proposal-class-properties"),
]
// Prefer Gatsby ES6 entrypoint (module) over commonjs (main) entrypoint
config.resolve.mainFields = ["browser", "module", "main"]
// Add typescript loader
config.module.rules.push({
test: /\.(ts|tsx)$/,
include: path.resolve(__dirname, "../src"),
loader: require.resolve("ts-loader"),
options: {
configFile: ".storybook/tsconfig.json"
}
});
config.resolve.extensions.push(".ts", ".tsx");
// Add markdown loader
config.module.rules.push({
test: /\.md$/,
include: path.resolve(__dirname, "../src"),
loader: require.resolve("raw-loader")
});
config.resolve.extensions.push(".md");
// Add svg loader
// modify storybook's file-loader rule to avoid conflicts with svgr
const fileLoaderRule = config.module.rules.find(rule => rule.test.test('.svg'));
fileLoaderRule.exclude = path.resolve(__dirname, "../src");
config.module.rules.push({
test: /\.svg$/,
include: path.resolve(__dirname, "../src"),
use: [{
loader: '@svgr/webpack',
options: {
icon: true,
},
}],
});
return config
}
@elie222 i've picked up on your issue and based on the information you supplied i have circunvented that issue and i'm able to get build with storybook and gatsby.
I'm going to break down my answer into smaller parts for better understanding:
gatsby-config.js without any options, to keep it simple.@svgr/cli package aswell to cover my basis..config.js inside .storybook folder as per Gatsby documentation here for v5.import { configure } from '@storybook/react';
// automatically import all files ending in *.stories.js
const req = require.context('../src', true, /.stories.js$/);
function loadStories() {
req.keys().forEach(filename => req(filename));
}
// Gatsby's Link overrides:
// Gatsby defines a global called ___loader to prevent its method calls from creating console errors you override it here
global.___loader = {
enqueue: () => {},
hovering: () => {},
}
// Gatsby internal mocking to prevent unnecessary errors in storybook testing environment
global.__PATH_PREFIX__ = ""
// This is to utilized to override the window.___navigate method Gatsby defines and uses to report what path a Link would be taking us to if it wasn't inside a storybook
window.___navigate = pathname => {
action("NavigateTo:")(pathname)
}
configure(loadStories, module);
webpack.config.js inside .storybook folder and added the Gatsby configuration used for v5 mentioned in here and as Gatsby uses babel under the hood and you're using typescript i added also the configuration mentioned here for typescript.module.exports = ({ config }) => {
// Transpile Gatsby module because Gatsby includes un-transpiled ES6 code.
config.module.rules[0].exclude = [/node_modules\/(?!(gatsby)\/)/]
// use installed babel-loader which is v8.0-beta (which is meant to work with @babel/core@7)
config.module.rules[0].use[0].loader = require.resolve("babel-loader")
// use @babel/preset-react for JSX and env (instead of staged presets)
config.module.rules[0].use[0].options.presets = [
require.resolve("@babel/preset-react"),
require.resolve("@babel/preset-env"),
]
// use @babel/plugin-proposal-class-properties for class arrow functions
config.module.rules[0].use[0].options.plugins = [
require.resolve("@babel/plugin-proposal-class-properties"),
]
// Prefer Gatsby ES6 entrypoint (module) over commonjs (main) entrypoint
config.resolve.mainFields = ["browser", "module", "main"]
config.module.rules.push({
test: /\.(ts|tsx)$/,
loader: require.resolve('babel-loader'),
options: {
presets: [['react-app', { flow: false, typescript: true }]],
},
});
config.resolve.extensions.push('.ts', '.tsx');
return config
}
Five-pointed_star.svg, that's nothing more, nothing less than well...a five points star, saved it inside the components folder for simplicity.StarComponentContainer.tsx inside that folder with the following code:import * as React from 'react';
import starUrl, { ReactComponent as Star } from './Five-pointed_star.svg'
const StarComponentContainer:React.FunctionComponent=()=>(
<div>
<h4>this is a star</h4>
<Star/>
</div>
);
export default StarComponentContainer;
import React from 'react';
import { storiesOf } from '@storybook/react';
import StarComponentContainer from './StarComponentContainer';
storiesOf('Star', module)
.add('StarComponent default',()=><StarComponentContainer/>)
npm run storybook. The build goes fine as you can see below:
It's when i open http://localhost:6006 that the problem pops up.
I'm presented with the following:

Created a new page called test under pages folder and a storybook story file to test it out
Page code:
import * as React from 'react';
import StarComponentContainer from '../components/StarComponentContainer';
const TestPage:React.FunctionComponent=()=>(
<div>
<h3>
This is a test page
</h3>
<StarComponentContainer/>
</div>
);
export default TestPage;
Storybook story code:
import React from 'react';
import { storiesOf } from '@storybook/react';
import TestPage from './test';
storiesOf('Test Page',module)
.add('default',()=><TestPage/>)
npm run storybook with the same outcome, the build went ok, but the same result as above.webpack.config.js to accomodate the code inside the library's gatsby-node file here, same result as above.There might be something here i'm not seeing and something else in terms of configuration might be needed. Sorry i could not be more of assistance.
What an awesome response! Thanks for taking the time. Will take a look at this soon!
@elie222 no need to thank, glad i could be of some assistance. Sorry that i could not fix the issue entirely.
Just read through it all now. Thanks for the help.
This is my attempt at getting things working:
https://github.com/elie222/elie-tech/tree/storybook
But no luck either. To see the issue at hand:
git clone [email protected]:elie222/elie-tech.git
cd elie-tech
git checkout storybook
yarn storybook
@elie222 It doesn't look like you're declaring @svgr/webpack in your dependencies; is it being loaded by Storybook? Can you go ahead and add it and see if that resolves your issue? For what it's worth, gatsby-plugin-svgr 2.0.2 resolves an incorrect (overly narrow) peer dependency and could be related.
@coreyward @svgr/webpack is added to the project's dependencies, as you can see here. @elie222 i'm currently making some tests to see if i have figured out is actually the problem here. I'll post my findings as soon as i can. Sounds good?
Sounds awesome 鉂わ笍 . This may be a problem with the latest version of Storybook. v5 just came out and there were a fair number of issues related to importing svgs/pngs on SB repo. I have posted there too:
@elie222 looks like it worked. I'm going to break down the answer into smaller bits so that you can make adjustments as you go along.
Going to start off with the typescript part and then move onto the storybook part.
custom.d.ts with the following code:declare module "*.svg" {
const content: any;
export default content;
}
{
"compilerOptions": {
"outDir": "./dist/",
"sourceMap": true,
"noImplicitAny": true,
"module": "commonjs",
"target": "esnext",
"jsx": "react",
"lib": ["dom", "esnext"]
},
"include": [
"./src/**/*"
],
"files": [
"custom.d.ts"
]
}
No more issues with svg and typescript inside the components.
static folder and have some of them replicated inside the some of the project's components folders.start-storybook -p 9001 -c .storybook -s ./static you're expecting that based on the arguments you supplied it will pick up the contents of the static folder and will automatically serve them. Based on your current setup that's not the case, because the exact folder structure will be served as is. And inside a component, for instance ArticleItem.tsx you have the following import import favorite from "./favorite.svg and with that the contents weren't being shown for me. What i did was was move all of the svg files to src/assets/icons and the images to src/assets/images. and updated the mentioned import to import Favorite from "../../assets/icons/favorite.svg";. And you'll understand why later in the comment.Onto the question at hand, to make storybook and gatsby "play nice".
@storybook/react": "^5.1.0-alpha.10 and revert it back to @storybook/react": "^5.0.3, reason why, basically it's an alpha and i'm seeing some chatter that it's still a bit brittle.@svgr/webpack loader was not being added. webpack.config.js file for storybook. Transforming the code into: const path= require('path');
const pathToInlineSvg = path.resolve(__dirname, '../src/assets/icons/');
module.exports = ({ config }) => {
// Transpile Gatsby module because Gatsby includes un-transpiled ES6 code.
config.module.rules[0].exclude = [/node_modules\/(?!(gatsby)\/)/]
// use installed babel-loader which is v8.0-beta (which is meant to work with @babel/core@7)
config.module.rules[0].use[0].loader = require.resolve("babel-loader")
// use @babel/preset-react for JSX and env (instead of staged presets)
config.module.rules[0].use[0].options.presets = [
require.resolve("@babel/preset-react"),
require.resolve("@babel/preset-env"),
]
// use @babel/plugin-proposal-class-properties for class arrow functions
config.module.rules[0].use[0].options.plugins = [
require.resolve("@babel/plugin-proposal-class-properties"),
]
// Prefer Gatsby ES6 entrypoint (module) over commonjs (main) entrypoint
config.resolve.mainFields = ["browser", "module", "main"]
config.module.rules.push({
test: /\.(ts|tsx)$/,
loader: require.resolve('babel-loader'),
options: {
presets: [['react-app', { flow: false, typescript: true }]],
},
});
config.resolve.extensions.push('.ts', '.tsx');
// svg with @svgr
const fileLoaderRule = config.module.rules.find(rule => rule.test.test('.svg'));
fileLoaderRule.exclude = pathToInlineSvg;
config.module.rules.push({
test: /\.svg$/,
include: pathToInlineSvg,
use: [{
loader: '@svgr/webpack',
options: {
icon: true,
},
}],
});
//
return config
}
Focusing on the important parts of the code here:
const pathToInlineSvg = path.resolve(__dirname, '../src/assets/icons/');, like i said above, as i moved all the svg files inside /src/assets/icons/, so this const will hold an array with all of them. const fileLoaderRule = config.module.rules.find(rule => rule.test.test('.svg'));
fileLoaderRule.exclude = pathToInlineSvg;
config.module.rules.push({
test: /\.svg$/,
include: pathToInlineSvg,
use: [{
loader: '@svgr/webpack',
options: {
icon: true,
},
}],
});
When it reaches here, it will exclude the out of the box svg support for the svgs in the above array and adds a new rule for loading svgs, but this time with @svgr/webpack.
ArticleItem.tsx is modified to the following, i left out the majority of the code to focus on the important parts:// import favorite from "./favorite.svg"
import Favorite from "../../assets/icons/favorite.svg";
export default (props: ArticleItemProps) => {
return (
<Wrapper href={props.link} target="_blank">
......
<Main>
<Bottom>
<Likes>
{props.likes || 0}
{/* <LikesIcon src={favorite} /> */}
<Favorite/>
</Likes>
<Tags>{props.tags.map((tag) => `#${tag.toLowerCase()}`).join(", ")}</Tags>
</Bottom>
</Main>
</Wrapper>
)
}
npm run storybook now yelds the following:
Sorry for the extremely long post, hope i could shed some insights and helpd solve your issue.
Thanks so much for the help. Working through this now, but just fixing the TS import and reverting to SB 5.0.3 has got SB loading again at least!
Can I buy you a coffee (or 3), on something like: https://www.buymeacoffee.com/? Or send you a tip in Bitcoin/Eth?
Also, I didn鈥檛 quite understand what you meant about me exposing items to graphql. I do load the data on the homepage using graphql so that I can use gatsby image. Is there another place I鈥檓 doing this for no need?
@elie222 i'm glad i could be of assistance. No need for the coffee but thanks for the offer. Also ok...i didn't go through the code with a fine tooth comb, just focused on the storybook part, so disregard the exposing items to graphl part of the comment.
Most helpful comment
@elie222 looks like it worked. I'm going to break down the answer into smaller bits so that you can make adjustments as you go along.
Going to start off with the typescript part and then move onto the storybook part.
So, to make typescript and svg "play nice", in the root of the project i've created a file called
custom.d.tswith the following code:No more issues with svg and typescript inside the components.
staticfolder and have some of them replicated inside the some of the project's components folders.I understood your reasoning, when you issue
start-storybook -p 9001 -c .storybook -s ./staticyou're expecting that based on the arguments you supplied it will pick up the contents of thestaticfolder and will automatically serve them. Based on your current setup that's not the case, because the exact folder structure will be served as is. And inside a component, for instanceArticleItem.tsxyou have the following importimport favorite from "./favorite.svgand with that the contents weren't being shown for me. What i did was was move all of the svg files tosrc/assets/iconsand the images tosrc/assets/images. and updated the mentioned import toimport Favorite from "../../assets/icons/favorite.svg";. And you'll understand why later in the comment.Also in that "department"(pardon the bad pun), i saw that you're making the folder available to graphql and if you absolutely need it, fine by me, just ignore this part of the comment. But as a good practice with gatsby, items inside the folder are already available to you directly, so you shouldn't need to add a extra layer to your code. More on that here.
Onto the question at hand, to make storybook and gatsby "play nice".
@storybook/react": "^5.1.0-alpha.10and revert it back to@storybook/react": "^5.0.3, reason why, basically it's an alpha and i'm seeing some chatter that it's still a bit brittle.@svgr/webpackloader was not being added.webpack.config.jsfile for storybook. Transforming the code into:Focusing on the important parts of the code here:
const pathToInlineSvg = path.resolve(__dirname, '../src/assets/icons/');, like i said above, as i moved all the svg files inside/src/assets/icons/, so this const will hold an array with all of them.When it reaches here, it will exclude the out of the box svg support for the svgs in the above array and adds a new rule for loading svgs, but this time with
@svgr/webpack.ArticleItem.tsxis modified to the following, i left out the majority of the code to focus on the important parts:npm run storybooknow yelds the following:Sorry for the extremely long post, hope i could shed some insights and helpd solve your issue.