Build React Components with Tailwind, Twin Macro and Storybook

Build React Components with Tailwind, Twin Macro and Storybook

In the previous article, I have wrote an article about React Component, but using Chakra. Now, I would like to share about React component again, but use Tailwind CSS + Twin Macro and Storybook.

What is Tailwind CSS

"Rapidly build modern websites without ever leaving your HTML". Tailwind is utility-first css framework packed with classes. official docs

Tailwind is another css framework like bootstrap, bulma, foundation, pureCSS, etc. But with benefits like:

  • Utility classes
  • Easy to custom
  • Easy to handle RWD (Responsive Web Design)

Even we can write Tailwind into small component, that every single piece components has ability to extends each other. (It just very possible if only using twin macro)

What is Twin Macro

If we write codes in React and Tailwind without Twin Macro , we used syntax like

<div className="text-lg font-semibold">Text</div>

But if we implement Tailwind with Twin macro we can write it to be like this:

<div css={[tw`text-lg font-semibold`]}>Text</div>

After I explored it, at least the power of Twin macro is:

  • Nesting tw with css prop, like example above
  • Mixing SASS styles with the css import
  • Write Component in Styled Components

You may check this out to get the insight about style in tailwind + twin macro

What is Storybook

Storybook is the tool that make Dev possible to transform components into web view. Even, all available props in the components can be explore just in preview like in the web based.

Prerequisites

To following this guide, at least you should be already:

  • Understand React.js
  • Understand HTML, CSS
  • Already have single React Project to practice.

Lets Get Started

You can following steps below, or you can skip these installation steps and look over at my real project tailwind-twin-macro-storybook

Look over my repository or just look at package.json file, when you got error or cannot installed as well.

Install Tailwind CSS & Twin Macro

You can follow all the steps from this tutorial. That is pretty much guide to you follow.

Of course, my repo above also guided by that article.

Install Storybook

There are some methods to install storybook.

# 1. using npx: an npm package runner
npx sb init

# 2. simpler via storybook cli
sb init

I personally prefer to used first step, because we don't need to installed storybook cli first. :)

Nb. when you got errors while installing storybook. Try to added few line of codes below. In my case, this can be solved

  "devDependencies": {
    "@babel/core": "^7.15.0",
    "@babel/plugin-transform-react-jsx": "^7.14.9",
    "@babel/runtime": "^7.14.8",
    "@babel/runtime-corejs3": "^7.14.9",

   ...
}

Lets build components

After above steps is already done. Now we can build component with TailwindCSS + Twin Macro.

Button

In this article, I would like to show you how to build Button component.

Our Button, will look like these and it has arguments of variant, size and children.

image.png

First. Make a structure folders like this

src
│   ...
└───components
│   │   ...
│   └───Button
│       │   Button.jsx
│       │   Button.stories.jsx
│       │   index.js
│       │   styles.js
│       │   theme.jsindex.js
│  ...

Explanation

components

Folder to placed all of generic components, like: Button, InputField, Heading, etc

Button.jsx

Main file to handle Button component. Logic, HTML Syntax, handle variant, size, etc.

Button.stories.jsx

Place of Storybook Component that will represent Button component. I personally prefer to placed in same folder in Button folder, instead of separated into single folder to handle all of components. IMO this will very easier to find while maintenance.

index.js

Re-export Button component file, so from outside can call simpler like import { Button } from './components/Button'

styles.js

To handle all of Button styles. Default, variant styles, and sizes are configure here.

theme.js

To simplify handle variant and size button (in these case). Separating from Button.jsx IMO to make the maintenance easier.

Button.jsx

import * as React from 'react';

/** @jsxImportSource @emotion/react */
import { buttonDefaultStyle } from './styles';
import { ButtonSizes, ButtonVariants } from './theme';

export const Button = ({ variant, size, children, ...rest }) => {
  return (
    <button
      css={[buttonDefaultStyle, ButtonVariants[variant], ButtonSizes[size]]}
      {...rest}
    >
      {children}
    </button>
  );
};

Button.defaultProps = {
  variant: 'default',
  size: 'sm',
};
  • Button.defaultProps is to give default value for Button component. You can do also to given default in the params function name e.g: variant = 'default', size = 'sm'. I not just as personally like those style. But, somehow when used storybook, storybook also will give default value when we wrote like those instead of given default in the params. And IMO it's also more clearly to separating the default value from function instead of writing like these:
