Styling Components
Styling React components: className, CSS Modules, inline styles, conditional classes, variants, themes, responsive CSS, and choosing a styling strategy.
Styling Components
Styling Components is one of the foundational pieces of modern React engineering.
A React application is not only about functionality. A good application also needs a clear, consistent, and responsive visual design.
React provides several ways to style components, including:
- Regular CSS
- CSS Modules
- Inline styles
- Dynamic styles and conditional classes
- CSS-in-JS libraries
- Utility-first CSS frameworks
The important thing is to understand how styling connects with component structure and state. A React component should ideally control its structure and behavior while using appropriate styling techniques to control its appearance.
Learning Objectives
- Why React uses
classNameand how to import CSS - Global CSS, component CSS, and CSS Modules
- Inline styles, camelCase properties, and dynamic values
- Conditional classes, variants, and prop-driven styling
- Themes, dark mode, responsive layout, and accessibility
- When to choose CSS vs CSS Modules vs utility-first vs CSS-in-JS
1. Why Styling Matters in React
function Button() {return (<button>Save</button>);}
The component works, but has no visual styling. You may want padding, radius, colors, hover, focus, and responsive behavior.
React → Component structure + behaviorCSS → Visual presentation
React does not replace CSS — they work together.
2. Basic CSS with React
function Button() {return (<button className="primary-button">Save</button>);}
.primary-button {background: blue;color: white;padding: 10px 16px;border: none;border-radius: 6px;}
3. Why React Uses className
In JSX use className, not HTML's class:
function Card() {return (<div className="card"><h2>Product</h2><p>Description</p></div>);}
.card {padding: 20px;border: 1px solid #ddd;}
4. Importing a CSS File
src/├── components/│ ├── Button.jsx│ └── Button.css├── App.jsx└── main.jsx
import "./Button.css";function Button() {return (<button className="button">Save</button>);}export default Button;
.button {padding: 10px 16px;border-radius: 6px;border: none;}
5. Global CSS
body {margin: 0;font-family: Arial, sans-serif;}button {font-family: inherit;}
Useful for base typography, resets, body styles, root layout, utilities, and design tokens. Global class names can become hard to manage in large apps.
6. Component-Level CSS
Button.jsx / Button.cssCard.jsx / Card.cssNavbar.jsx / Navbar.css
import "./Card.css";function Card({ title, description }) {return (<article className="card"><h2 className="card-title">{title}</h2><p className="card-description">{description}</p></article>);}
.card {padding: 20px;border-radius: 8px;border: 1px solid #ddd;}.card-title {margin-bottom: 8px;}.card-description {color: #666;}
7. CSS Modules
Use Button.module.css for locally scoped class names:
.button {padding: 10px 16px;border-radius: 6px;}
import styles from "./Button.module.css";function Button() {return (<button className={styles.button}>Save</button>);}
Use className={styles.button} rather than className="button".
8. Why Use CSS Modules?
CSS Modules help prevent class-name collisions. With ordinary global CSS, two .title rules can interfere. With modules, styles.title belongs to that component's module.
Button.module.css → Scoped stylesCard.module.css → Scoped styles
9. CSS Modules Example
.card {padding: 20px;border: 1px solid #ddd;border-radius: 8px;}.title {font-size: 20px;}.price {font-weight: bold;}
import styles from "./ProductCard.module.css";function ProductCard({ product }) {return (<article className={styles.card}><h2 className={styles.title}>{product.name}</h2><p className={styles.price}>₹{product.price}</p></article>);}
10. Inline Styles
The style value is a JavaScript object:
function Button() {return (<buttonstyle={{backgroundColor: "blue",color: "white",padding: "10px 16px"}}>Save</button>);}
11. CSS Property Names in Inline Styles
CSS uses kebab-case (background-color); React inline styles use camelCase (backgroundColor):
<divstyle={{backgroundColor: "lightgray",fontSize: "18px",marginTop: "20px"}}>Hello</div>
12. Numeric Values in Inline Styles
Unitless numbers are often treated as pixels. For other units, specify them explicitly:
<div style={{ padding: 20, marginTop: 10 }}>Content</div>// Explicit unitswidth: "50%"fontSize: "1.5rem"height: "100vh"
13. When Inline Styles Are Useful
Useful when styles are small, highly dynamic, data-dependent, or unique to one element:
function ProgressBar({ progress }) {return (<divstyle={{width: `${progress}%`}}/>);}
14. Limitations of Inline Styles
Inline styles are less convenient for pseudo-classes, media queries, complex selectors, animations, large style systems, and global styles. Prefer CSS for:
button:hover {background: black;}
15. Conditional Styling
function Button({ disabled }) {return (<buttonclassName={disabled ? "button disabled" : "button"}>Save</button>);}
disabled = true → "button disabled"disabled = false → "button"
16. Conditional Classes with State
import { useState } from "react";function Menu() {const [isOpen, setIsOpen] = useState(false);return (<div className={isOpen ? "menu open" : "menu"}><button onClick={() => setIsOpen(!isOpen)}>Menu</button>{isOpen && (<div className="menu-content">Menu items</div>)}</div>);}
State controls both behavior and styling — a major strength of React.
17. Multiple Conditional Classes
<divclassName={`card${active ? "active" : ""}${disabled ? "disabled" : ""}${error ? "error" : ""}`}>Content</div>
Works, but can become hard to read with many conditions.
18. Using an Array for Class Names
const classes = ["card",active && "active",disabled && "disabled",error && "error"].filter(Boolean).join(" ");<div className={classes}>Content</div>
19. The classnames Pattern
Many projects use a utility like classnames:
className={classNames("button", {active,disabled,primary})}
Do not introduce a library when a simple conditional expression is enough.
20. Dynamic Inline Styles
function ProgressBar({ progress }) {return (<div className="progress"><divclassName="progress-bar"style={{ width: `${progress}%` }}/></div>);}
progress = 25 → width 25%; progress = 80 → 80%.
21. CSS Custom Properties
.button {background: var(--button-color);color: white;}
function Button({ color }) {return (<buttonclassName="button"style={{"--button-color": color}}>Save</button>);}
React provides dynamic values; CSS handles the styling.
22. Styling Based on Props
function Button({ variant }) {return (<button className={`button ${variant}`}>Save</button>);}<Button variant="primary" /><Button variant="secondary" />
.button {padding: 10px 16px;}.primary {background: blue;}.secondary {background: gray;}
23. Component Variants
function Button({ variant = "primary", children }) {return (<button className={`button ${variant}`}>{children}</button>);}<Button variant="primary">Save</Button><Button variant="secondary">Cancel</Button><Button variant="danger">Delete</Button>
More scalable than separate PrimaryButton / DangerButton unless behavior truly differs.
24. Styling Based on State
function Tab({ active, children }) {return (<button className={active ? "tab active" : "tab"}>{children}</button>);}
.tab {opacity: 0.7;}.tab.active {opacity: 1;font-weight: bold;}
25. Styling Lists
function TodoList({ todos }) {return (<ul>{todos.map((todo) => (<likey={todo.id}className={todo.completed ? "todo completed" : "todo"}>{todo.title}</li>))}</ul>);}
.todo.completed {text-decoration: line-through;opacity: 0.6;}
26. CSS Pseudo-Classes
.button:hover {transform: translateY(-1px);}.button:focus-visible {outline: 2px solid currentColor;}.button:disabled {opacity: 0.5;cursor: not-allowed;}
React → Application stateCSS → Visual interaction states
27. Responsive Styling
Prefer CSS media queries over manually checking screen width in React:
.container {display: grid;grid-template-columns: repeat(3, 1fr);gap: 20px;}@media (max-width: 768px) {.container {grid-template-columns: 1fr;}}
28. CSS Grid with React
function ProductGrid({ products }) {return (<div className="product-grid">{products.map((product) => (<ProductCard key={product.id} product={product} />))}</div>);}
.product-grid {display: grid;grid-template-columns: repeat(3, 1fr);gap: 24px;}
29. CSS Variables for Themes
:root {--color-primary: #2563eb;--color-background: #ffffff;--color-text: #111827;}.button {background: var(--color-primary);}.page {background: var(--color-background);color: var(--color-text);}
30. Dark Mode
function App() {const [darkMode, setDarkMode] = useState(false);return (<div className={darkMode ? "app dark" : "app"}><button onClick={() => setDarkMode(!darkMode)}>Toggle Theme</button></div>);}
.app {background: white;color: black;}.app.dark {background: #111;color: white;}
React state → CSS class → Theme styles
31. Avoiding Styling Logic Everywhere
Avoid nested ternaries for classes. Prefer:
const classes = ["card",isActive && "active",isDisabled && "disabled"].filter(Boolean).join(" ");<div className={classes}>Content</div>
32. Styling Component APIs
Prefer design intent over raw CSS values:
// Good<Button variant="primary" size="large">Save</Button>// Less desirable<Buttoncolor="#2563eb"padding="14px"borderRadius="8px"fontSize="16px">Save</Button>
Prefer APIs like variant, size, disabled, fullWidth.
33. Styling with Composition
<Card><CardHeader /><CardBody /><CardFooter /></Card>
Card├── CardHeader├── CardBody└── CardFooter
Each piece can own its styles.
34. CSS-in-JS
Libraries such as Emotion and styled-components write styles via JavaScript/component APIs:
const Button = styled.button`padding: 10px 16px;border-radius: 6px;`;
Not required for React. Choose based on requirements, team conventions, performance, design system, and DX.
35. Utility-First CSS
Frameworks like Tailwind compose small utility classes in markup:
<button className="px-4 py-2 rounded-lg">Save</button>
36. Choosing a Styling Strategy
| Approach | Best For |
|---|---|
| Regular CSS | Simple projects and global styles |
| CSS Modules | Component-scoped CSS |
| Inline styles | Small dynamic styles |
| CSS variables | Themes and design tokens |
| CSS-in-JS | Component-oriented styling systems |
| Utility-first CSS | Rapid, composable UI styling |
The key skill is knowing when each approach is appropriate.
37. Practical Example — Reusable Button
import "./Button.css";function Button({children,variant = "primary",size = "medium",disabled = false,onClick}) {const className = ["button",`button-${variant}`,`button-${size}`].join(" ");return (<buttonclassName={className}disabled={disabled}onClick={onClick}>{children}</button>);}export default Button;
.button {border: none;border-radius: 6px;cursor: pointer;}.button-primary {background: #2563eb;color: white;}.button-secondary {background: #e5e7eb;color: #111827;}.button-small {padding: 6px 10px;}.button-medium {padding: 10px 16px;}.button-large {padding: 14px 20px;}.button:disabled {opacity: 0.5;cursor: not-allowed;}
38. Practical Example — Card with CSS Module
import styles from "./ProductCard.module.css";function ProductCard({ product }) {return (<article className={styles.card}><h2 className={styles.title}>{product.name}</h2><p className={styles.price}>₹{product.price}</p>{product.discount && (<p className={styles.discount}>Discount available</p>)}</article>);}
39. Practical Example — Dynamic Status
function StatusBadge({ status }) {return (<span className={`status status-${status}`}>{status}</span>);}
.status {padding: 4px 8px;border-radius: 999px;}.status-pending { background: #fef3c7; }.status-shipped { background: #dbeafe; }.status-delivered { background: #dcfce7; }.status-cancelled { background: #fee2e2; }
40. Styling and Accessibility
Do not rely only on color for errors — also provide an accessible message:
<input className="error" /><p role="alert">Email is required.</p>
button:focus-visible {outline: 2px solid currentColor;outline-offset: 2px;}
41. Common Mistakes
Mistake 1: class instead of className
// Incorrect<div class="card">// Correct<div className="card">
Mistake 2: Putting everything in inline styles
Prefer CSS for reusable and complex styling.
Mistake 3: Overusing global class names
Names like .card / .title collide easily — use Modules or a naming strategy.
Mistake 4: Using React for CSS responsibilities
Do not invent hover state in React when CSS :hover is enough.
Mistake 5: Overly complex conditional classes
Prefer array/.filter/join or a classnames utility.
Mistake 6: Ignoring responsive design
Use media queries.
Mistake 7: Ignoring focus states
Do not remove outlines without an accessible replacement.
42. Best Practices
- Keep styling close to component ownership
- Use CSS for hover, focus, responsive layouts, pseudo-elements
- Use React for application-driven styling (active, disabled, error, selected, theme)
- Prefer semantic variants over arbitrary CSS props
- Use CSS Modules when local scoping helps
- Use CSS variables for shared design values
- Avoid unnecessary styling abstractions
- Keep accessibility in mind
43. Styling Architecture
src/├── components/│ ├── Button/│ │ ├── Button.jsx│ │ └── Button.module.css│ ├── Card/│ │ ├── Card.jsx│ │ └── Card.module.css│ └── Modal/│ ├── Modal.jsx│ └── Modal.module.css├── styles/│ ├── globals.css│ └── variables.css└── App.jsx
Component-specific styles + Global styles + Design variables
44. Styling Mental Model
Component│┌─────────┴─────────┐│ │Behavior Presentation│ │React CSS│ │└─────────┬─────────┘│▼Interactive UI
React answers: active? disabled? which variant/data/component? CSS answers: look, layout, hover, screen sizes.
45. Interview Questions
1. How do you add CSS to a React component?
Regular CSS, CSS Modules, inline styles, CSS-in-JS, or utility-first CSS.
2. Why className instead of class?
JSX uses className for the HTML class attribute.
3. How do you apply inline styles?
<div style={{ color: "red", fontSize: "20px" }} />
4. Inline CSS property names?
CamelCase: backgroundColor, fontSize, marginTop.
5. What are CSS Modules?
Locally scoped CSS classes imported into components.
6. Conditional classes?
className={active ? "button active" : "button"}
7. Should hover use React state?
Usually no — prefer CSS :hover.
8. When are inline styles useful?
Small, dynamic styles that depend on component data.
9. Dynamic values into CSS?
style={{ "--progress": `${progress}%` }}
10. Global CSS vs CSS Modules?
Global can affect the whole app; Modules scope names to the import.
11. Reusable styling APIs?
<Button variant="primary" size="large" />
46. Practice Exercises
- Basic Button — CSS file with padding, radius, background, text color
- Card — border, padding, shadow, rounded corners
- CSS Module — convert Card to
Card.module.css - Variants — primary / secondary / danger buttons
- StatusBadge — pending, processing, completed, cancelled
- Active nav — active link gets an
activeclass - Dark mode — toggle Light/Dark via React state + CSS class
- ProgressBar — width from
progressprop - Responsive grid — 3 / 2 / 1 columns via media queries
47. Quick Cheat Sheet
| Requirement | Approach |
|---|---|
| Regular CSS | className="card" |
| Import CSS | import "./Card.css" |
| CSS Modules | styles.card |
| Inline style | style={{ ... }} |
| Dynamic class | condition ? "a" : "b" |
| Multiple classes | ["a", "b"].join(" ") |
| Hover / focus | CSS :hover / :focus-visible |
| Responsive | CSS media queries |
| Theme | CSS variables / classes |
| Variant | variant prop |
Key Takeaway
React → Component + State + Props → className / style → CSS → Visual UI
<button className="button">Save</button><button className={styles.button}>Save</button><div className={isActive ? "card active" : "card"} /><div style={{ width: `${progress}%` }} />
Use React to express application state and component structure, and use CSS to express presentation and visual behavior.
Next: organizing larger UI systems with component patterns, reusable UI, and design-system concepts.