← Back

React.js + Next.js · Module 2. React Basics · Lesson 10

Styling Components

React components can be styled in several ways. Pizza Palace will use styles for menu cards, offer banners, availability messages, cart buttons, layouts, and responsive pages.

The most common approaches are:

  1. Inline styles.
  2. Regular CSS files.
  3. CSS Modules.

This lesson focuses mainly on CSS Modules. They provide locally scoped class names and reduce accidental conflicts between components.

You will learn

  1. How styling works in React.
  2. How to use inline styles and regular CSS files.
  3. Why JSX uses className instead of class.
  4. What global CSS classes are.
  5. What CSS Modules are and how Vite processes them.
  6. How to use styles.className.
  7. How to organize component styles.
  8. How to combine global CSS with CSS Modules.
  9. How to select a class conditionally.
  10. Common styling mistakes.

REACT · 01

1. How Styling Works in React

React does not replace CSS. Components return HTML-like JSX, and CSS controls how the resulting browser elements look.

CSS can control:

  • colours and fonts;
  • backgrounds and borders;
  • spacing and layout;
  • responsive design;
  • hover and focus states;
  • transitions and animations.
function PizzaCard() {
  return (
    <article>
      <h2>Margherita</h2>
      <p>Price: 8.50 €</p>
    </article>
  );
}

export default PizzaCard;

Browser Result — Without Custom CSS

Margherita

Price: 8.50 €

The component uses the browser's basic heading and paragraph styles.

We can now connect this markup to one of React's styling approaches.


REACT · 02

2. Inline Styles

The JSX style property accepts a JavaScript object.

function OfferBanner() {
  return (
    <p
      style={{
        padding: "12px 16px",
        color: "white",
        backgroundColor: "#b42318",
        borderRadius: "8px"
      }}
    >
      Tuesday offer: two Margheritas for 15 €!
    </p>
  );
}

export default OfferBanner;

Browser Result

Tuesday offer: two Margheritas for 15 €!

The JavaScript style object creates a red promotional banner.

The outer {} enters JavaScript inside JSX. The inner {} creates the style object.

CSS Names Become camelCase

CSS property React inline property
background-color backgroundColor
font-size fontSize
margin-top marginTop
border-radius borderRadius
text-align textAlign

Some properties accept numbers:

<div style={{ padding: 20, opacity: 0.8 }}>Pizza details</div>

React interprets padding: 20 as pixels. Properties such as opacity and lineHeight are unitless.

Inline styles are useful for small or dynamic values, but they are inconvenient for large stylesheets, pseudo-classes such as :hover, and media queries.


REACT · 03

3. Regular CSS Files

Regular CSS keeps styling separate from the JSX.

src/
└── components/
    ├── OrderNotice.tsx
    └── OrderNotice.css

OrderNotice.css

.order-notice {
  padding: 12px 16px;
  color: white;
  background-color: #b42318;
  border-radius: 8px;
}

OrderNotice.tsx

import "./OrderNotice.css";

function OrderNotice() {
  return (
    <p className="order-notice">
      Delivery takes approximately 35 minutes.
    </p>
  );
}

export default OrderNotice;

Browser Result

Delivery takes approximately 35 minutes.

The imported stylesheet applies its class to the notice.

The CSS import loads the file, while className="order-notice" assigns the class.


REACT · 04

4. Why React Uses className

Ordinary HTML uses class:

<article class="pizza-card">Margherita</article>

JSX uses the DOM property name className:

function PizzaCard() {
  return (
    <article className="pizza-card">
      <h2>Margherita</h2>
      <p>8.50 €</p>
    </article>
  );
}

Browser Result

Margherita

8.50 €

React turns className into the element's HTML class attribute.

Use className for both regular CSS and CSS Modules.


REACT · 05

5. The Problem with Global CSS Classes

Classes in regular CSS files are global by default. Imagine two files with the same selector:

AddToCartButton.css

.button {
  background-color: #2563eb;
  color: white;
}

