Tailwind vs CSS Modules Comparison
Compare Tailwind utilities and CSS Modules across scoping, performance, refactoring, and team workflow to pick the right styling system.
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 { ... }
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.
Related articles
- Tailwind Tailwind Animation and Transition Utilities
Animate hover states, page mounts, and component transitions with Tailwind's transition and animation utilities and a few custom keyframes.
- Tailwind Tailwind Arbitrary Values and the JIT Engine
How Tailwind's JIT engine generates classes on demand, when arbitrary values are the right tool, and how to keep your design system tidy.
- Tailwind Tailwind Custom Plugins Tutorial
Write your own Tailwind plugins to add utilities, components, and variants that match your design system without reaching for arbitrary values everywhere.
- Tailwind Customizing Tailwind: Theme and Design Tokens
Extend Tailwind's theme with custom colors, spacing, and design tokens that scale across a real product without fighting the framework.