Integrating Gatsby, Tailwind, and Storybook
A guide to the quick and easy setup of Storybook and Tailwind within Gatsby.
Gist: https://gist.github.com/romansorin/916cf914b90456f0ea584847d0db2db7
One of my current projects is dedicated to documenting and describing UX research that I’ll be conducting within the coming weeks. In building this project, I got the idea to use Tailwind, Gatsby, Firebase, and Storybook to get my hands dirty whilst still being able to build the app without too much effort.
In trying to do so, I had difficulty finding any sort of guide which covers how to use Tailwind and Gatsby with Storybook and found that traditional setup resulted in Tailwind styling to not work correctly or be loaded in. After coming to a working setup, I figured I'd release a guide to help others interested in integrating these.
Initial Setup
Start by creating a new Gatsby project:
npm install -g gatsby-cli gatsby new tailwind-gatsby-storybook cd tailwind-gatsby-storybook gatsby develop
Dependencies
You'll need to install the associated Tailwind and Storybook dependencies, as per the Gatsby documentation. We'll start with installing TailwindCSS, the utility-first CSS framework that is, in my opinion, the best framework out there.
Tailwind setup
npm install tailwindcss --save-dev npx tailwind init
This will install the framework and create a tailwind.config.js
file, which can be used to theme and extend the framework.
Next, we'll setup PostCSS to work with Gatsby and Tailwind directive loading:
npm install --save gatsby-plugin-postcss
In your gatsby-config.js
file, modify the plugins property to include this plugin:
// gatsby-config.js plugins: [`gatsby-plugin-postcss`];
Create a postcss.config.js
file in the root of your project:
// postcss.config.js module.exports = () => ({ plugins: [require("tailwindcss")], });
Now that the initial setup is completed, we can install Storybook for Gatsby. Later in the tutorial, we'll create/modify our app.css
file (or whatever you intend on using) to utilize the Tailwind directives.
Storybook
Start by making sure you have the CLI installed. In the root of your project, run:
npm install -g @storybook/cli sb init
This will create a .storybook
folder in your project root which will contain storybook config files:
.storybook/ - addons.js - config.js
Edit the config.js
file found in the .storybook
directory:
import { configure } from "@storybook/react"; import { action } from "@storybook/addon-actions"; // We will address this later. import "../src/components/layout.css"; // automatically import all files ending in *.stories.js configure(require.context("../src", true, /\.stories\.js$/), module); // 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); };
Note: It's important to pay attention to the directory specified in the configure(require.context(), module)
statement. This will recursively scan the specified directory and import all *.stories.js
files. For this tutorial (and as recommended by Gatsby docs) we've moved all stories into src
.
Next, create a webpack.config.js
within the .storybook
directory:
const path = require("path"); module.exports = ({ config }) => { config.module.rules.push({ test: /\.css$/, use: [ // Loader for webpack to process CSS with PostCSS { loader: "postcss-loader", options: { sourceMap: true, config: { path: "./.storybook/", }, }, }, ], include: path.resolve(__dirname, "../"), }); // 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"), ]; config.module.rules[0].use[0].options.plugins = [ // use @babel/plugin-proposal-class-properties for class arrow functions require.resolve("@babel/plugin-proposal-class-properties"), // use babel-plugin-remove-graphql-queries to remove static queries from components when rendering in storybook require.resolve("babel-plugin-remove-graphql-queries"), ]; // Prefer Gatsby ES6 entrypoint (module) over commonjs (main) entrypoint config.resolve.mainFields = ["browser", "module", "main"]; return config; };
This is almost identical to the configuration provided at the Gatsby docs, but includes the PostCSS loader. If you're using Typescript in your project, refer to the Gatsby docs on proper setup.
At this point, your project structure will look something like this (only including relevant files):
tailwind-gatsby-storybook/ ├── .storybook/ ├── addons.js ├── config.js ├── taildwind.config.js ├── src/ ├── components/ ├── stories/ ├── Examples.stories.js ├── tailwind.config.js ├── postcss.config.js ├── gatsby-config.js
Note that we haven't created the necessary CSS files for directives yet, so that is not included here.
Final Setup
We'll now need to create our app's main CSS file to hold our directives in, and make sure it's referenced correctly in our config files.
Start by creating a layout.css
file (or similarly named):
touch ./src/components/layout.css
Modify the file:
@tailwind base; @tailwind components; @tailwind utilities;
Check that the reference to this file in .storybook/config.js
is set correctly:
import "../src/components/layout.css";
This should point directly to the file.
And that's it! You can now build Storybook stories and use Tailwind + React inside of it.
Conclusion
Given the popularity and recent growth of things like Gatsby, Tailwind, and Storybook, I figured it would pay off to be able to integrate all of these properly. Having found both Tailwind and Gatsby to be super helpful, rapid tools for development and MVP in isolated environments, this integration with Storybook for UI testing/style guide can increase the perceived productivity tenfold.