DEV Community

Cover image for Why Tailwind CSS Might Be Hurting Your Large-Scale Projects
Gouranga Das Samrat
Gouranga Das Samrat

Posted on

Why Tailwind CSS Might Be Hurting Your Large-Scale Projects

_A honest discussion about the trade-offs nobody

Introduction

After 5 years of using Tailwind CSS in production, I’ve learned some hard lessons about utility-first CSS at scale. This isn’t another “Tailwind bad” rant — it’s an honest conversation about when this popular framework starts showing its limitations.

Disclaimer: I don’t have exact metrics to share (NDAs exist), but I’ll walk you through the real patterns and problems we encountered.

The Good: Why We Chose Tailwind

Let’s start with credit where it’s due. Tailwind CSS delivers on many promises:

  • Rapid prototyping — Building UIs has never been faster
  • No context switching — Stay in your HTML/JSX files
  • Consistent spacing — The design system is built-in
  • Small learning curve — New developers can contribute immediately

For the first few months, it felt revolutionary.

The Reality Check: When Scale Hits

captionless image

1. The Readability Problem

Here’s what our components started looking like:

// A real card component from our codebase
<article className="relative flex flex-col min-w-0 rounded-lg break-words border border-gray-200 bg-white shadow-sm dark:border-gray-700 dark:bg-gray-800 md:flex-row md:items-center md:justify-between hover:shadow-lg transition-shadow duration-200">
  <div className="flex-1 p-4 md:p-6 lg:p-8">
    <h2 className="text-lg md:text-xl lg:text-2xl font-semibold text-gray-900 dark:text-white mb-2">
      {title}
    </h2>
    <p className="text-sm md:textbase text-gray-600 dark:text-gray-300 line-clamp-3">
      {description}
    </p>
  </div>
</article>
Enter fullscreen mode Exit fullscreen mode

The problem?

  • Can you tell what this component does at a glance?
  • How would you modify just the desktop layout?
  • Where does the component styling end and utility classes begin?

2. The Maintenance Challenge

Consider this scenario we faced last fewmonths:

Design team: “We need to update all card shadows to be more subtle”

With Tailwind: Search for shadow-sm, shadow-lg, hover:shadow-xl across:

  • Components
  • Templates
  • Random utility combinations
  • Third-party components we customized

With CSS Modules: Update one variable:

:root {
  --card-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
}
Enter fullscreen mode Exit fullscreen mode

3. The Bundle Size Discussion

While Tailwind’s PurgeCSS is impressive, here’s what people don’t discuss:

// What your HTML looks like
<div className="p-4 md:p-6 lg:p-8 xl:p-10 2xl:p-12">
Enter fullscreen mode Exit fullscreen mode
// What CSS gets generated (simplified)
.p-4 { padding: 1rem; }
.md\:p-6 { padding: 1.5rem; }
.lg\:p-8 { padding: 2rem; }
.xl\:p-10 { padding: 2.5rem; }
.2xl\:p-12 { padding: 3rem; }
Enter fullscreen mode Exit fullscreen mode

Compare to modern CSS:

.container {
  padding: clamp(1rem, 4vw, 3rem);
}
Enter fullscreen mode Exit fullscreen mode

One line vs five classes. Multiply this by every responsive property in your app.

The Patterns That Hurt Us

Anti-Pattern 1: The Copy-Paste Culture

New developers would copy this:

<button className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
Enter fullscreen mode Exit fullscreen mode

Instead of using:

<Button variant="primary">
Enter fullscreen mode Exit fullscreen mode

Result: Inconsistent button implementations everywhere

Anti-Pattern 2: The Component Sprawl

// What we ended up with across different files
<div className="bg-white p-6 rounded-lg shadow-md">
<div className="bg-white p-4 rounded shadow-sm">
<div className="bg-white p-8 rounded-xl shadow-lg">
Enter fullscreen mode Exit fullscreen mode

Result: Three slightly different “card” components that should have been one reusable <Card> component.

Anti-Pattern 3: The Responsive Madness

<div className="text-sm md:text-base lg:text-lg xl:text-xl 2xl:text-2xl p-2 md:p-4 lg:p-6 xl:p-8 2xl:p-10 m-1 md:m-2 lg:m-3 xl:m-4 2xl:m-5">
Enter fullscreen mode Exit fullscreen mode

Compare this to modern CSS

