React changes how CSS gets applied to markup, from the className attribute to inline style objects and locally scoped CSS Modules. This lesson covers the core styling techniques available in React and when each one makes sense.
1Styling React
Building a React application is only half the job ā the other half is making it look good. In traditional HTML, styling means the class attribute plus a separate CSS file, but because JSX compiles down to JavaScript function calls, React handles styling a bit differently.
React supports several distinct approaches to styling, from basic class names to dynamic inline styles and locally scoped CSS Modules, each suited to different situations.
// Styling React ComponentsAesthetics Engine
Bridging the gap between JS and CSS.
2The className Attribute
The most fundamental rule of React styling: never use class in JSX. class is a reserved keyword in JavaScript, used for defining object-oriented classes, and since JSX compiles down to JavaScript function calls, using class as an attribute name would conflict with that.
React requires className instead, and automatically translates it back into a standard class attribute when it renders the actual HTML to the DOM ā so the underlying browser output is identical to writing class directly.
function MyComponent() {
return <div className='container'>Hello!</div>;
}User Info
3Inline Styles Overview
Importing an entire CSS file can be overkill for a single style, especially one that needs to change dynamically based on state. React supports inline styles through the style prop ā but unlike standard HTML, where style takes a CSS string, React requires a JavaScript object instead.
A style object like const headerStyle = { backgroundColor: 'blue', fontSize: '24px' } gets passed directly as <h1 style={headerStyle}>, applying those declarations straight to the element.
const headerStyle = {
backgroundColor: 'blue',
fontSize: '24px'
};
<h1 style={headerStyle}>Styled Header</h1>Inline Objects
JS replaces CSS strings
4camelCase Properties
Inside a React inline style object, every CSS property that normally contains a hyphen ā like background-color or font-size ā must be rewritten in camelCase, as backgroundColor and fontSize. This isn't a React-specific quirk; it's because hyphens aren't valid inside unquoted JavaScript object property names.
So style={{ backgroundColor: 'black', color: 'white' }} is correct, while style={{ background-color: 'black' }} is a syntax error ā properties without hyphens, like color, stay exactly as they are in CSS.
<div className={`box `}></div>
<p style={{ color: isError ? 'red' : 'black' }}>Status</p>camelCase Required
For all hyphenated properties.
5Double Curly Braces
Inline styles are often written with what looks like a special double-curly-brace syntax, like style={{ color: 'red' }} ā but there's nothing magic about it. It's simply a JavaScript object literal placed inside a normal JSX interpolation.
The outer { } tells JSX "this is JavaScript, not a string," and the inner { } is the object literal itself, with color: 'red' as one of its properties ā two separate, ordinary pieces of syntax that happen to sit next to each other.
import './styles.css';
function App() {
return <div className='main-app'>Content</div>;
}Double Braces {{ }}
Just an object inside an interpolation
6Dynamic Inline Styles
Inline styles are especially powerful because the style prop is just a JavaScript object, which means state variables and ternary operators can be used directly inside the style declaration ā no separate CSS class needed.
Something like style={{ backgroundColor: isError ? 'red' : 'green', opacity: isFading ? 0.5 : 1 }} produces a UI element that reacts fluidly to state changes, recalculating its appearance on every render.
import styles from './Button.module.css';
<button className={styles.primary}>Click Me</button>65% Complete
7Dynamic Class Names
Inline styles are best for highly specific, dynamic values, like an exact progress bar percentage ā but for larger stylistic changes, dynamically toggling a className string is usually preferable to computing every property inline.
JavaScript template literals make this straightforward: something like className={btn ${isSelected ? 'btn-selected' : ''}} conditionally appends a class name based on a state variable, letting the actual visual rules live in CSS rather than inline JS objects.
// Choose the right tool for the job8Global CSS Imports
For standard application-wide styling, a plain .css file can be imported directly at the top of a component file, like import './global.css'. This tells the bundler (Vite, Webpack, etc.) to include those styles in the final build.
The catch is that these class names are globally scoped by default ā a class defined in one file's CSS can unintentionally collide with a same-named class used by an entirely different component elsewhere in the app.
<h1>Styling: Mastered</h1>Global Scope
Can cause naming collisions
9CSS Modules
CSS Modules solve the global-collision problem directly. Naming a file with a .module.css extension, like Button.module.css, tells React's build tooling to automatically generate a unique, locally scoped class name for every class defined in that file.
Instead of a plain string, the file is imported as an object ā import styles from './Button.module.css' ā and referenced by property, as in <button className={styles.primary}>, guaranteeing that class can't accidentally clash with one from another component.
import './global.css';10Mastery Achieved
Putting it together: className is the required replacement for class in JSX, inline style objects handle values that need to change dynamically based on state, and CSS Modules solve the naming-collision problem that plain global CSS imports create.
Choosing between them comes down to the situation ā reach for className and CSS/CSS Modules for most static and reusable styling, and inline styles specifically for values computed at render time from component state.
<h1>Design: Premium</h1>Design Aesthetics Mastered ā
11Step-by-Step Breakdown
Styling React. Welcome to React Styling. Building a React application is only half the battle; the other half is making it look incredible. In traditional HTML, you use the 'class' attribute and a separate CSS file. In React, because we are writing JSX (which compiles down to JavaScript), things work a bit differently. Today, we'll master the various techniques to style React applications, from basic class names to dynamic inline styles and scoped CSS modules.
The className Attribute. The most fundamental rule of React styling: NEVER use 'class'. 'class' is a reserved keyword in JavaScript (used for defining Object-Oriented classes). Because JSX is really just syntactic sugar for JavaScript functions, React requires you to use 'className' instead to avoid conflicts. Under the hood, React automatically translates 'className' back into a standard HTML 'class' attribute when it renders to the DOM.
Which attribute MUST you use in JSX to apply CSS classes to an element?
- āclass
- āclassList
- āclassName
Inline Styles Overview. Sometimes, importing an entire CSS file is overkill for a single specific style, especially if that style needs to change dynamically based on state. React supports inline styles via the style prop. However, instead of passing a CSS string like in standard HTML, you must pass a JavaScript object.
camelCase Properties. When defining an inline style object in React, there is a critical rule: all CSS properties that contain a hyphen (like background-color or font-size) MUST be converted to camelCase (backgroundColor and fontSize). This is because hyphens are not valid in standard JavaScript object property names without quotes.
How should the CSS property margin-top be written inside a React inline style object?
- āmargin-top
- āmarginTop
Double Curly Braces. You will often see inline styles written with double curly braces, like style={{ color: 'red' }}. This isn't a special JSX syntax; it's simply an object literal placed inside an interpolation block. The outer braces { } tell JSX we are injecting JavaScript, and the inner braces { } define the actual JavaScript object.
Dynamic Inline Styles. The true power of inline styles lies in their ability to be dynamic. Because the style prop takes a JavaScript object, you can use state variables and ternary operators directly within the style declaration to create fluid, interactive UI elements without touching CSS classes.
If isPrimary is a state boolean, how would you dynamically set the color property using a ternary operator inside an inline style?
- ā{isPrimary ? 'blue' : 'gray'}
- āisPrimary ? 'blue' : 'gray'
Dynamic Class Names. While inline styles are good for highly specific, dynamic values (like a progress bar percentage), you should prefer dynamic className strings for larger stylistic changes. You can use JavaScript template literals (backticks) to conditionally inject class names based on state variables.
Global CSS Imports. For standard application-wide styling, you can simply import a standard .css file directly at the top of your React component file. This signals to your bundler (like Vite or Webpack) to include those styles in the final build. However, note that these classes become globally scoped and can conflict with other components.
CSS Modules. To solve the problem of global CSS collisions, React supports CSS Modules out of the box. If you name your file ending in .module.css, React will locally scope all class names automatically. You import the styles as an object, and reference them as properties.
If you want a CSS file to be locally scoped to prevent naming conflicts, what file extension pattern should you use?
- ālocal.css
- āmodule.css
Mastery Achieved. Styling mastery achieved! You now know how to build beautiful, dynamic interfaces using React. You understand why className is required, how to write JS objects for inline styles, and when to use template literals or CSS Modules for complex visual logic.
Level Up š
Advanced cheat sheets, SEO tricks, and interview prep for this topic.
Browser Support
Fully supported.
Fully supported.
Fully supported.
Fully supported.
Accessibility (A11y)
1Visual State Shouldn't Be the Only Signal
When dynamic inline styles or conditional class names communicate state, such as a red border for an invalid form field, pair that visual change with a programmatic signal like `aria-invalid` so assistive technology conveys the same information.
2CSS Modules Don't Change Underlying Semantics
Locally scoped class names from CSS Modules only affect naming and specificity ā the actual HTML elements still need proper semantic tags and ARIA attributes; scoping styles doesn't substitute for accessible markup.
SEO Implications
- 1
CSS Import Strategy Doesn't Directly Affect Crawlability
Whether styles come from global CSS, CSS Modules, or inline style objects has no direct bearing on SEO ā what matters for crawlers is the text content and semantic HTML structure that ends up in the rendered DOM, not how it's styled.
- 2
Avoid Layout Shift from Late-Loading Styles
Global CSS files that load after initial content renders can cause visible layout shift, which search engines factor into page experience signals ā CSS Modules and colocated styles tend to load more predictably alongside the components that need them.
Best Practices
Reserve Inline Styles for Values Computed at Render Time
Use the `style` prop for values that genuinely depend on component state or props, like a dynamic width percentage; for static or reusable styling, className with CSS or CSS Modules is easier to maintain.
Prefer CSS Modules Over Global CSS for Component-Specific Styles
Naming a file `Component.module.css` and importing it as an object avoids the class-name collisions that plain global `.css` imports are prone to as an application grows.
Frequent Bugs
An inline style silently fails to apply, or React throws a syntax error.
A hyphenated CSS property was written as-is instead of camelCase, e.g. `background-color` instead of `backgroundColor`. All hyphenated properties in a style object must use camelCase.
A class name defined in one component's CSS file unexpectedly styles an unrelated component.
The class was defined in a plain global `.css` file, where all class names share one global namespace. Rename the file to `Component.module.css` and import it as an object to get automatically scoped class names.
Real-World Examples
Dynamic Status Badge with Inline Styles
A status badge component reads a boolean from state to switch its background color and opacity on every render, using the style prop's ability to accept a plain JavaScript object computed from state.
function StatusBadge({ isError, isFading }) {
return (
<div style={{ backgroundColor: isError ? 'red' : 'green', opacity: isFading ? 0.5 : 1 }}>
Status
</div>
);
}