DEV Community

Royce
Royce

Posted on • Originally published at starterpick.com

Tailwind CSS vs CSS Modules in Boilerplates

TL;DR

Tailwind CSS won the SaaS boilerplate styling debate. 95%+ of boilerplates released in 2024-2026 use Tailwind. CSS Modules remain valid for teams that prefer traditional CSS, but Tailwind's component ecosystem (shadcn/ui, Radix Themes, Tailwind UI) creates strong lock-in.

The Current Landscape

Approach Boilerplate Adoption Community Component Libraries
Tailwind CSS ~95% Dominant shadcn/ui, Tailwind UI, Headless UI
CSS Modules ~3% Declining Custom only
styled-components ~1% Declining Most UI libraries
CSS-in-JS (Emotion) <1% Declining MUI
Vanilla CSS <1% Custom only

Why Tailwind Won

1. shadcn/ui Changed Everything

shadcn/ui is the most impactful UI library in years. Copy-paste components, full customization, TypeScript, and built on Tailwind CSS. Over 60k GitHub stars in 2 years.

# Add shadcn/ui to any Tailwind project
npx shadcn@latest init
npx shadcn@latest add button card dialog input form
Enter fullscreen mode Exit fullscreen mode

2. Design Consistency at Speed

// Tailwind — design decisions encoded in utilities
<div className="flex items-center gap-4 rounded-lg border bg-card p-6 shadow-sm">
  <div className="h-12 w-12 rounded-full bg-primary" />
  <div>
    <h3 className="font-semibold">User Name</h3>
    <p className="text-sm text-muted-foreground">Member since 2026</p>
  </div>
</div>
Enter fullscreen mode Exit fullscreen mode

3. No CSS File Management

Traditional CSS requires maintaining separate files, naming conventions (BEM?), and tracking class name collisions. Tailwind eliminates the problem.

CSS Modules Still Work

// CSS Modules — traditional approach

  return (
    <div className={styles.card}>
      <div className={styles.avatar} />
      <div>
        <h3 className={styles.name}>{user.name}</h3>
        <p className={styles.subtitle}>Member since 2026</p>
      </div>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode
/* UserCard.module.css */
.card { display: flex; align-items: center; gap: 1rem; border-radius: 8px; border: 1px solid var(--border); background: var(--card); padding: 1.5rem; box-shadow: var(--shadow-sm); }
.avatar { width: 3rem; height: 3rem; border-radius: 50%; background: var(--primary); }
.name { font-weight: 600; }
.subtitle { font-size: 0.875rem; color: var(--muted-foreground); }
Enter fullscreen mode Exit fullscreen mode

CSS Modules advantages: Better TypeScript completion for class names, more predictable specificity, easier to move between frameworks, familiar to CSS developers.

The Tailwind Resistance Arguments

"Tailwind clutters JSX" — Valid concern for complex components. Counter: component abstraction solves this.

// Instead of long className strings everywhere:
const cn = (...classes: string[]) => classes.filter(Boolean).join(' ');

function Card({ children, className }: { children: React.ReactNode; className?: string }) {
  return (
    <div className={cn('rounded-lg border bg-card p-6 shadow-sm', className)}>
      {children}
    </div>
  );
}

// Usage is clean
Enter fullscreen mode Exit fullscreen mode

"Tailwind is hard to read" — True for newcomers, not for experienced users. The utility names become second nature within a few weeks.

When to Use CSS Modules Instead

CSS Modules are worth considering when:

  • Your team has strong CSS expertise and prefers traditional workflows
  • You need precise control over specificity
  • You're building a design system that will be consumed outside of React
  • You're on a framework where Tailwind doesn't integrate well (rare in 2026)

Otherwise: Tailwind + shadcn/ui is the fastest path to a polished SaaS UI.


Find boilerplates by styling approach on StarterPick.

Top comments (0)