BREAKING: We just launched MeUndies on Shopify Plus. Check out the case study!

Using Tailwind and Storybook with Next.js

Andrea Vassallo

18 Dec 2020 Software Development, Design, Headless

Andrea Vassallo

8 mins
Configure NextJS with Storybook and TailwindCSS

In this article, we are going to show how you can create a NextJs project with Storybook and TailwindCSS.

Before starting, I want to explain who are the actors and why it should be better adding them to our frontend application.

NextJS

NextJS doesn’t need a lot of presentation. If you are here, you probably already know what NextJs is and why we should use it to build robust applications.

NextJs is a great React frontend framework, and it’s mainly used to build SEO-friendly applications since you can use three different types of rendering/pre-rendering methods:

  • SSG (Static-site generation) the HTML is generated at build time and will be reused on each request,

  • SSR (Server-side rendering) the HTML is generated on each request using a node server, so the response is SEO-friendly.

  • CSR (Client-side rendering) the normale React behavior, the response is simply Javascript run by the client.

TailwindCSS

Tailwind is one of the most powerful libraries to build your application style using HTML classes. TailwindCSS implements a ton of dynamic classes which apply different style rules to your application, and it is fully customizable using the configuration file!

Initially it will take time to understand which are the correct classes you need to use to build your interface, but one of the coolest thing in Tailwind is its super documentation.

By the way, the 2.0 version has recently been released with a lot of new features.

Storybook

Storybook is a great library that is mainly used to render our application components outside the NextJs environment. This means that you can check your components UI without making real queries or fetching data. You can also figure out how the application components change based on the passed props.

