DEV Community

Yuya Uzu
Yuya Uzu

Posted on

Tailwind vs styled-components in React.js

On Twitter, I constantly see people who use Tailwind and how they fell in love with it. I've been pretty much satisfied with my current work flow (styled-components), yet, I finally decided to give it a try and see if the tool was for me. This article is my own verdict after I used Tailwind for several days.

I mainly use React.js and styled-components. So this article is about a comparison between styled-components and Tailwind in the context of React.js development.

TLDR

Long story short, I decided to stick with styled-components and not to use Tailwind. Overall, I liked the Tailwind's shortened CSS properties. It's way faster to write than original CSS. However, I didn't like the fact that you have to write all those classes inside the class attribute. It can make JSX hard to read and less flexible to do something like dynamic styling.

Where styled-components win

1. Readability

What I realized first is that it's hard to read JSX when using Tailwind. With conventional CSS strategy, you give meaningful class names like class="todos-wrapper". This helps make sense of the structure just by glance. With styled-components, this is even better because you give unique name to each tags like <TodosWrapper>. This makes the maintenance work easier especially when you work with old codes.

// with Tailwind

export const MainPage = () => {
  <div className="bg-white rounded px-5 pt-1 pb-7">
  // todo elements
  </div>
}
Enter fullscreen mode Exit fullscreen mode
// with styled-components

export const MainPage = () => {
  return <TodosWrapper>
  // todo elements
  </TodosWrapper>;
}

const TodosWrapper = styled.div`
  background-color: white;
  border-radius: 0.3rem;
  padding: 8px 12px 12px 18px;
`;
Enter fullscreen mode Exit fullscreen mode

2. Dynamic styling

Dynamic styling is another thing I prefer styled-components approach. Let's say you want to style an input form dynamically based on whether the input value is valid or not. In that case, you want 3 conditions such as "empty", "invalid", and "valid". With Tailwind, you have to manage this condition by directly modifying the class attribute. This means you have to write if statement within the class attribute string, which can be a little tricky to write. While you can make it cleaner by writing an outside function to handle the if statement, I personally prefer the styled-components approach because all the styling logic happens in one place and it makes clearer separation between the structure and styling.

// with Tailwind

const MainPage = (props) => {
  const getFormState = {
    // some logic to return form state
  }

  const getBorderColor = (formState: string) => {
    if (formState === 'empty') return "border-gray-300";
    if (formState === 'invalid') return "border-red-400";
    return "border-green-400";
  };

  return (
    <div>
      <input
        type="email"
        value={email}
        onChange={handleChangeEmail}
        className={`${getBorderColor(
          getFormState()
        )} border border-gray-300 px-3 py-2 rounded w-full`}
      />
    </div>
  );
};
Enter fullscreen mode Exit fullscreen mode
// with styled-components

const MainPage = () => {
    const getFormState = {
      // some logic to return form state
    }

    return (
      <div>
        <EmailForm
          type="email"
          value={email}
          onChange={onChangeEmail}
          placeholder="example@mail.com"
          formState={getFormState()}
        />
      </div>
    );
};

const EmailForm = styled.input`
  padding: 0.7rem 1rem
  ${(props) => {
    switch (props.formState) {
      case 'invalid':
        return 'border-color: red';
      case 'valid':
        return 'border-color: green';
      default:
        return 'border-color: gray';
    }
  }}
`;
Enter fullscreen mode Exit fullscreen mode

3. Dark mode support

Adding themes such as dark mode isn't really easy with Tailwind. Sure, Tailwind has Dark Mode feature but it's not really efficient to add "dark:border-white" attribute to every elements.

It's more easier to define a dedicated variable like "borderColor" and then change the value of that variable based on which theme you are in. This is especially better when you want to add more themes later. All you have to do is to give new value to "borderColor" and you don't need to touch JSX or CSS at all (please refer to ThemeProvider of styled-components).
https://styled-components.com/docs/advanced

// With styled-components

const MainPage = () => {
  return <EmailForm />;
}

export const EmailForm = styled.input`
  // With ThemeProvider, you can access to theme variable from any styled components without prop drilling.
  border: 1px solid ${(props) => props.theme.borderColor};
`;
Enter fullscreen mode Exit fullscreen mode

But what about development speed?

I agree that development time is generally faster with Tailwind. However, it's mainly because Tailwind has predefined utility classes and you don't have to write plain CSS. It is the great part of Tailwind and I actually liked the experience. However, styled-components is not far behind on this. Yes, you have to write the plain CSS but you can still reduce the development time by making your own utility styles. Just like Tailwind, you can make common styled components like Container, TextLarge, ShadowLight, etc and call it whenever you want. Of course, you will need initial development time but I'm sure it will pay off after you prepare 10~20 frequently used style sets.

Conclusion: I choose styled-components

Maybe some of the above issues will be addressed in the future by Tailwind. However, it doesn't mean that this kind of issue won't happen again. The chances are, when you want to do some unusual things with styling or integrate with other tools, you may not have a straightforward solution. So my current conclusion is sticking with styled-components and not rushing into Tailwind (at least for now).

How to end the argument

It's sad to see devs arguing over which tool is better. These kind of arguing does not seem to stop. But don't worry. There already is a "perfect" solution. If you combine Tailwind and styled-components, there will be no argument -> https://www.npmjs.com/package/tailwind-styled-components
*I'm not using this package but let you know it exists.

It really makes sense because Tailwind and styled-components are actually solving different problems. Why not combine them rather than choosing one another.

Maybe I'm too new to Tailwind

Above opinion is made only after I used Tailwind for 2 days. So it's highly possible that I'm missing out on some point. If you have different view on Tailwind, please write in the comment. I would be happy to learn from experienced devs.

Top comments (2)

Collapse
 
vincentdorian profile image
Vincent

I think that the main benefits of using tailwindcss are the following:

  1. You never have to leave your components
  2. The base utility classes and consistent and fit perfectly well together
  3. Easy styling of component variants

Once you've got into Tailwind, you will never want to go back to CSS.

Collapse
 
uzura89 profile image
Yuya Uzu

Thanks for the comment. I think those benefits are true if you compare Tailwind with Vanilla CSS. However, they can also be achieved by CSS-in-JS libraries like styled-components. My take is that Tailwind wins on speed, styled-components wins on flexibility and readability.