CheckoutButton.css

.button {
  background-color: #16803c;
  color: white;
}

If both files load, both rules match every element with className="button". The final result depends on the cascade, specificity, and source order.

Names such as .button, .title, .card, and .container are likely to be repeated in a large application. CSS Modules reduce this risk by generating locally scoped class names.


REACT · 06

6. What Are CSS Modules?

A CSS Module is a CSS file whose class names are locally scoped when processed by the build tool. Vite recognises files ending in .module.css:

PizzaCard.module.css

Import the file as an object:

import styles from "./PizzaCard.module.css";

Then use a class from that object:

<article className={styles.card}>Margherita</article>

PizzaCard.module.css and CartSummary.module.css can both define .card; Vite generates different class names for them.


REACT · 07

7. Creating Your First CSS Module

Create a delivery notice with locally scoped styles.

src/
└── components/
    ├── DeliveryNotice.tsx
    └── DeliveryNotice.module.css

DeliveryNotice.module.css

.notice {
  padding: 12px 16px;
  background-color: #475467;
  border-radius: 8px;
  color: white;
}

DeliveryNotice.tsx

import styles from "./DeliveryNotice.module.css";

export default function DeliveryNotice() {
  return (
    <p className={styles.notice}>
      Free delivery for orders over 25 €.
    </p>
  );
}

App.tsx

import DeliveryNotice from "./components/DeliveryNotice";

export default function App() {
  return (
    <main>
      <h1>Pizza Palace</h1>
      <DeliveryNotice />
    </main>
  );
}

Browser Result

Pizza Palace

Free delivery for orders over 25 €.

The notice class belongs to DeliveryNotice.module.css.

REACT · 08

8. Understanding styles.notice

This import:

import styles from "./DeliveryNotice.module.css";

provides an object that maps source class names to generated class names. Vite may create a mapping conceptually similar to:

const styles = {
  notice: "_notice_a1b2c_1"
};

Therefore:

<p className={styles.notice}>Free delivery</p>

becomes HTML conceptually similar to:

<p class="_notice_a1b2c_1">Free delivery</p>

The generated name is implementation-dependent and can change. Never hard-code it in your component.


REACT · 09

9. Does the Import Name Have to Be styles?

No. The local variable name is the developer's choice.

import styles from "./DeliveryNotice.module.css";

<p className={styles.notice}>Free delivery</p>

This is also valid:

import css from "./DeliveryNotice.module.css";

<p className={css.notice}>Free delivery</p>

The important rule is consistency. If you import the module as styles, use styles.notice, not css.notice.

styles is a common convention because it clearly communicates the object's purpose.


REACT · 10

10. Multiple Classes in One CSS Module

One module can provide all classes needed by its component.

PizzaCard.module.css

.card {
  padding: 20px;
  border: 1px solid #e4e7ec;
  border-radius: 12px;
  background-color: white;
}

.title {
  color: #9f1d16;
  font-size: 24px;
}

.price {
  color: #16803c;
  font-size: 20px;
  font-weight: 700;
}

PizzaCard.tsx

import styles from "./PizzaCard.module.css";

export default function PizzaCard() {
  return (
    <article className={styles.card}>
      <h2 className={styles.title}>Pepperoni</h2>
      <p>Tomato, mozzarella, pepperoni</p>
      <p className={styles.price}>10.50 €</p>
    </article>
  );
}

Browser Result

Pepperoni

Tomato, mozzarella, pepperoni

10.50 €

The card, title, and price receive separate locally scoped classes.

REACT · 11

11. The Same Class Name in Different Components

Both components can define .card without affecting each other.

PizzaCard.module.css

.card {
  background-color: #fff3dc;
  border-left: 5px solid #d92d20;
  padding: 20px;
}

CartSummary.module.css

.card {
  background-color: #e8f2ff;
  border-left: 5px solid #2563eb;
  padding: 20px;
}
// PizzaCard.tsx
import styles from "./PizzaCard.module.css";

