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.
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:
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:
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:
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.
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.
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-colorbecomesbackgroundColor, andfont-sizebecomesfontSize. - Numbers become pixels for size properties.
style={{ marginTop: 8 }}means8px. 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.
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:
.card {
padding: 16px;
border-radius: 12px;
}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.
Style the Reading List card
Make BookCard look like a finished card:
- Give the badge the classes
badgeandbadge-plus the status, so each status gets its own color. - Add the class
is-doneto thearticleonly when the book's status is'done'. The CSS for it is already written. - Show the progress bar only for books with status
'reading', using the book'sprogressfield for the width.
Show solution
Template literals build both class strings, and && shows the progress bar only while reading. The width is the one inline style.
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