.responsive-text {
  font-size: clamp(0.875rem, 2.5vw, 1.5rem);
  padding: clamp(0.5rem, 4vw, 2.5rem);
  margin: clamp(0.25rem, 1vw, 1.25rem);
}
Enter fullscreen mode Exit fullscreen mode

The Real Numbers: What UK Developers Are Saying
------------------------to---------------------

Based on recent survey data, the UK was among the top three countries participating in developer surveys, alongside the USA and Germany State of CSS 2024 Survey Now Open — WP Tavern. Here’s what the data reveals:

Usage Reality Check: While 75% usage seems impressive, only 50% of survey respondents actually answered the Tailwind question.

This means real usage is closer to 37.5% of all developers 🔍 Going through the “State of CSS” results… • Josh W. Comeau — significant, but not the overwhelming majority some claim.

The Maintenance Problem is Real: As one developer put it:

“I really tried to like Tailwind. And I understand what it does and its benefits. But ease of code readability, to me anyway, is #1. I am not used to having my HTML/JSX/whatever filled to the brim with a million classes. It’s unbelievably messy.”

When Tailwind Actually Works Well

Let’s be fair — Tailwind isn’t always the wrong choice:

  • Rapid prototyping — For hackathons and proof-of-concepts
  • Small teams with limited CSS knowledge
  • Projects with simple design systems
  • Marketing sites with minimal maintenance needs

The key insight: It’s perfect for projects compatible with uncomplicated CSS and low maintenance, and it’s ideal for front-end teams with little knowledge of CSS or limited resources allocated for CSS architecture.

My solution: A Hybrid Approach

After few years of pure Tailwind, here’s what we’re moving towards:

Component-First Architecture

// Instead of this Tailwind mess 
<button className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline">  
// We use this <Button variant="primary">Submit</Button>
Enter fullscreen mode Exit fullscreen mode

CSS Custom Properties for Theming

:root {   --color-primary: #3b82f6;   
--spacing-card: clamp(1rem, 4vw, 2rem);   
--shadow-card: 0 4px 6px -1px rgba(0, 0, 0, 0.1); 
}
Enter fullscreen mode Exit fullscreen mode

Modern CSS Features

.container {   
container-type: inline-size;   
padding: clamp(1rem, 5vw, 3rem); 
}  
.card {   padding: var(--spacing-card);   
box-shadow: var(--shadow-card); 
}  
@container (min-width: 768px) {   
  .card-content {     
  display: grid;     grid-template-columns: 1fr 2fr;     gap: 2rem;   
  } 
}
Enter fullscreen mode Exit fullscreen mode

The Final Verdict

Tailwind CSS isn’t inherently bad — it’s a tool that works brilliantly for specific use cases. However, the scenario where “what once felt like a productivity boost now feels like technical debt” is all too common when developers adopt Tailwind without established patterns for large-scale implementations Best Practices for Using Tailwind CSS in Large Projects — Wisp CMS.

The hard truth? For large-scale projects, traditional CSS architecture often provides better long-term maintainability, performance, and developer experience.

Before choosing Tailwind, ask yourself:

  • Will this project need significant design updates?
  • Do we have senior CSS developers on the team?
  • Are we building a design system that will be used across multiple projects?
  • Do we need fine-grained control over our styles?

If you answered “yes” to any of these, consider modern CSS approaches instead.

Remember: The goal isn’t to use the trendiest tool — it’s to build maintainable, performant websites that serve your users well.

What’s been your experience with Tailwind at scale? Have you faced similar challenges, or found solutions that work? I’d love to hear your thoughts in the comments below.

Top comments (10)

Collapse
 
mikebot profile image
MikeBot • Edited

I think this misses one key point about Tailwind in modern component-based frameworks (React/Angular/etc).

The "loss of semantic meaning" only exists if you stop at utility classes. In a proper setup, semantics move up into components. We don’t need <button class="primary-submit"> — we build <PrimarySubmitButton />, and the Tailwind utilities live inside it.

Prototype fast → extract to components → reuse.
If Tailwind is leaking all over pages, that’s not a Tailwind problem, it’s an architectural one.

Collapse
 
jon49 profile image
Jon Nyman

And... you just created a leaky abstraction.

Collapse
 
mikebot profile image
MikeBot

I see what you mean about leaky abstractions, but in this case the abstraction is intentional and controlled. encapsulates all the Tailwind classes, so the parent page doesn’t need to know anything about the styling details.

If the component is well-designed, any “leakage” is internal, not exposed—Tailwind stays an implementation detail. The leak you mention usually happens when utilities are scattered across pages, not when they’re wrapped in semantic components.

