Styling React ComponentsEntry 4 of 7
CSS Modules
CSS files named *.module.css whose class names are scoped to the component that imports them.
Notes
- The bundler rewrites each class name to a unique one (e.g.
.button→.Button_button__x7a2k), so styles can't leak to other components. - The file is imported as an object; each class is a property:
styles.button. - Hyphenated class names need bracket access (
styles['primary-button']), so camelCase names are more convenient. - Vite supports CSS Modules out of the box, including TypeScript types through
vite/client.
What / Why / When
- What
Component-scoped CSS with plain CSS syntax.
- Why
You keep writing normal CSS but get no class-name collisions.
- When
A good default for most React projects that don't use Tailwind.
- Use case
A reusable
Buttonwhose.buttonclass never clashes with another component's.button.
Code compare
/* Button.module.css */.button { padding: 0.5rem 1rem; background-color: #2563eb; color: white; border: none; border-radius: 0.25rem;}.button:hover { background-color: #1d4ed8;}.danger { background-color: #dc2626;}No extra setup is needed: every component's styles already behave like CSS Modules.
import { Component, input } from "@angular/core";@Component({ selector: "button[appButton]", template: `<ng-content />`, styles: ` :host { padding: 0.5rem 1rem; background-color: #2563eb; color: white; border: none; border-radius: 0.25rem; } :host(:hover) { background-color: #1d4ed8; } :host(.danger) { background-color: #dc2626; } `, host: { "[class.danger]": "danger()" },})export class ButtonComponent { danger = input(false);}Differences and gotchas
- CSS Modules scope by rewriting class names. Angular scopes by adding generated attributes (
_ngcontent-xyz) to elements and selectors. - In the Angular example,
:hoststyles the<button>itself because of the attribute selector pattern (see React Essentials, Entry 5).
Quick reference
| React | Angular |
|---|---|
Button.module.css | button.component.css (scoped by default) |
className={styles.button} | class="button" |
clsx(styles.a, cond && styles.b) | [class.b]="cond" |