Why Tailwind Sucks in Production (And What to Use Instead)

You're probably using Tailwind wrong. I did for a year. Cost us 3 months of refactoring and a 40% increase in bundle size. Here's the brutal truth about Tailwind in production and what actually works.

The Tailwind Honeymoon Phase

Tailwind is great for prototyping. You can slap together a UI in hours. But when your app grows, Tailwind becomes a liability. Here's why:

1. Your CSS Bundle Explodes

Tailwind's utility-first approach means you're shipping classes you don't need. A medium-sized app can easily hit 500KB+ of CSS. That's not "lightweight."

Real numbers from our production app:

  • Initial Tailwind build: 680KB CSS
  • After purging: 320KB
  • After switching to CSS Modules: 80KB

2. Specificity Wars

Tailwind's low-specificity classes seem great until you need to override them. Suddenly you're adding !important everywhere. That's not maintainable.

/* Tailwind mess */
<button class="bg-blue-500 hover:bg-blue-700 !text-white !p-4">Click me</button>

/* CSS Modules alternative */
.button {
  background: blue;
  color: white;
}
.button:hover {
  background: darkblue;
}

3. The "Just Use @apply" Trap

Tailwind fans say "just use @apply for repeated styles." But @apply doesn't work with media queries or pseudo-classes. You end up with a mess of workarounds.

What Tailwind Gets Wrong

The "No Naming Classes" Lie

Tailwind claims you don't need to name classes. That's bullshit. You still need semantic names for components. You're just hiding them in JSX.

// Tailwind: "No class names!" (but you still have them)
function Button() {
  return <button className="bg-blue-500 hover:bg-blue-700 text-white p-4">Click</button>;
}

// CSS Modules: Explicit and maintainable
import styles from './Button.module.css';
function Button() {
  return <button className={styles.button}>Click</button>;
}

The Refactoring Nightmare

Changing a design system in Tailwind means grep-replacing classes across your entire codebase. With CSS Modules, you change one file.

The Performance Cost

Tailwind's JIT mode helps, but it's not magic. You're still shipping more CSS than you need. And the build time? Painful.

Build times on our CI:

  • Tailwind: 45s
  • CSS Modules: 8s

What Actually Works in Production

CSS Modules: The Pragmatic Choice

CSS Modules give you:

  • Scoped styles by default
  • Real class names (not utility spam)
  • No specificity wars
  • Tiny bundle sizes
/* Button.module.css */
.button {
  background: blue;
  color: white;
  padding: 1rem;
}
.button:hover {
  background: darkblue;
}

Styled Components: When You Need JS

If you need dynamic styles, Styled Components is better than Tailwind's arbitrary values.

const Button = styled.button`
  background: ${props => props.primary ? 'blue' : 'gray'};
  color: white;
  padding: 1rem;
`;

Vanilla Extract: The Best of Both Worlds

Vanilla Extract gives you TypeScript support and zero-runtime CSS. It's what we use now.

// button.css.ts
import { style } from '@vanilla-extract/css';

export const button = style({
  background: 'blue',
  color: 'white',
  padding: '1rem',
  ':hover': {
    background: 'darkblue',
  },
});

The Verdict

  • Use Tailwind if you're prototyping or building a small marketing site.
  • Avoid Tailwind if you're building a large, maintainable app.
  • Use CSS Modules for most projects. Simple, scalable, performant.
  • Use Vanilla Extract if you want TypeScript and zero-runtime CSS.

Tailwind isn't bad. It's just overhyped for production. Choose the right tool for the job.

#Tailwind CSS production issues#Tailwind alternatives#CSS frameworks for production#Tailwind vs BEM#Tailwind scalability problems
Raja CRN

Raja CRN

Tech enthusiast and developer.