Thread Thread
 
jon49 profile image
Jon Nyman

Using frameworks like MUI I've found them to be really leaky abstractions. "Oh, I want this to work slightly differently, oh, its code is encapsulated and I can't change how it works for this one thing I need."

I prefer to just add the class myself to the HTML. Old school, but now I have full control.

But, yes, if you control the component then it isn't as big of a deal. Granted, if you don't expose the internals and someone tries to update the anchor element with an attribute and then becomes confused as to why it isn't working... true story, not me but a coworker.

Collapse
 
missatrox44 profile image
Sara Baqla • Edited

In my previous role we definitely had a mixed approach of tailwind and custom classes. We usually used tailwind classes for basic layout (grid, flex, responsive columns etc) and custom classes for everything else. I think also using a CSS preprocessor like Sass really helps to keep styles reusable and scoped cleanly which makes the system more scalable.

I also wonder how far Tailwind’s own features could help mitigate the copy/paste inconsistency problem. Extracting classes using @apply in v3 or @layerin v4 could centralize common patterns like buttons/cards so you’re not repeating utilities everywhere. It feels like Tailwind can work at scale, but only if there’s intentional architecture on top of it.

Collapse
 
maxart2501 profile image
Massimo Artizzu

I'll take this a step further and boldly say that Tailwind is inherently bad. Even the use cases you list are vastly debatable.

Rapid prototyping

It works, but other alternatives don't force you to raise a complex build system just for make it compile. It also doesn't provide pre-made customizable components like Bootstrap gives you, so you have to provide your own -- that works well with Tailwind, of course.

Small teams with limited CSS knowledge

In no case Tailwind saves you to actually know CSS. It may give you the impression, but it fades once you start struggling with complex layouts and less-than-trivial values. And maybe with the inevitable cascade problems once someone discovers the square bracket syntax.

Projects with simple design systems

Then probably Tailwind is overkill. Because Tailwind aims to completely replace CSS files, so tends to be equivalent.

Marketing sites with minimal maintenance needs

If I had a medal every time someone said a project "needed minimal maintainance" only to discover that maintainance actually costed much more effort than anticipated, I'd be forced on my knees...

The only good thing I can find in Tailwind is its default design token, which is good - but, after all, not better than less pervasive alternatives.

Collapse
 
appurist profile image
Paul / Appurist

You're right. But it's even worse than this.

I've been a professional software developer since 10 years before the first web browser. I've seen tech change over the years. The problem exists beyond Tailwind and CSS to frameworks and tools.

About 10-15 years ago, web development grew impatient with HTML and CSS and decided to code their own version with frameworks and CSS libraries. We lost our way. TailwindCSS is just one example. I've loved a LOT of frameworks in the last 15 years but after taking a look at NueJS, I now realize how derailed we've been. While we've been reinventing everything, often poorly, HTML, CSS and JS have been evolving.

Take a look at this short page for the short summary of what's wrong with web development in 2025. (You don't need to love the Nue solution to recognize the significant problems it is trying to solve.) I can't believe I didn't see these problems sooner. I felt them, but I didn't really clue in so clearly until now. To me, your article is another example of someone zooming out and seeing the problem(s), or at least recognizing the significant clues that are right there in front of us. Kudos for posting it and getting the discussion going.

Collapse
 
vilce profile image
Ștefan Vîlce

Great observation! I had the same problem with one of my projects, I choose to use TailwindCSS and I couldn't change some of the issues regarding the usability of some forms. It was a nightmare. In the second version of the application I used HTML5 boiler template. My problems are gone now.
I worked a bit more at the beginning but the usability works smooth and I can maintain it easily.

Collapse
 
sylwia-lask profile image
Sylwia Laskowska

Great observation! I feel the same – Tailwind is amazing for prototyping, but not necessarily for large projects. And who loved it the most? Backend devs who occasionally had to do something on the frontend 😅

Collapse
 
xwero profile image
david duymelinck

I think it is more of a frontend FOMO thing like SPA or micro frontends.
People see it used in one situation and then they want to use it everywhere.

I still don't understand why people are drawn to a utility classes only framework. Utility classes are not bad but this is the equivalent of using a, b and c to name your variables. You don't create context, it is easy to make mistakes, and you are copying things all over the code.

Bootstrap has flaws, but when CSS got better they incorporated the new features. I see that that less with Tailwind. It looks like they are more concerned with improving the utility classes.

With every tool people create you have to ask yourself, do I need this for my application.