export function PizzaCard() {
  return <article className={styles.card}>Margherita · 8.50 €</article>;
}
// CartSummary.tsx
import styles from "./CartSummary.module.css";

export function CartSummary() {
  return <aside className={styles.card}>Your cart · 1 item</aside>;
}

Browser Result

Margherita · 8.50 €
Both source files use .card, but each component keeps its own appearance.

REACT · 12

12. How Vite Processes CSS Modules

CSS Modules are a build-tool feature, not a native browser feature.

PizzaCard.module.css
        ↓
Vite processes the module
        ↓
Unique class names are generated
        ↓
JavaScript receives a class-name mapping
        ↓
React assigns the generated class
        ↓
The browser applies the CSS

Source CSS:

.card {
  padding: 20px;
}

Illustrative generated CSS:

._card_a1b2c_1 {
  padding: 20px;
}

Illustrative mapping:

{ card: "_card_a1b2c_1" }

The browser receives the generated string returned by styles.card.


REACT · 13

13. CSS Modules Support Regular CSS Features

CSS Modules support pseudo-classes, media queries, CSS variables, animation, Flexbox, and Grid.

OrderButton.module.css

.button {
  padding: 10px 20px;
  border: 0;
  border-radius: 8px;
  background-color: #d92d20;
  color: white;
  cursor: pointer;
}

.button:hover {
  background-color: #9f1d16;
}

@media (max-width: 600px) {
  .button {
    width: 100%;
  }
}

OrderButton.tsx

import styles from "./OrderButton.module.css";

export default function OrderButton() {
  return (
    <button className={styles.button}>
      Add Margherita to cart
    </button>
  );
}

Browser Result

Desktop

Screen below 600 px

The same local class supports a hover colour and a responsive media query.

Move the pointer over the buttons on the real page to see the hover state.


REACT · 14

14. Global CSS vs CSS Modules

Both approaches can be used in one application.

index.css — Global Foundations

body {
  margin: 0;
  font-family: Arial, sans-serif;
  background-color: #fffaf5;
}

img {
  max-width: 100%;
}

PizzaCard.module.css — Component Styles

.card {
  padding: 20px;
  border-radius: 12px;
  background-color: white;
}
src/
├── components/
│   ├── PizzaCard.tsx
│   ├── PizzaCard.module.css
│   ├── OrderButton.tsx
│   └── OrderButton.module.css
├── App.tsx
├── index.css
└── main.tsx
Global CSS CSS Modules
Global selectors Locally scoped class names
Good for resets, fonts, and page foundations Good for component-specific styles
Naming conflicts are possible Naming conflicts are reduced
Imported for its side effect Imported as a class-name mapping

Use global CSS intentionally for truly shared rules and modules for component details.


REACT · 15

15. Practical Example — Styling Pizza Cards

The same component can style available and sold-out pizzas differently.

PizzaCard.module.css

.card {
  padding: 20px;
  border: 1px solid #e4e7ec;
  border-radius: 12px;
  background-color: white;
}

.title {
  margin-bottom: 8px;
  font-size: 22px;
}

.ingredients {
  color: #475467;
}

.price {
  color: #16803c;
  font-size: 20px;
  font-weight: 700;
}

.available {
  color: #16803c;
}

.unavailable {
  color: #b42318;
}

PizzaCard.tsx

import styles from "./PizzaCard.module.css";

interface PizzaCardProps {
  name: string;
  ingredients: string;
  price: number;
  available: boolean;
}

export default function PizzaCard(props: PizzaCardProps) {
  const availabilityClass = props.available
    ? styles.available
    : styles.unavailable;

  return (
    <article className={styles.card}>
      <h2 className={styles.title}>{props.name}</h2>
      <p className={styles.ingredients}>{props.ingredients}</p>
      <p className={styles.price}>{props.price.toFixed(2)} €</p>
      <p className={availabilityClass}>
        {props.available ? "Available" : "Sold out"}
      </p>
    </article>
  );
}

