Styling components
React has no styling system of its own. You write ordinary CSS, and JSX gives you two ways to attach it to an element: a class name, which covers almost everything, and a style prop for the handful of values a stylesheet has no way of knowing ahead of time. This chapter covers both, along with the pattern that sits between them: building a class name out of props or state, so an element restyles itself as your data changes.
Class names in JSX
class is a reserved word in JavaScript, so JSX names the attribute className. The value is a plain string, and React sets it as the element's class attribute in the DOM, which means the CSS on the other side is written exactly the way it always was.
function Avatar() {
return <img className="avatar avatar--large" src={photo} alt="Ada Lovelace" />
}Two class names in one string, separated by a space, the same as HTML. The camelCase rule from JSX applies to the attribute names in your markup; the class names themselves are yours, so kebab-case, BEM, or whatever convention your stylesheet already uses carries over untouched.
Importing a stylesheet
A component file pulls in the CSS it needs with a bare import at the top:
import './App.css'
export default function App() {
return <h1 className="title">React Facts</h1>
}There is no variable on the left of that import, and the component never reads the file. It is an instruction to the build tool: this module depends on that CSS, so include it. Vite injects the rules into the page during development and emits a real .css file in a production build. Setting up a React project describes the same mechanism handling images.
Where you put the import is a question of organization. Importing Button.css at the top of Button.jsx keeps the styles beside the component that uses them. The rules themselves stay global: a .title selector matches every element carrying that class anywhere on the page, whichever file happened to import it. Naming conventions like BEM exist to keep that shared namespace manageable, and CSS Modules and CSS-in-JS libraries scope the rules for you once an app outgrows naming discipline alone.
Version note
React 19 added support for rendering <link rel="stylesheet"> and <style> from inside a component, with a precedence prop that lets React hoist them into the document head and order them predictably. Earlier versions needed a library such as react-helmet. The build-tool import above behaves the same way in every version.
Class names that depend on data
className sits inside curly braces like any other JSX expression, so the string can be computed. Take a dice game where each die can be held between rolls. Whether a die is held lives in state higher up and arrives here as a prop:
function Die({ value, isHeld }) {
return (
<button className={`die ${isHeld ? 'die--held' : ''}`}>
{value}
</button>
)
}Every die gets die. A held one also gets die--held. The stylesheet holds the actual colors and borders, and the component's only job is deciding which class applies. When isHeld flips, React updates the class attribute and the browser repaints. Nothing inside the component knows what "held" looks like, so that appearance can change without this file changing at all.
One wrinkle: when isHeld is false, the template literal produces "die " with a trailing space. Browsers ignore it, though it looks untidy in devtools. An array handles it more cleanly and scales past one condition:
const classes = ['die', isHeld && 'die--held'].filter(Boolean).join(' ')
return <button className={classes}>{value}</button>isHeld && 'die--held' evaluates to false when the condition fails, filter(Boolean) drops it, and join(' ') puts a single space between whatever survives.
The style prop
The style prop takes a JavaScript object. Property names are the camelCased versions of the CSS ones, and the double braces are the usual expression slot with an object literal inside it:
<div style={{ backgroundColor: 'darkslateblue', paddingTop: 12 }}>
Styled inline
</div>background-color becomes backgroundColor and padding-top becomes paddingTop. Values are usually strings. Pass a bare number and React appends px for properties that take a length, so paddingTop: 12 renders as padding-top: 12px. Properties that CSS treats as unitless keep the raw number: lineHeight, opacity, flexGrow, zIndex and fontWeight all pass straight through. Any other unit has to be written as a string, as in width: '60%' or margin: '2rem'.
Most styling belongs in a stylesheet, where one class can carry a dozen declarations and be shared by a dozen elements. The style prop earns its place when a value is only known at runtime and CSS has no way to reach it:
function ProgressBar({ percent }) {
return (
<div className="progress">
<div className="progress__fill" style={{ width: `${percent}%` }} />
</div>
)
}The fill's color, height, transition and corner radius all live in .progress__fill. The single value the stylesheet cannot know is the width, because it comes from data, so that one value goes inline. A background image whose URL arrives from an API has the same shape:
<div className="hero" style={{ backgroundImage: `url(${photo.url})` }} />The sizing, positioning and overlay stay in .hero, and only the URL comes through the prop.
Inline styles also have a hard ceiling. A style object describes one element's declarations, so there is no way to express a hover state, a media query, or a keyframe animation in it. Those belong in CSS regardless of how dynamic the rest of the styling gets.
className because class is a reserved word in JavaScript. Import your stylesheet at the top of the file, put your rules in it, and let the component decide which class applies by building the string from props or state. Save the style prop for values your CSS file cannot know in advance, like a width that comes from data. Next up: Props, where components start taking data from their parent.