export const Button = ({
  variant = 'default',
  size = 'sm',
  children,
  ...rest
}) => {
   ...
}

Button.stories.jsx

/* eslint-disable import/no-anonymous-default-export */
import React from 'react';

import { Button } from './Button';

export default {
  title: 'Components/Button',
  component: Button,
  argTypes: {
    variant: {
      control: {
        type: 'select',
        options: ['default', 'primary', 'success', 'info', 'warning', 'error'],
      },
    },
    size: {
      control: {
        type: 'select',
        options: ['xs', 'sm', 'lg', 'xl'],
      },
    },
    children: { control: 'text' },
  },
};

const Template = args => <Button {...args} />;

export const Example = Template.bind({});
Example.args = {
  variant: 'primary',
  size: 'sm',
  children: 'Button Text',
};

index.js

export * from './Button';

styles.js

import tw from 'twin.macro';

export const buttonDefaultStyle = tw`border-DEFAULT rounded-md transition-all duration-500 ease-in select-none focus:outline-none focus:shadow-DEFAULT`;

export const buttonVariantDefault = tw`border-gray-200 bg-gray-200 text-gray-700 hover:bg-gray-300`;
export const buttonVariantPrimary = tw`border-blue-500 bg-blue-500 text-white hover:bg-blue-600`;
export const buttonVariantSuccess = tw`border-green-500 bg-green-500 text-white hover:bg-green-600`;
export const buttonVariantError = tw`border-red-500 bg-red-500 text-white hover:bg-red-600`;
export const buttonVariantWarning = tw`border-yellow-500 bg-yellow-500 text-white hover:bg-yellow-600`;
export const buttonVariantInfo = tw`border-teal-500 bg-teal-500 text-white hover:bg-teal-600`;

export const buttonSizeEkstraLarge = tw`px-8 py-5`
export const buttonSizeLarge = tw`px-5 py-3`
export const buttonSizeSmall = tw`px-4 py-2 `
export const buttonSizeEkstraSmall = tw`px-3 py-1 font-medium text-sm`

theme.js

import {
  buttonSizeEkstraLarge,
  buttonSizeEkstraSmall,
  buttonSizeLarge,
  buttonSizeSmall,
  buttonVariantDefault,
  buttonVariantError,
  buttonVariantInfo,
  buttonVariantPrimary,
  buttonVariantSuccess,
  buttonVariantWarning,
} from './styles';

export const ButtonVariants = {
  default: buttonVariantDefault,
  primary: buttonVariantPrimary,
  success: buttonVariantSuccess,
  error: buttonVariantError,
  warning: buttonVariantWarning,
  info: buttonVariantInfo,
};

export const ButtonSizes = {
  xl: buttonSizeEkstraLarge,
  lg: buttonSizeLarge,
  sm: buttonSizeSmall,
  xs: buttonSizeEkstraSmall,
};

Lets config & run Storybook

In your package.json should contains 2 line codes like these. It's automatically when you run npx sb init like above.

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

If you did, now you can running:

yarn storybook

It will automatically opened a page localhost:6006

But, you need to modify storybook configuration, to make tailwind styles loaded properly.

Add some files inside of .storybook folder like below:

src
│   ...
└───.storybook
│   │   main.js
│   │   manager.js
│   │   preview.js
│   │   storyTheme.js
│  ...

manager.js

File to modify storybook theme. You may check this official storybook theme docs for more insight.

import { addons } from '@storybook/addons'
import storyTheme from './storyTheme'

addons.setConfig({
  theme: storyTheme,
})

preview.js

Modify top of preview.js. Add single line of code below

import 'tailwindcss/dist/base.css';

...

storyTheme.js

This file is your theme configuration placed.

import { create } from '@storybook/theming/create'

export default create({
  base: 'light',
  brandTitle: 'Awesome UI',
  brandUrl: 'https://rohmad-st.github.io',
})

If you done with all those steps, it's should will be like below. But if any wrong with your step, feel free to check from my repository again. :)

awesome-ui.gif

Summary

Implement React Component in Tailwind will very comfortable, if you are CSS guy that loved so much about CSS (Sass). Maybe hard enough to initiate in the first time. But I believe that on the process development, it will very joyful.

If you have feedback, idea about this article, questions or anything. Don't hesitate to reach me by given comment below.

Thanks you.