Understanding the trade-offs of using Tailwind CSS

If you work in web development, there’s a good chance you’ve come across Tailwind CSS. If not, it’s a good tool to have on your radar.

Tailwind is a CSS framework that uses a utility-first approach. Elements are styled directly, without needing to leave your HTML.

Its promise is that you can build modern web pages quickly and simply. Choosing a utility-first framework could have a significant impact on your project — particularly in maintenance and scaling. It undoubtedly has benefits, but they come with trade-offs.

In this post we want to explore some counterpoints to those benefits. Hopefully this will help you decide whether it’s the right tool for your project.

In no way is this intended as a take-down. In the quest to make life better for the people that build the web, Tailwind is a positive force. We write this from the point of view of practitioners evaluating a tool.

Benefits of Tailwind

Much has been written on the benefits of Tailwind, so we’ll cover those we see in brief:

No custom CSS: It provides ready-to-use utility classes that let you style elements directly in your markup.

Less need for naming conventions: It largely does away with creating and naming custom classes.

Good performance: It’s a small library with production builds that automatically strip out unused CSS.

CSS knowledge is less important: There’s less need for CSS skills on your team — lots of developers know Tailwind.

Good design defaults: You can mostly trust Tailwind’s defaults to quickly help you build something decent.

Easy to customise: If you do want to edit its defaults, it makes this easy through changes to a single configuration file.

Design consistency: Developers can apply consistent styles without having to choose exact values.

Responsive design: You can apply classes conditionally at various breakpoints without writing additional CSS.

Fast iteration: You can test and adjust things quickly, making changes directly in the markup.

Well supported Tailwind is backed by many plugins, extensive documentation and well-known paradigms.

For more on the benefits of utility-first, see:

John Polacek’s The Case for Atomic / Utility-First CSS (via Tailwind)

Ryan Flynn’s Mastering Tailwind CSS: An In-Depth Look at the Utility-First Framework

This Reddit thread, where the community shares benefits from a boots-on-the-ground perspective

Counterpoints to Tailwind’s documentation

Many of Tailwind’s benefits are clear-cut. But we’ve also seen cases made that we think require caveats, including those in Tailwind’s own Utility-First Fundamentals. Let’s look at these in turn.

Effort saved with class names and selectors

You don't spend any time coming up with class names [and] making decisions about selectors […] so your designs come together very fast.

Naming things is hard. But a well-named styling or theming architecture is a worthwhile investment on large projects.

Thoughtfully-named styles help teams develop a shared language for components and layouts, making it easier to collaborate across disciplines. This reinforces consistency and helps make sure that intentional design choices are carried forward as the project evolves. Consequently, experienced CSS people generally don’t spend time agonising over class names and selectors.

In contrast, relying too heavily on utility-first classes can result in context being lost. For example, developers have to interpret design intent from raw style rules rather than clear names.

Going further, advanced CSS selectors can make it easy to do things that are much harder, or cumbersome, using only utilities. For example, focus by demotion can be achieved in a few lines of CSS, but is an absolute casserole to do in Tailwind — less than ideal if the object is to keep your codebase simple.

Naming CSS classes isn’t that hard in any case. Well-considered naming conventions provide a straightforward way to choose intuitive class names.

There are also technical solutions that make naming CSS classes easier. For example, CSS Modules lets you scope class names locally. This means you can use simple, logical names without worrying about conflicts in the global namespace.

Overall, taking the trouble to develop a well-named styling architecture may mean more up-front effort, but it can pay dividends longer term.

CSS stops growing

Since utility classes are so reusable, your CSS doesn't continue to grow linearly with every new feature you add to a project.

This is true, but by using Tailwind your markup gets bigger every time you add a utility class. Consequently, it becomes harder to read and maintain.

Front-end abstractions are about more than CSS. Tailwind encourages utility-first development to avoid thinking about encapsulation and accelerate development, but this can have diminishing returns.

In our experience, it can mean time-pressed developers choose to keep adding Tailwind classes rather than breaking up their components into smaller, systematic pieces. Over time, this can make your front-end cumbersome and difficult to scale.

When you have a system of composable components, you rarely need to write new CSS. You can simply reuse the components.

Safer changes

Adding or removing a utility class to an element only ever affects that element, so you never have to worry about accidentally breaking something [on] another page that's using the same CSS.

This is a problem that has been solved by hard encapsulation in general. This means a component’s styles are confined to only that component — they can’t leak to other parts of the project.

Of course, Tailwind’s approach is a form of hard encapsulation, but it’s by no means the only game in town.

