TUTORIAL BASED ON STORYBOOK v5, ON v6 EVERYTHING STORYBOOK-RELATED WORKS OUT OF THE BOX!
Following is a simple guide to get up and running with the NextJS+TypeScript+TailwindCSS+Storybook combination, a task which took me a lot more time than I originally estimated, due to the unexpected lack of specific guides on how to deal with this particular scenario plus the sparse nature of the information I had to look up to set everything up as desired.
NEXTJS SETUP
yarn create next-app
That's it. Done. A fully-functional NextJS app will be created using the official starter kit.
TAILWINDCSS SETUP
-
yarn add -D tailwindcss postcss-preset-env
to install the TailwindCSS library and some useful PostCSS polyfills -
npx tailwind init
to generate atailwind.config.js
file. - Edit the TailwindCSS configuration file we just created to enable and configure the built-in CSS purging process. (Notice how I'm using the ".tsx'" extension here, since I already know I'm going to use TypeScript for this project)
module.exports = {
purge: ['./components/**/*.tsx', './pages/**/*.tsx'],
theme: {
extend: {},
},
variants: {},
plugins: [],
}
- Create
postcss.config.js
to configure PostCSS in a minimal way, like the following
module.exports = {
plugins: [
"tailwindcss",
"postcss-preset-env"
]
};
- Create
/styles/index.css
and populate it using thepostcss-import
-friendly@import
directives (instead of using@tailwind
)
@import "tailwindcss/base";
@import "tailwindcss/components";
@import "tailwindcss/utilities";
TYPESCRIPT SETUP
yarn add --dev typescript @types/react @types/node
touch tsconfig.json
-
yarn next
to start NextJS, which will automagically recognize our newly createdtsconfig.json
and inject a valid configuration json into it - Create a
/pages/_app.tsx
file
import React from "react";
import "../styles/index.css"; // <- applied everywhere in the NextJS application scope
const MyApp = ({ Component, pageProps }) => {
return <Component {...pageProps} />;
};
export default MyApp;
STORYBOOK SETUP
yarn add @storybook/react babel-loader @babel/core awesome-typescript-loader react-docgen-typescript-loader -D
mkdir .storybook
cp ./tsconfig.json ./.storybook/
- Edit
/.storybook/tsconfig.json
to suit your Storybook-TypeScript integration
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": false,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "react" // <- important!
},
"exclude": ["node_modules"],
"include": [ // <- important!
"../types.d.ts",
"../next-env.d.ts",
"../**/*.stories.ts",
"../**/*.stories.tsx"
]
}
- Create
/.storybook/main.js
and feel free to copy the following configuration
const path = require("path");
module.exports = {
stories: ["../components/**/**/*.stories.tsx"],
webpackFinal: async (config) => {
config.module.rules.push({
test: /\.(ts|tsx)$/,
use: [
{
loader: require.resolve("awesome-typescript-loader"),
options: {
configFileName: path.resolve(__dirname, "./tsconfig.json"),
},
},
/*
** OPTIONAL **
Basically a webpack loader used to
generate docgen information from TypeScript React components.
The primary use case is to get the prop types
table populated in the Storybook Info Addon.
*/
{
loader: require.resolve("react-docgen-typescript-loader"),
options: {
tsconfigPath: path.resolve(__dirname, "./tsconfig.json"),
},
},
],
});
config.resolve.extensions.push(".ts", ".tsx");
return config;
},
};
- create
/.storybook/preview.js
and use it to import our stylesheet as follows
// The preview application is essentially just your stories with
// a framework-agnostic 'router'.
// It renders whichever story the manager application tells it to render.
// In this case we just use it to import the stylesheet and inject it
// in the context of our stories
import "../styles/index.css";
- Update
postcss.config.js
to use an object-based format. This is super important in order to solve any issues Storybook's webpack process may encounter while resolving the dependencies!
module.exports = {
plugins: {
tailwindcss: {},
"postcss-preset-env": {}
}
};
DONE
Now you will be able to create your component stories in your own component's folders (ex.: /components/button/1-button.stories.tsx
) using this powerful toolset.
P.S.: If you wish to organize your Storybook stories using a different folder structure, you'll need to do no more than editing the
stories
property inside the exported configuration in/.storybook/main.js
using your desired glob patterns
Top comments (7)
So glad you wrote this article on my birthday. Thank you very much for writing this. I've been stuck on Storybook
postcss.config.js
webpack issue. Am quite surprised the fix is as easy as makingtailwindcss: {}
an object. My StoryBook setup is now loading tailwind styles🙏🏽Happy (belated) birthday man! As you pointed out the solution was pretty simple to implement, yet absolutely unintuitive. I'm glad I saved you some headaches!
You kick ass, thanks for the article! FINALLY figured out the missing plugin in my setup...
Thank for the positive feedback! What was the missing plugin? BTW if Storybook was the origin of your issues, I strongly suggest you try v6, which makes the integration really seamless.
Agreed--I'm on v6 but had to add the tailwind plugin to
postcss.config.js
as an object for Storybook to work, which you thankfully pointed out :-)Would have never guessed that it should be an object structure. Thanks!
Glad I helped you too!