Skip to content
C Codeloom
Tailwind

Tailwind vs CSS Modules Comparison

Compare Tailwind utilities and CSS Modules across scoping, performance, refactoring, and team workflow to pick the right styling system.

·4 min read · By Codeloom
Beginner 8 min read

What you'll learn

  • How Tailwind and CSS Modules scope styles
  • Build output and bundle size
  • Refactoring and renaming workflow
  • Component reuse and design tokens
  • Team and codebase fit

Prerequisites

  • Comfortable with HTML and JavaScript

What and Why

Tailwind and CSS Modules are two popular ways to write component styles. Tailwind ships a giant set of utility classes you compose directly on elements. CSS Modules let you write traditional class-based CSS that the build tool scopes per file. Both solve global namespace collisions but feel very different to work with.

Mental Model

Tailwind says: keep markup and styles together with short utility classes. CSS Modules say: keep styles in a sibling file and reference them by name. Tailwind tends to make components self-contained at the cost of long class strings. CSS Modules tend to make markup cleaner at the cost of jumping between files.

Tailwind                         CSS Modules
<button class="bg-blue-600       <button class={styles.save}>
px-3 py-1 rounded">                Save
Save                              </button>
</button>                         styles.module.css:
                                  .save { ... }
Where the styling lives

Hands-on Example

A Tailwind button.

export function Button({ children }) {
  return (
    <button className="rounded bg-blue-600 px-3 py-1 font-medium text-white hover:bg-blue-700">
      {children}
    </button>
  );
}

The same button with CSS Modules.

// Button.jsx
import styles from './Button.module.css';

export function Button({ children }) {
  return <button className={styles.save}>{children}</button>;
}
/* Button.module.css */
.save {
  background: #2563eb;
  color: white;
  padding: 0.25rem 0.75rem;
  border-radius: 0.25rem;
  font-weight: 500;
}
.save:hover {
  background: #1d4ed8;
}

For a single component the line counts are close. The difference shows up at scale. Tailwind never grows the stylesheet because every class is reused. CSS Modules accumulate one file per component, but each file is small and self-explanatory.

For shared design tokens, Tailwind uses its config.

// tailwind.config.js
module.exports = {
  theme: {
    extend: {
      colors: { brand: { 500: '#2563eb', 600: '#1d4ed8' } },
      borderRadius: { card: '0.75rem' },
    },
  },
};

CSS Modules use custom properties.

/* tokens.css */
:root {
  --color-brand: #2563eb;
  --radius-card: 0.75rem;
}
.save {
  background: var(--color-brand);
  border-radius: var(--radius-card);
}

Both keep designers happy, but Tailwind’s classes appear in autocomplete and lint warnings, which is hard to match with hand-written CSS.

Common Pitfalls

Tailwind class strings can grow long. Without a system to extract repeated combinations, components become hard to scan. Most teams reach for clsx plus small helper components or the @apply directive in a layer to tame the noise.

CSS Modules invite global leaks if you compose with raw :global selectors. Use them sparingly and prefer scoped class composition with composes instead.

Refactoring is asymmetric. Renaming a Tailwind utility is a config change that ripples through every file. Renaming a CSS Modules class is a local change in one component. Removing a Tailwind class from one element does not affect others. Removing a CSS class accidentally breaks every consumer.

Practical Tips

Use Tailwind for design system rich UIs with consistent spacing, color, and typography. The utility-first approach enforces the system because there is no easy way to invent a one-off pixel value.

Use CSS Modules for products with very custom visuals that do not map well to utilities, like data visualization or graphic-heavy interfaces. The freedom to write any CSS without fighting a system pays off when the design is unusual.

The two are not mutually exclusive. You can use Tailwind for layout and CSS Modules for the few components that need bespoke styling. Vite and Next.js both support this combination out of the box.

For very large teams, Tailwind’s lint rules and class sorter (prettier-plugin-tailwindcss) keep style consistent across thousands of files. CSS Modules rely more on team discipline.

Wrap-up

Tailwind and CSS Modules answer the same question with different tradeoffs. Tailwind co-locates styles with markup, enforces a design system, and ships a small CSS file. CSS Modules give you full CSS power per component without global leaks. Pick Tailwind when consistency matters most, CSS Modules when expression matters most, and feel free to mix the two when your codebase has both kinds of pages.