CSS Modules, Tailwind, CSS-in-JS — A Map
Styling Options
A quick map of the major React styling approaches and when each one fits.
What you'll learn
- Recognize the major styling approaches
- Pick one for a new project
React doesn’t care how you style your components. The ecosystem has a handful of popular options, each with its own trade-offs.
Plain CSS
Just import a CSS file:
// App.jsx
import "./App.css";
function App() {
return <button className="btn">OK</button>;
} Simplest possible setup. Class names are global — your problem to keep them organized. Fine for very small projects.
CSS Modules
Locally scoped class names — no global collisions.
// Button.module.css
.btn { padding: 10px; }
// Button.jsx
import styles from "./Button.module.css";
function Button() {
return <button className={styles.btn}>OK</button>;
} Vite/Next.js support CSS Modules out of the box. You write normal CSS but get unique class names per file.
Tailwind CSS
Utility-first classes. No CSS files for components — you compose classes in the JSX.
<button className="px-4 py-2 rounded bg-blue-500 text-white hover:bg-blue-600">
OK
</button> Pros: very fast to build with, no class-naming questions, automatic purging of unused styles. Cons: classes can pile up; opinionated about visual design.
CSS-in-JS — styled-components / Emotion
Write CSS inside your JS:
import styled from "styled-components";
const Button = styled.button`
padding: 10px;
background: ${p => p.kind === "danger" ? "red" : "blue"};
`;
<Button kind="danger">Delete</Button> Pros: dynamic styles from props feel natural. Cons: runtime cost, and the React Server Components story is awkward.
CSS-in-JS — “Zero-runtime”
Tools like vanilla-extract and Linaria offer the same author experience but compile to real CSS files (no runtime). The current sweet spot for typed CSS without performance trade-offs.
Quick Comparison
| Approach | Setup | Power | Bundle cost | Best for |
|---|---|---|---|---|
| Plain CSS | None | Low | Tiny | Demos |
| CSS Modules | Tiny | Medium | Tiny | Most apps |
| Tailwind | One config | High | Tiny (after purge) | Speed-focused builds |
| styled-components | Library | High | Larger | Highly dynamic styles |
| vanilla-extract | Library | High | Tiny | Type-safe design systems |
Pick One
There’s no objectively right answer. Pick by:
- What your team knows
- What your design system requires
- Whether you’re using a framework with opinions (Next.js works well with all of these)
For a new app on your own: Tailwind for speed, CSS Modules if you want clean separation, vanilla-extract if you’re TypeScript-heavy.
Up Next
Forms get complicated. A library helps.
Forms Library →