Thursday, August 15, 2019

Building Reusable React Components Using Tailwind

Tailwind is a popular utility-first CSS framework that provides low-level class names to web developers. It does not have any JavaScript and works well with existing frameworks such as React, Vue, Angular, Ember, and others. Whilst this is positive, it can be confusing for new developers to understand how to integrate Tailwind in their applications. In this article, we’ll explore ways to build reusable React components using Tailwind.


In this post, we’ll look at several different ways you can build reusable React components that leverage Tailwind under the hood while exposing a nice interface to other components. This will improve your code by moving from long lists of class names to semantic props that are easier to read and maintain.
You will need to have worked with React in order to get a good understanding of this post.
Tailwind is a very popular CSS framework that provides low-level utility classes to help developers build custom designs. It’s grown in popularity over the last few years because it solves two problems really well:
  1. Tailwind makes it easy to make iterative changes to HTML without digging through stylesheets to find matching CSS selectors.
  2. Tailwind has sane conventions and defaults. This makes it easy for people to get started without writing CSS from scratch.
Add the comprehensive documentation and it’s no surprise why Tailwind is so popular.
These methods will help you transform code that looks like this:
<button className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
  Enable
</button>
To code that looks like this:
<Button size="sm" textColor="white" bgColor="blue-500">
  Enable
</Button>
The difference between both snippets is that in the first we made use of a standard HTML button tag, while the second used a <Button> component. The <Button> component had been built for reusability and is easier to read since it has better semantics. Instead of a long list of class names, it uses properties to set various attributes such as size, textColor, and bgColor.
Let’s get started.

Method 1: Controlling Classes With The Classnames Module

A simple way to adapt Tailwind into a React application is to embrace the class names and toggle them programmatically.
The classnames npm module makes it easy to toggle classes in React. To demonstrate how you may use this, let’s take a use case where you have <Button> components in your React application.
// This could be hard to read.
<button className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">Enable</button>

// This is more conventional React.
<Button size="sm" textColor="white" bgColor="blue-500">Enable</Button>
Let’s see how to separate Tailwind classes so people using this <Button> component can use React props such as size, textColor, and bgColor.
  1. Pass props such as bgColor and textColor directly into the class name string template.
  2. Use objects to programmatically switch class names (as we have done with the size prop)
In the example code below, we’ll take a look at both approaches.
// Button.jsx
import classnames from 'classnames';

function Button ({size, bgColor, textColor, children}) {
    return (
        <button className={classnames("bg-${bgColor} text-${textColor} font-bold py-2 px-4 rounded", {
    "text-xs": size === 'sm'
    "text-xl": size === 'lg',
    })}>
        {children}
    </button>
    )
};

export default Button;
In the code above, we define a Button component that takes the following props:
  • size
    Defines the size of the button and applies the Tailwind classes text-xs or text-xl
  • bgColor
    Defines the background color of the button and applies the Tailwind bg-* classes.
  • textColor
    Defines the text color of the button and applies the Tailwind text-* classes.
  • children
    Any subcomponents will be passed through here. It will usually contain the text within the <Button>.
By defining Button.jsx, we can now import it in and use React props instead of class names. This makes our code easier to read and reuse.
import Button from './Button';
<Button size="sm" textColor="white" bgColor="blue-500">Enable</Button>

Using Class Names For Interactive Components

A Button is a very simple use-case. What about something more complicated? Well, you can take this further to make interactive components.

For this example, we create the HTML component using Tailwind CSS classnames but we expose a React component that looks like this:
<Dropdown 
  options={\["Edit", "Duplicate", "Archive", "Move", "Delete"\]} 
  onOptionSelect={(option) => { 
    console.log("Selected Option", option)}
  } 
/>
Looking at the code above, you’ll notice that we don’t have any Tailwind classes. They are all hidden inside the implementation code of <Dropdown/>. The user of this Dropdown component just has to provide a list of options and a click handler, onOptionSelect when an option is clicked.
Let’s see how this component can be built using Tailwind.

import classNames from 'classnames';

function Dropdown({ options, onOptionSelect }) {

  // Keep track of whether the dropdown is open or not.
  const [isActive, setActive] = useState(false);
  
  const buttonClasses = `inline-flex justify-center w-full rounded-md border border-gray-300 px-4 py-2 bg-white text-sm leading-5 font-medium text-gray-700 hover:text-gray-500 focus:outline-none focus:border-blue-300 focus:shadow-outline-blue active:bg-blue-500 active:text-gray-200 transition ease-in-out duration-150`;

  return (
    // Toggle the dropdown if the button is clicked
    <button onClick={() => setActive(!isActive)} className={buttonClasses}>
      Options
    </button>
    // Use the classnames module to toggle the Tailwind .block and .hidden classes
    <div class={classNames("origin-top-right absolute right-0 mt-2 w-56 rounded-md shadow-lg", {
      block: isActive,
      hidden: !isActive
    })}>
    // List items are rendered here.
    {options.map((option) => <div key={option} onClick={(e) => onOptionSelect(option)}>{option}</div>)}
   </div>
  )
}

export default Dropdown;
The dropdown is made interactive by selectively showing or hiding it using the .hidden and .block classes. Whenever the <button> is pressed, we fire the onClick handler that toggles the isActive state. If the button is active (isActive === true), we set the block class. Otherwise, we set the hidden class. These are both Tailwind classes for toggling display behavior.
In summary, the classnames module is a simple and effective way to programmatically control class names for Tailwind. It makes it easier to separate logic into React props, which makes your components easier to reuse. It works for simple and interactive components.