tailwind cssvscss modules
for: component-driven React/Next.js/Svelte work where design system consistency and iteration speed matter more than stylesheet purity
skip if: teams migrating large existing codebases, those with strict CSS naming conventions, or server-rendered apps where class verbosity in markup is a concern
tailwind won this debate in practice. the ecosystem, the tooling, and the community have converged around it. css modules are still a legitimate and clean choice, but if you're starting a new project today and don't have strong objections, tailwind is the default.
tailwind for new projects, especially with react and component frameworks. css modules for teams with existing css investments or strong preferences against utility classes.
what you're actually comparing
Tailwind CSS is a utility-first CSS framework. Instead of writing padding: 1rem in a CSS file, you add p-4 as a class on your element. Instead of display: flex; justify-content: center, you write flex justify-center. Everything is a utility class; you compose styles by combining them in your markup.
CSS Modules is a build-time feature (supported by Webpack, Vite, Next.js, etc.) that scopes CSS class names to the component that imports them. You write normal CSS, but .button in Button.module.css becomes something like .Button_button__abc12 at build time, preventing class name collisions.
Both solve the same problem — styling components without global CSS conflicts — but with very different philosophies.
where tailwind wins
Speed of iteration. You make a style change by editing the JSX, not by switching to a CSS file, finding the right selector, and toggling back. For rapid prototyping, this removes a massive amount of friction. The feedback loop is tighter.
Design system out of the box. Tailwind comes with a sensible spacing scale, color palette, font sizes, and breakpoints baked in. When your whole team uses p-4, p-8, text-sm, text-lg — you're automatically on the same grid. CSS Modules has no equivalent guardrails; each developer invents their own values.
No dead CSS. Tailwind's build-time analysis removes every utility you don't use. CSS Modules doesn't prune — you can accumulate hundreds of dead style rules over a project's lifetime.
Co-location. Styles live directly in the markup that uses them. You never open a .module.css file to find that the class you're looking for was renamed three months ago and the old name is still there.
Ecosystem and community. Tailwind is what shadcn/ui, most Next.js templates, and the majority of open-source React components assume. The ecosystem momentum is decisive.
where css modules wins
Familiarity. CSS Modules lets you write CSS. If you know CSS, you know CSS Modules. There's no framework vocabulary to learn, no class-name-to-property mapping to internalize.
Complex styles that fight utilities. Some styles are genuinely easier in CSS — complex selectors, :nth-child rules, multi-step animations, pseudo-element heavy designs. These can be done in Tailwind (via @layer or arbitrary values) but become awkward.
Existing codebase migration. If you have thousands of lines of CSS already, CSS Modules is a lower-migration-cost upgrade than converting to Tailwind utilities. You restructure into module files; you don't rewrite every style.
Cleaner JSX output. CSS Modules keeps your JSX clean: className={styles.button} instead of className="flex items-center px-4 py-2 text-sm font-mono border border-rule rounded-sm hover:border-ink". If markup readability is important to your team, this matters.
No framework dependency. CSS Modules is a build-tool feature, not a framework. It works in any project, has no version to upgrade, and won't change its API. Tailwind's major versions have had breaking changes.
things to know
Tailwind arbitrary values are a smell. If you find yourself writing text-[#3a3a3a] or h-[47px] frequently, you might be fighting the system. This often means you should extend the config rather than use arbitrary values inline.
CSS Modules and global styles coexist awkwardly. Styling child elements, targeting third-party components, and global reset styles in a CSS Modules project require workarounds (:global() selector, separate global CSS file). Tailwind has the same issues but they're more documented.
Tailwind v4 is a significant change. Tailwind v4 moves configuration from tailwind.config.js to @theme declarations in CSS files. If you're referencing tutorials or configs from before 2025, some of it won't apply directly.
The debate has settled. In 2022 this was contested. In 2026, Tailwind has won in the React/Next.js ecosystem. Starting a new project in CSS Modules requires a deliberate reason, not just preference.
frequently asked
Does Tailwind produce more CSS than CSS Modules?
Is Tailwind hard to learn?
Can I use Tailwind with CSS Modules?
What about readability? Tailwind class strings look long.
Does shadcn/ui use Tailwind?
What's the alternative if I don't want either?
some links on this page are affiliate links. we earn a small commission if you sign up, at no extra cost to you. we don't change verdicts for affiliate money — see how this site makes money.
last updated: june 14, 2026
related
shadcn/ui vs MUI
shadcn ui for tailwind-based projects and teams who want to own their components. mui for teams who want a complete design system out of the box.
Framer Motion vs GSAP
framer motion for react component animations. gsap for complex timelines, scroll-driven sequences, and non-react environments.