Storybook is more powerful when used with well-organized components, for example, using [Atomic Design(https://bradfrost.com/blog/post/atomic-web-design/).

Example

You will probably create your custom Button component in your project that will be rendered differently based on the passed props or the fetched data. With Storybook, you can check all the UI changes applying custom props from the Storybook panel or stubbing all your requests.

TL;DR

Here you can find the fully configurated project

Let’s start!

For this article, I won’t split the application into components to be more direct. It would be best to do it in a real application to avoid using the index.js page directly.

The best practice is writing components splitting them by context using a great file structure:

  • React documentation about components: link
  • Atomic Design pattern about components structure: link

Create the NextJS app

First of all, run this command to create your NextJs project:

npx create-next-app --use-npm nextjs-storybook-tailwindcss

and navigate inside the project directory with cd nextjs-storybook-tailwindcss.

Run your application

npm run dev

Refactor the default code

The create-next-app command creates the project with some unnecessary configuration since we’ll use TailwindCSS to build our style.

Replace the content in pages/index.js with:

const Home = ({ article, showImage }) => {
  if (!article) return "The article wasn't found!"

  const {author, company, image_url, content} = article

  const Image = showImage ? (<img className="w-24 h-24 rounded-full mx-auto md:flex md:self-center"
    src={image_url} alt="A beautiful cat" width="200" height="200" />) : ''

  return (
    <div className="w-96 mx-auto">
      <div className="h-screen flex items-center">
        <figure className="lg:flex bg-gray-200 rounded-xl p-8 lg:p-4">
          {Image}
          <div className="pt-6 lg:p-4 text-center lg:text-left space-y-4">
            <p className="text-lg font-semibold">{content}</p>
            <figcaption className="font-medium">
              <div className="text-blue-600">{author}</div>
              <div className="text-gray-500">{company}</div>
            </figcaption>
          </div>
        </figure>
      </div>
    </div>
  )
}

Home.defaultProps = {
  showImage: true
}

export const getServerSideProps = async () => {
  const response = await fetch('http://localhost:3000/api/article')
  const data = await response.json()

  if (!data) return { props: {}}

  return {
    props: {
      article: data
    }
  }
}

export default Home

Add your first API:

touch pages/api/article.js
const Article = (_, res) => {
  res.statusCode = 200
  res.json({
    id: 1,
    author: 'Andrea Vassallo',
    company: 'Nebulab',
    image_url: 'http://placekitten.com/200/200',
    content: 'The cat is a domestic species of small carnivorous mammal.'
  })
}

export default Article

Remove the CSS file: styles/Home.module.css:

rm styles/Home.module.css

Nextjs without style result

Man pointing thumb down

The result is an ugly and boring HTML page.

Let’s go applying our awesome style.

Install TailwindCSS

Why the documentation isn’t enough in this case?

Because NextJS came up with a bunch of pre-configured services like Babel and PostCSS and we have to TailwindCSS with them.*

Reading the TailwindCSS documentation, you’ll see that we need three different libraries to complete the installation:

  • tailwindcss

  • postcss

  • autoprefixer

We don’t need to install postcss and the autoprefixer libraries since they are already installed in NextJs.

Run these commands to install TailwindCSS:

npm install --save-dev tailwindcss

Unfortunately, we can’t use npx tailwind init to create the configuration file since it’ll check if you have installed the autoprefixer library.

Let’s create the configuration file adding the tailwind.config.js file:

touch tailwind.config.js

with the default configuration:

module.exports = {
  purge: [
    // This is not present inside the default configuration
    // but it's good to build your production application
    // Read more about this here: https://tailwindcss.com/docs/installation#building-your-css
    './pages/**/*.js',
  ],
  darkMode: false,
  theme: {
    extend: {},
  },
  variants: {
    extend: {},
  },
  plugins: [],
}

Configure PostCSS

To configure PostCSS we have to override the NextJs default one:

Install the dependencies:

npm install --save-dev postcss-flexbugs-fixes postcss-preset-env

Create the postcss.config.js file:

touch postcss.config.js

and fill it with the default configuration as well:

module.exports = {
  plugins: {
    'postcss-flexbugs-fixes': {},
    'postcss-preset-env': {
      autoprefixer: {
        flexbox: 'no-2009',
      },
      stage: 3,
      features: {
        'custom-properties': false,
      },
    },
  },
}

Add the tailwindcss plugin to the top of the plugin list:

module.exports = {
  plugins: {
    tailwindcss: {},
    ...
  },
}

Include Tailwind in your CSS

Add these three lines of code to the top of this file: styles/globals.css:

@tailwind base;
@tailwind components;
@tailwind utilities;

We made it

Check your application homepage; the result should be similar to this one:

Tailwind result

State of mind

From here on, it’ll be a breeze building beautiful component with Tailwind, but we need to answer a couple of questions about testing:

  1. How is this component rendered when the article data isn’t found?

  2. How can we check all the component variants at any moment without changing the code?

Storybook will help us to accomplish both.

Install Storybook

The default npx sb init command that is used inside the official documentation installs libraries that we don’t need and adds a bunch of files that we don’t use.

For that reason, I usually prefer to use the from scratch configuration.

npm install --save-dev @storybook/addon-actions @storybook/addon-essentials @storybook/addon-links @storybook/react

Add the Storybook commands inside the package.json scripts:

"scripts": {
  "dev": "next dev",
  "build": "next build",
  "start": "next start",
  "storybook": "start-storybook -p 6006",
  "storybook:build": "build-storybook"
}

Copy this piece of code inside the .storybook/main.js file

module.exports = {
  "stories": [
    // Paths to the story files
    "../pages/*.stories.mdx",
    "../pages/*.stories.js",
  ],
  "addons": [
    "@storybook/addon-links",
    "@storybook/addon-essentials"
  ]
}

Copy this piece of code inside the .storybook/preview.js file

// Import the global style enabling tailwind classes
import '../styles/globals.css'

export const parameters = {
  actions: { argTypesRegex: "^on[A-Z].*" },
}

Add a story

For each page or component that we want to tell using a story, we need to create a file called component-name.stories.js.

In this case, we want to create the index.stories.js inside the pages dir:

import Article from './index';

const articleMock = {
  id: 3,
  author: 'Dave Canterbury',
  company: 'Bushcraft 101',
  image_url: 'https://via.placeholder.com/200/200',
  content: 'David Michael Canterbury born September 19, 1963 is a survival expert'
}

const Story = (props) => <Article {...props} />

// Here we export a variant of the default template passing props
export const ArticleStory = Story.bind({})
ArticleStory.args = {
  article: articleMock,
};

// Here we export a variant of the default template passing props
export const EmptyArticleStory = Story.bind({})
EmptyArticleStory.args = {
  article: null,
};

// Here we export the default component that
// will be used by Storybook to show it inside the sidebar
export default {
  title: 'Article',
  component: Article,
  argTypes: {
    showImage: { control: 'boolean' },
  },
};

Run the Storybook server

npm run storybook

Storybook result with style

At the time of writing there is a chance you might run into this bug. You can fix it with this solution.

That's all

Your project is ready to be built using a robust framework and beautiful style with an easy way to check component behavior in isolation!

I hope that you found this article helpful and easy to read.

Do you need the Typescript version? Let us know with a comment.

Follow up story - use TailwindCSS and Storybook with SCSS

NextJS side this isn’t a problem, you need just to install the scss library using npm.

npm install sass

Rename the styles/globals.css to styles/globals.scss and fix the _app.js and .storybook/preview.js style imports.

and replace the TailwindCSS style import from this:

@tailwind base;
@tailwind components;
@tailwind utilities;

to this:

@import "tailwindcss/base";
@import "tailwindcss/components";
@import "tailwindcss/utilities";

And Storybook?

Storybook SCSS error

It seems that Storybook doesn’t recognize the SCSS syntax, and for this reason we need to extend the library Webpack configuration adding the sass loader.

The Webpack loader is used starting from the end, and this means that the sass-loader should be added at the end of the loader list.

Here you can find the .storybook/main.js file with the correct configuration:

module.exports = {
  stories: [
    // Paths to the story files
    "../pages/*.stories.mdx",
    "../pages/*.stories.js",
  ],
  addons: ["@storybook/addon-links", "@storybook/addon-essentials"],
  webpackFinal: async (config) => {
    config.module.rules.push({
      test: /\.scss$/,
      use: [
        "style-loader",
        "css-loader",
        "postcss-loader",
        // Add the sass loader to process scss files
        "sass-loader",
      ],
    })

    return config
  },
};

You may also like

Let’s redefine
eCommerce together.