App.tsx

import PizzaCard from "./components/PizzaCard";

export default function App() {
  return (
    <main>
      <h1>Pizza Palace Menu</h1>
      <PizzaCard
        name="Margherita"
        ingredients="Tomato, mozzarella, basil"
        price={8.5}
        available={true}
      />
      <PizzaCard
        name="Truffle Pizza"
        ingredients="Mushrooms, mozzarella, truffle cream"
        price={14}
        available={false}
      />
    </main>
  );
}

Browser Result

Pizza Palace Menu

Margherita

Tomato, mozzarella, basil

8.50 €

Available

Truffle Pizza

Mushrooms, mozzarella, truffle cream

14.00 €

Sold out
One typed component displays different data and selects the availability class conditionally.

REACT · 16

16. Common Beginner Mistakes

Mistake 1 — Using class Instead of className

Incorrect: <article class="card">

Correct: <article className="card">

Mistake 2 — Using the Wrong Import Variable

If you write import styles from "./PizzaCard.module.css", use styles.card, not css.card.

Mistake 3 — Forgetting JSX Curly Braces

Incorrect:

<article className="styles.card">

This assigns the literal class name styles.card.

Correct:

<article className={styles.card}>

Mistake 4 — Incorrect File Name

PizzaCard.css is regular global CSS. Use PizzaCard.module.css when you want a CSS Module.

Mistake 5 — Importing a Module Only for Its Side Effect

This does not provide the styles object:

import "./PizzaCard.module.css";

Use:

import styles from "./PizzaCard.module.css";

Mistake 6 — Using Hyphenated Class Names with Dot Notation

For .pizza-card, use styles["pizza-card"]. A simpler module convention is .pizzaCard, which works as styles.pizzaCard.

Mistake 7 — Expecting Generated Names to Stay Fixed

Do not copy a generated name such as _card_a1b2c_1 into JSX, tests, or JavaScript selectors. Use the imported mapping.

Mistake 8 — Using Inline Styles for Everything

Inline styles are useful for genuinely dynamic values, but CSS Modules are clearer for hover states, media queries, reusable rules, and complete component designs.


REACT · 17

17. Practice Exercises

Exercise 1 — Inline Offer

Create a Pizza Palace offer using an inline background colour, padding, and border radius.

Exercise 2 — Regular CSS Notice

Create DeliveryNotice.css, import it into DeliveryNotice.tsx, and apply its class with className.

Exercise 3 — First CSS Module

Create PizzaCard.module.css with .card, .title, and .price, then use all three classes in PizzaCard.tsx.

Exercise 4 — Local Class Names

Create .card in both PizzaCard.module.css and CartSummary.module.css. Give them visibly different designs and render both components.

Exercise 5 — Hover and Focus

Add :hover and :focus-visible rules to an order button. Ensure keyboard users can see the focus indicator.

Exercise 6 — Conditional Availability

Use a Boolean prop to choose between styles.available and styles.unavailable.

Exercise 7 — Responsive Menu

Use a media query so a three-column pizza grid becomes one column on a narrow screen.


REACT · 18

18. Key Takeaways

  • React still uses CSS to style browser elements.
  • Inline styles accept JavaScript objects and camelCase property names.
  • Regular CSS classes are global unless another scoping system is used.
  • JSX uses className, not class.
  • Vite recognises CSS Modules by the .module.css extension.
  • Import a CSS Module as an object and use classes such as styles.card.
  • The import name is flexible, but it must be used consistently.
  • Generated class names are implementation details.
  • Two CSS Modules can safely define the same source class name.
  • CSS Modules support pseudo-classes, media queries, variables, Grid, Flexbox, and animations.
  • Global CSS is suitable for application-wide foundations; CSS Modules are suitable for component styles.
  • Conditional expressions can select different local classes from props or state.

REACT · 19

19. Further Reading

Next lesson: Class composition with clsx, using Pizza Palace component props and state to select styles cleanly.

← Back↑ Back to the top