React Tutorial 0/120 lessons ~6 min read Lesson 27

    Styling Components

    Styling React components: className, CSS Modules, inline styles, conditional classes, variants, themes, responsive CSS, and choosing a styling strategy.

    Course progress0%
    Focus
    50 guided sections
    Practice signal
    Examples included
    Career prep
    Interview Q&A included

    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

    1. Why React uses className and how to import CSS
    2. Global CSS, component CSS, and CSS Modules
    3. Inline styles, camelCase properties, and dynamic values
    4. Conditional classes, variants, and prop-driven styling
    5. Themes, dark mode, responsive layout, and accessibility
    6. When to choose CSS vs CSS Modules vs utility-first vs CSS-in-JS

    1. Why Styling Matters in React

    tsx
    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.

    text
    React → Component structure + behavior
    CSS → Visual presentation

    React does not replace CSS — they work together.

    2. Basic CSS with React

    tsx
    function Button() {
    return (
    <button className="primary-button">
    Save
    </button>
    );
    }
    css
    .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:

    tsx
    function Card() {
    return (
    <div className="card">
    <h2>Product</h2>
    <p>Description</p>
    </div>
    );
    }
    css
    .card {
    padding: 20px;
    border: 1px solid #ddd;
    }

    4. Importing a CSS File

    text
    src/
    ├── components/
    │ ├── Button.jsx
    │ └── Button.css
    ├── App.jsx
    └── main.jsx
    tsx
    import "./Button.css";
    function Button() {
    return (
    <button className="button">
    Save
    </button>
    );
    }
    export default Button;
    css
    .button {
    padding: 10px 16px;
    border-radius: 6px;
    border: none;
    }

    5. Global CSS

    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

    text
    Button.jsx / Button.css
    Card.jsx / Card.css
    Navbar.jsx / Navbar.css
    tsx
    import "./Card.css";
    function Card({ title, description }) {
    return (
    <article className="card">
    <h2 className="card-title">{title}</h2>
    <p className="card-description">{description}</p>
    </article>
    );
    }
    css
    .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:

    css
    .button {
    padding: 10px 16px;
    border-radius: 6px;
    }
    tsx
    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.

    text
    Button.module.css → Scoped styles
    Card.module.css → Scoped styles

    9. CSS Modules Example

    css
    .card {
    padding: 20px;
    border: 1px solid #ddd;
    border-radius: 8px;
    }
    .title {
    font-size: 20px;
    }
    .price {
    font-weight: bold;
    }
    tsx
    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:

    tsx
    function Button() {
    return (
    <button
    style={{
    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):

    tsx
    <div
    style={{
    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:

    tsx
    <div style={{ padding: 20, marginTop: 10 }}>
    Content
    </div>
    // Explicit units
    width: "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:

    tsx
    function ProgressBar({ progress }) {
    return (
    <div
    style={{
    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:

    css
    button:hover {
    background: black;
    }

    15. Conditional Styling

    tsx
    function Button({ disabled }) {
    return (
    <button
    className={disabled ? "button disabled" : "button"}
    >
    Save
    </button>
    );
    }
    text
    disabled = true → "button disabled"
    disabled = false → "button"

    16. Conditional Classes with State

    tsx
    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

    tsx
    <div
    className={`
    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

    tsx
    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:

    tsx
    className={classNames("button", {
    active,
    disabled,
    primary
    })}

    Do not introduce a library when a simple conditional expression is enough.

    20. Dynamic Inline Styles

    tsx
    function ProgressBar({ progress }) {
    return (
    <div className="progress">
    <div
    className="progress-bar"
    style={{ width: `${progress}%` }}
    />
    </div>
    );
    }

    progress = 25 → width 25%; progress = 80 → 80%.

    21. CSS Custom Properties

    css
    .button {
    background: var(--button-color);
    color: white;
    }
    tsx
    function Button({ color }) {
    return (
    <button
    className="button"
    style={{
    "--button-color": color
    }}
    >
    Save
    </button>
    );
    }

    React provides dynamic values; CSS handles the styling.

    22. Styling Based on Props

    tsx
    function Button({ variant }) {
    return (
    <button className={`button ${variant}`}>
    Save
    </button>
    );
    }
    <Button variant="primary" />
    <Button variant="secondary" />
    css
    .button {
    padding: 10px 16px;
    }
    .primary {
    background: blue;
    }
    .secondary {
    background: gray;
    }

    23. Component Variants

    tsx
    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

    tsx
    function Tab({ active, children }) {
    return (
    <button className={active ? "tab active" : "tab"}>
    {children}
    </button>
    );
    }
    css
    .tab {
    opacity: 0.7;
    }
    .tab.active {
    opacity: 1;
    font-weight: bold;
    }

    25. Styling Lists

    tsx
    function TodoList({ todos }) {
    return (
    <ul>
    {todos.map((todo) => (
    <li
    key={todo.id}
    className={todo.completed ? "todo completed" : "todo"}
    >
    {todo.title}
    </li>
    ))}
    </ul>
    );
    }
    css
    .todo.completed {
    text-decoration: line-through;
    opacity: 0.6;
    }

    26. CSS Pseudo-Classes

    css
    .button:hover {
    transform: translateY(-1px);
    }
    .button:focus-visible {
    outline: 2px solid currentColor;
    }
    .button:disabled {
    opacity: 0.5;
    cursor: not-allowed;
    }
    text
    React → Application state
    CSS → Visual interaction states

    27. Responsive Styling

    Prefer CSS media queries over manually checking screen width in React:

    css
    .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

    tsx
    function ProductGrid({ products }) {
    return (
    <div className="product-grid">
    {products.map((product) => (
    <ProductCard key={product.id} product={product} />
    ))}
    </div>
    );
    }
    css
    .product-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 24px;
    }

    29. CSS Variables for Themes

    css
    :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

    tsx
    function App() {
    const [darkMode, setDarkMode] = useState(false);
    return (
    <div className={darkMode ? "app dark" : "app"}>
    <button onClick={() => setDarkMode(!darkMode)}>
    Toggle Theme
    </button>
    </div>
    );
    }
    css
    .app {
    background: white;
    color: black;
    }
    .app.dark {
    background: #111;
    color: white;
    }
    text
    React state → CSS class → Theme styles

    31. Avoiding Styling Logic Everywhere

    Avoid nested ternaries for classes. Prefer:

    tsx
    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:

    tsx
    // Good
    <Button variant="primary" size="large">
    Save
    </Button>
    // Less desirable
    <Button
    color="#2563eb"
    padding="14px"
    borderRadius="8px"
    fontSize="16px"
    >
    Save
    </Button>

    Prefer APIs like variant, size, disabled, fullWidth.

    33. Styling with Composition

    tsx
    <Card>
    <CardHeader />
    <CardBody />
    <CardFooter />
    </Card>
    text
    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:

    tsx
    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:

    tsx
    <button className="px-4 py-2 rounded-lg">
    Save
    </button>

    36. Choosing a Styling Strategy

    ApproachBest For
    Regular CSSSimple projects and global styles
    CSS ModulesComponent-scoped CSS
    Inline stylesSmall dynamic styles
    CSS variablesThemes and design tokens
    CSS-in-JSComponent-oriented styling systems
    Utility-first CSSRapid, composable UI styling

    The key skill is knowing when each approach is appropriate.

    37. Practical Example — Reusable Button

    tsx
    import "./Button.css";
    function Button({
    children,
    variant = "primary",
    size = "medium",
    disabled = false,
    onClick
    }) {
    const className = [
    "button",
    `button-${variant}`,
    `button-${size}`
    ].join(" ");
    return (
    <button
    className={className}
    disabled={disabled}
    onClick={onClick}
    >
    {children}
    </button>
    );
    }
    export default Button;
    css
    .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

    tsx
    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

    tsx
    function StatusBadge({ status }) {
    return (
    <span className={`status status-${status}`}>
    {status}
    </span>
    );
    }
    css
    .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:

    tsx
    <input className="error" />
    <p role="alert">
    Email is required.
    </p>
    css
    button:focus-visible {
    outline: 2px solid currentColor;
    outline-offset: 2px;
    }

    41. Common Mistakes

    Mistake 1: class instead of className

    tsx
    // 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

    1. Keep styling close to component ownership
    2. Use CSS for hover, focus, responsive layouts, pseudo-elements
    3. Use React for application-driven styling (active, disabled, error, selected, theme)
    4. Prefer semantic variants over arbitrary CSS props
    5. Use CSS Modules when local scoping helps
    6. Use CSS variables for shared design values
    7. Avoid unnecessary styling abstractions
    8. Keep accessibility in mind

    43. Styling Architecture

    text
    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
    text
    Component-specific styles + Global styles + Design variables

    44. Styling Mental Model

    text
    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?

    tsx
    <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?

    tsx
    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?

    tsx
    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?

    tsx
    <Button variant="primary" size="large" />

    46. Practice Exercises

    1. Basic Button — CSS file with padding, radius, background, text color
    2. Card — border, padding, shadow, rounded corners
    3. CSS Module — convert Card to Card.module.css
    4. Variants — primary / secondary / danger buttons
    5. StatusBadge — pending, processing, completed, cancelled
    6. Active nav — active link gets an active class
    7. Dark mode — toggle Light/Dark via React state + CSS class
    8. ProgressBar — width from progress prop
    9. Responsive grid — 3 / 2 / 1 columns via media queries

    47. Quick Cheat Sheet

    RequirementApproach
    Regular CSSclassName="card"
    Import CSSimport "./Card.css"
    CSS Modulesstyles.card
    Inline stylestyle={{ ... }}
    Dynamic classcondition ? "a" : "b"
    Multiple classes["a", "b"].join(" ")
    Hover / focusCSS :hover / :focus-visible
    ResponsiveCSS media queries
    ThemeCSS variables / classes
    Variantvariant prop

    Key Takeaway

    text
    React → Component + State + Props → className / style → CSS → Visual UI
    tsx
    <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.

    Ready to mark this lesson complete?Track your journey across the entire course.