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 Button whose .button class never clashes with another component's .button.

Code compare

React
/* 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.

Angular
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, :host styles the <button> itself because of the attribute selector pattern (see React Essentials, Entry 5).

Quick reference

ReactAngular
Button.module.cssbutton.component.css (scoped by default)
className={styles.button}class="button"
clsx(styles.a, cond && styles.b)[class.b]="cond"