NextJS + TypeScript + TailwindCSS + Storybook project setup
Tags: nextjs, typescript, tailwindcss, storybook
Published: 2020-07-14 15:15:04

> 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

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)
```js
module.exports = {
  purge: ['./components/**/*.tsx', './pages/**/*.tsx'],
  theme: {
    extend: {},
  },
  variants: {},
  plugins: [],
}
```
4. Create `postcss.config.js` to configure PostCSS in a minimal way, like the following
```js
module.exports = {
  plugins: [
    "tailwindcss", 
    "postcss-preset-env"
  ]
};
```
5. Create `/styles/index.css` and populate it using the `postcss-import`-friendly `@import` directives (instead of using `@tailwind`)
```css
@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
```jsx
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
```json
{
  "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"
    ]
  }
```

6. Create `/.storybook/main.js` and feel free to copy the following configuration
```js
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;
  },
};
```

7. create `/.storybook/preview.js` and use it to import our stylesheet as follows
```js
// 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";
```

8. 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!
```js
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