With CSS’s natural inheritance, styles can cascade across elements and contexts, sometimes unpredictably. Tailwind defines styles locally in the markup, sidestepping CSS inheritance by making sure that each element’s styles are explicitly declared.

This can reduce unintended side effects, but it also bypasses one of CSS’s strengths: the ability to create flexible systems that adapt globally with minimal duplication.

There are others tools available to do hard encapsulation, such as:

CSS Modules

shadowDOM

CSS-in-JS

But, if your project is relatively simple, soft encapsulation through naming conventions like SUIT CSS may be all you need.

In sum, there are many ways to architect your styles so you never have to think about unintended side effects. Weigh up the options and choose an approach that works for you.

Designing with constraints

Using inline styles, every value is a magic number. With utilities, you’re choosing styles from a predefined design system, which makes it much easier to build visually consistent UIs.

We don’t advocate for inline styles, but this does raise a question about the problems Tailwind solves.

Magic numbers are arbitrary values in CSS that lack clear purpose. They tend to persist for fear of breaking something. Or they break things when they’re removed because their purpose wasn’t clear.

Avoiding magic numbers is good practice. But Tailwind utilities can be magic numbers too. In fact, Tailwind provides advice on how to use arbitrary values.1

Designing to constraints can be done in several ways. CSS Custom Properties enforced by linting is a technique we’ve used at Measured.

Tailwind’s out-of-the-box utilities define a system, but it’s broad, with little guidance on usage. They can be an aid to consistency but they’re not a panacea.

Responsive design, hover, focus, and other states

You can’t use media queries in inline styles, but you can use Tailwind’s responsive variants to build fully responsive interfaces easily.

And:

Inline styles can’t target states like hover or focus, but Tailwind’s state variants make it easy to style those states with utility classes.

These points highlight Tailwind’s advantages over raw inline styles, but they’re less about why Tailwind is uniquely good and more about why inline styles are problematic.

While it’s true that Tailwind’s utilities can simplify responsive design and state-based styling, these problems can also be solved in other ways.

Libraries like CSS Hooks let you manage responsive designs and state styles directly in JavaScript. Similarly, approaches like styled-components or other CSS-in-JS libraries let you define styles with responsive and state-based capabilities in a flexible, programmatic way.

Tailwind makes these tasks easier in its own context, but is that a reason to choose Tailwind?

Other considerations

Here are two more considerations, though these aren’t in reference to Tailwind’s documentation.

Potential brand conflicts

Tailwind makes design choices so designers and developers don't have to. This can result in a recognisable look. Those familiar with Tailwind can often spot Tailwind-built sites.

However, every brand has its own architecture and identity (sometimes complex), which needs to be represented in UI design decisions. Though Tailwind supports theming, this is done within the constraints of Tailwind’s naming conventions, which might not make sense for your brand.

This is more complex with multi-brand systems, where one of two outcomes is likely. Either the resulting solution will make all the brands look like Tailwind, or it requires so many escape hatches that you'd be better off not using Tailwind in the first place.

You need to know some CSS anyway

To be most effective with Tailwind, you still need to have some understanding of the underlying CSS. For example, to understand Tailwind's flex class, you probably need to understand how flexbox works.

If you only learn through Tailwind, you will only have a truncated understanding of CSS. This means you will likely struggle to debug more complicated issues, or transfer your skills to other CSS architectures in the future.

To some extent, it’s an example of ecosystem lock-in, and one that creates barriers for experienced CSS people. Sometimes the class names are intuitive, and sometimes they’re not. Modern CSS is pretty concise in any case.

All things considered…

There are no silver bullets when making web UI. Weighing up CSS against Tailwind means weighing up:

Time spent naming CSS classes against time spent reading and parsing utility names

CSS size against markup size

Inheritance against encapsulation

Opinionated systems against flexible systems

CSS expertise against Tailwind expertise

Tailwind’s utility-first approach can work well for small-to-medium projects where speed and cost-efficiency are top priorities. This is especially true if you already have Tailwind skills — or a lack of CSS skills — on deck.

But for larger projects or teams with established CSS practices, it introduces challenges around readability, maintainability, and scalability. The larger and more complex your project, the less sense Tailwind might make.

Thank you to Matthew Lynch and Chris Villa for contributing insights and feedback to this article.

We’d love to hear your thoughts. Get involved on LinkedIn.

Footnotes

We recommend linting to prevent magic numbers in Tailwind, using ESLint-plugin-tailwindcss, for example. ↩