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.
yarn create next-app
That's it. Done. A fully-functional NextJS app will be created using the official starter kit.
yarn add -D tailwindcss postcss-preset-env
to install the TailwindCSS library and some useful PostCSS polyfillsnpx tailwind init
to generate a tailwind.config.js
file.module.exports = {
purge: ['./components/**/*.tsx', './pages/**/*.tsx'],
theme: {
extend: {},
},
variants: {},
plugins: [],
}
postcss.config.js
to configure PostCSS in a minimal way, like the followingmodule.exports = {
plugins: [
"tailwindcss",
"postcss-preset-env"
]
};
/styles/index.css
and populate it using the postcss-import
-friendly @import
directives (instead of using @tailwind
)@import "tailwindcss/base";
@import "tailwindcss/components";
@import "tailwindcss/utilities";
yarn add --dev typescript @types/react @types/node
touch tsconfig.json
yarn next
to start NextJS, which will automagically recognize our newly created tsconfig.json
and inject a valid configuration json into it/pages/_app.tsx
fileimport React from "react";
import "../styles/index.css"; // <- applied everywhere in the NextJS application scope
const MyApp = ({ Component, pageProps }) => {
return <Component {...pageProps} />;
};
export default MyApp;
yarn add @storybook/react babel-loader @babel/core awesome-typescript-loader react-docgen-typescript-loader -D
mkdir .storybook
cp ./tsconfig.json ./.storybook/
/.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"
]
}
/.storybook/main.js
and feel free to copy the following configurationconst 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;
},
};
/.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";
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": {}
}
};
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