NextJS + TypeScript + TailwindCSS + Storybook project setup

Introduction

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.

Next.JS setup

yarn create next-app

That's it. Done. A fully-functional NextJS app will be created using the official starter kit.

TailwindCSS setup

  1. yarn add -D tailwindcss postcss-preset-env to install the TailwindCSS library and some useful PostCSS polyfills
  2. npx tailwind init to generate a tailwind.config.js file.
  3. 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: [],
}
  1. Create postcss.config.js to configure PostCSS in a minimal way, like the following
module.exports = {
  plugins: [
    "tailwindcss", 
    "postcss-preset-env"
  ]
};
  1. Create /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";

Typescript SETUP

  1. yarn add --dev typescript @types/react @types/node
  2. touch tsconfig.json
  3. yarn next to start NextJS, which will automagically recognize our newly created tsconfig.json and inject a valid configuration json into it
  4. 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

  1. yarn add @storybook/react babel-loader @babel/core awesome-typescript-loader react-docgen-typescript-loader -D
  2. mkdir .storybook
  3. cp ./tsconfig.json ./.storybook/
  4. 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"
    ]
  }
  1. 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;
  },
};
  1. 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";
  1. 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": {}
  }
};

Conclusions

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

Publication date: 24/07/2020