Skip to Content
React in a Week
Day 2 · Describing the UI

Styling components

Style React components with className and CSS files, build class names from your data, and use inline styles for values that change.

Your components now show the right data, but they look like a plain document from 1995. React itself has no opinion about how you style things. Underneath, your JSX becomes ordinary HTML elements, so ordinary CSS works.

There are a few small differences from writing CSS for plain HTML, and a few patterns that make styling from data easy. That is what this lesson covers.

className and a stylesheet

In HTML you add a class with class="...". In JSX you write className instead. JSX is JavaScript, and class is a reserved word in JavaScript, so React uses the name of the matching DOM property, className.

Live code· className and styles.css

In these sandboxes, styles.css is loaded for you. In a real project, you load a stylesheet by importing it from a JavaScript file. A build tool such as Vite sees the import and adds the CSS to the page:

src/main.jsx
import './styles.css';

You can also give a component its own stylesheet and import it right next to the component. The CSS still applies to the whole page, so give your classes specific names to avoid clashes:

Live code· A component with its own CSS file

Class names from data

Because className takes any string, you can build it from props. A template literal (the string with backticks you met on Day 1) is the usual tool:

Live code· A badge that changes color by status

When status is 'done', the class string becomes "badge badge-done", and the matching CSS rule applies. The JSX stays short, and all the colors live in CSS where they belong.

Adding a class only sometimes

To add a class only when a condition is true, combine a ternary with the template literal:

<article className={`book-card ${book.status === 'done' ? 'is-done' : ''}`}>

That works, but it gets messy with several optional classes. A cleaner trick is to list the possible classes in an array, drop the falsy ones, and join the rest with spaces:

const className = [
  'book-card',
  book.status === 'done' && 'is-done',
  book.rating === 5 && 'is-favorite',
]
  .filter(Boolean)
  .join(' ');

filter(Boolean) removes every false from the array, so only the classes that apply are left.

Deep diveThe clsx helper

Building class strings is so common that many projects use a tiny library called clsx for it. It accepts strings and objects, and includes a class when its value is true:

import clsx from 'clsx';

<article className={clsx('book-card', { 'is-done': book.status === 'done' })}>

You don't need it for this course, but you will see it in many React codebases.

Quick check

A component renders a span whose className is the template literal badge badge-${status}. What is the class string when status is 'reading'?

Inline styles with the style prop

Sometimes a style depends on a number that can be anything, such as how far through a book you are. Writing a CSS class for every percentage would be silly. For values like that, use the style prop.

In JSX, style takes a JavaScript object, not a string. That is why you see double curly braces: the outer pair means "JavaScript here" and the inner pair is the object.

Live code· A progress bar with inline styles

Notice that most of the styling still lives in CSS. Only the one value that comes from data, the width, is inline.

Two rules for the style object:

  • Property names are camelCase. CSS background-color becomes backgroundColor, and font-size becomes fontSize.
  • Numbers become pixels for size properties. style={{ marginTop: 8 }} means 8px. For any other unit, pass a string such as '50%' or '2rem'.

Prefer className for most styling. CSS classes are easier to reuse, can handle hover states and media queries, and keep your JSX readable. Save inline style for values that truly come from your data.

Quick check

What does style={{ width: 50 }} set the width to?

Other ways to style

Plain CSS files are all you need for this course. In real projects you'll often meet two other approaches, so it helps to recognize them.

CSS Modules keep class names local to one component. You name the file something.module.css, import it as an object, and read class names from that object. The build tool renames each class to something unique, so two components can both have a .card class without clashing. Vite supports this out of the box:

src/BookCard.module.css
.card {
  padding: 16px;
  border-radius: 12px;
}
src/BookCard.jsx
import styles from './BookCard.module.css';

export function BookCard({ book }) {
  return <article className={styles.card}>{book.title}</article>;
}

Tailwind CSS takes a different route. Instead of writing your own classes, you combine many small ready-made utility classes directly in className:

<article className="rounded-xl border border-slate-200 p-4 shadow-sm">
  <h2 className="text-xl font-semibold">{book.title}</h2>
</article>

Both work with everything you've learned today, because in the end they only produce a className string. The site you're reading right now is styled with Tailwind.

Try it

Style the Reading List card

Make BookCard look like a finished card:

  1. Give the badge the classes badge and badge- plus the status, so each status gets its own color.
  2. Add the class is-done to the article only when the book's status is 'done'. The CSS for it is already written.
  3. Show the progress bar only for books with status 'reading', using the book's progress field for the width.
Live code· Challenge: styling
Show solution

Template literals build both class strings, and && shows the progress bar only while reading. The width is the one inline style.

Live code· Solution: styling

The progress field is only for this exercise. The Reading List's book data doesn't include it, so the project checkpoint leaves the progress bar out.

Day 2 progress

0/7
Next: Day 3: Interactivity

On this page