Inline Styling

Inline styling in React means applying CSS directly to an element through its style prop instead of a separate CSS file or class name. Unlike plain HTML, where style is a semicolon-separated string, React’s style prop takes a JavaScript object. This makes styles easy to compute dynamically from props and state, which is exactly why inline styles are so common for values that change at runtime, like a progress bar’s width or a button’s color when it’s active.

Overview / How it works

Every DOM element that React renders accepts a style attribute, but React intercepts it and treats it specially. Instead of a CSS string like "color: red; font-size: 20px;", React expects a plain JavaScript object whose keys are CSS property names written in camelCase (backgroundColor instead of background-color) and whose values are strings or numbers. React then converts that object into the actual inline style attribute on the underlying DOM node during the commit phase.

Why camelCase? CSS property names like background-color contain a hyphen, which is not valid JavaScript object key syntax without quoting it, and quoting every key is tedious. React (borrowing from the DOM’s own element.style object) uses the camelCase convention instead, so backgroundColor, fontSize, and borderRadius map directly onto real CSS properties.

Numbers are also treated specially: for most properties that expect a length, React automatically appends px to a bare number. So { fontSize: 20 } becomes font-size: 20px. A handful of unitless CSS properties, such as opacity, zIndex, flex, flexGrow, and lineHeight, are left as plain numbers because CSS itself doesn’t want a unit there. If you need a different unit, pass a string explicitly, like { width: "50%" } or { margin: "1rem" }.

Because the style object is just a JavaScript value, you can build it from props, state, or any computation, and it recalculates on every render like any other expression in your component. This is the main reason developers reach for inline styles: they react (no pun intended) instantly to component data without needing to toggle CSS classes or write custom CSS-in-JS tooling.

Syntax

The general form looks like this:

<element style={{ property: value, anotherProperty: value }} />

The double curly braces often confuse beginners — the outer {} is JSX syntax for embedding a JavaScript expression, and the inner {} is the object literal itself. It is usually clearer to define the object separately:

Part Description
style A built-in prop every DOM element accepts; must receive an object, never a string.
Property keys CSS properties written in camelCase (backgroundColor, fontSize), except custom properties (CSS variables) like --main-color, which keep their original name.
Property values Strings for anything with units, keywords, or colors ("1rem", "bold", "#333"); numbers for unitless properties or properties that accept a bare pixel value.
Object identity A brand-new object is fine to create on every render — it is cheap and does not break React’s rendering model.

Examples

Example 1: A simple styled card

function Card() {
  const cardStyle = {
    padding: "16px",
    borderRadius: "8px",
    backgroundColor: "#f5f5f5",
    boxShadow: "0 2px 4px rgba(0, 0, 0, 0.1)",
  };

  return (
    <div style={cardStyle}>
      <h3 style={{ margin: 0, color: "#333" }}>Welcome</h3>
      <p style={{ color: "#666", fontSize: 14 }}>This card uses inline styles.</p>
    </div>
  );
}

export default Card;

This renders a light-gray, padded, rounded card with a subtle shadow, a dark heading, and smaller gray body text. Notice that cardStyle is defined once as a plain object and reused on the div, while the heading and paragraph use inline object literals directly in the JSX since they are only needed once. fontSize: 14 is a bare number, so React renders it as font-size: 14px.

Example 2: Dynamic styling based on state

import { useState } from "react";

function ToggleButton() {
  const [isActive, setIsActive] = useState(false);

  const buttonStyle = {
    padding: "10px 20px",
    borderRadius: "6px",
    border: "none",
    color: "#fff",
    backgroundColor: isActive ? "#22c55e" : "#94a3b8",
    cursor: "pointer",
    transition: "background-color 0.2s ease",
  };

  return (
    <button style={buttonStyle} onClick={() => setIsActive(!isActive)}>
      {isActive ? "Active" : "Inactive"}
    </button>
  );
}

export default ToggleButton;

Renders a button labeled “Inactive” on a gray background. Clicking it calls setIsActive, triggering a re-render; on the next render buttonStyle.backgroundColor evaluates to "#22c55e" (green) and the label becomes “Active”. This is the core pattern for inline styles: the object is recomputed from state on every render, so the DOM always reflects the current value.

Example 3: A progress bar driven by props

function ProgressBar({ percent }) {
  const clamped = Math.min(100, Math.max(0, percent));

  const trackStyle = {
    width: "100%",
    height: "12px",
    backgroundColor: "#e5e7eb",
    borderRadius: "999px",
    overflow: "hidden",
  };

  const fillStyle = {
    width: `${clamped}%`,
    height: "100%",
    backgroundColor:
      clamped < 50 ? "#ef4444" : clamped < 80 ? "#f59e0b" : "#22c55e",
    transition: "width 0.3s ease, background-color 0.3s ease",
  };

  return (
    <div style={trackStyle}>
      <div style={fillStyle} />
    </div>
  );
}

export default ProgressBar;

Called as <ProgressBar percent={72} />, this renders a light-gray track with an inner bar filled to 72% width in amber (since 72 is between 50 and 80). Because fillStyle.width is built from the percent prop with a template literal, the bar automatically resizes and recolors whenever the parent passes a new percent value — no CSS class juggling required.

How it works step by step / Under the hood

  • On render: your component function runs, builds the style object (or objects) from current props/state, and includes it in the JSX it returns.
  • On reconciliation: React compares the new element tree to the previous one. If a DOM node persists between renders, React does not replace it — it only needs to know what changed on it.
  • On commit: React applies just the style differences to the real DOM node’s style property (React does not literally serialize a CSS string and reassign the whole attribute each time; it updates only the properties that changed since the last render).
  • On unmount: the DOM node is removed entirely, taking its inline styles with it — there is no separate cleanup step needed for styles themselves.

Because a new style object is just a plain JavaScript value, creating one on every render is normal and inexpensive. It only becomes worth memoizing (with useMemo) in the rare case where you pass it to a child wrapped in React.memo and want to avoid that child re-rendering solely because it received a new (but equal-in-value) object reference.

Common Mistakes

Mistake 1: Passing a CSS string instead of an object

function Banner() {
  return <div style="color: red; font-size: 20px;">Warning</div>;
}

This is valid HTML but not valid React. The style prop must be a JavaScript object, not a string; passing a string either throws or is silently ignored depending on the React version, and either way the styles will not apply correctly.

function Banner() {
  return <div style={{ color: "red", fontSize: 20 }}>Warning</div>;
}

Wrapping the properties in an object (the double braces) fixes it: the outer braces enter JavaScript-expression mode, and the inner braces define the object literal.

Mistake 2: Using kebab-case property names

const style = {
  "background-color": "black",
  "font-size": "18px",
};

Kebab-case keys need to be quoted to be valid object keys, and worse, React’s style object does not recognize hyphenated CSS property names at all — only the camelCase form is understood, so these properties are silently dropped.

const style = {
  backgroundColor: "black",
  fontSize: "18px",
};

Using backgroundColor and fontSize matches what React expects and applies correctly. The one exception is CSS custom properties (variables), which keep their original dashed name, e.g. { "--accent-color": "#333" }.

Mistake 3: Mutating a style object stored in state

function Highlighter() {
  const [style, setStyle] = useState({ backgroundColor: "yellow" });

  function handleClick() {
    style.backgroundColor = "orange"; // mutates state directly
    setStyle(style);
  }

  return (
    <p style={style} onClick={handleClick}>
      Click me
    </p>
  );
}

Mutating style in place and then calling setStyle with the same object reference does not reliably trigger a re-render, because React compares state by reference for many optimizations and sees the “same” object coming back.

function Highlighter() {
  const [style, setStyle] = useState({ backgroundColor: "yellow" });

  function handleClick() {
    setStyle({ ...style, backgroundColor: "orange" });
  }

  return (
    <p style={style} onClick={handleClick}>
      Click me
    </p>
  );
}

Spreading the old object into a new one and overriding just the changed property creates a fresh reference, so React reliably detects the state update and re-renders.

Best Practices

  • Reserve inline styles for values computed from props or state (colors, widths, positions); put static, reusable styling in a CSS file, CSS module, or utility classes instead.
  • Always use camelCase property names, and remember bare numbers become pixels except for a small set of unitless properties like opacity, zIndex, and lineHeight.
  • Never mutate a style object that lives in state or props — always spread into a new object.
  • Extract a style object into a named constant (or a small helper function) when it has more than a couple of properties, so the JSX stays readable.
  • Remember that inline styles cannot use pseudo-classes (:hover), pseudo-elements (::before), or media queries — for those, use a CSS file, CSS module, or a styling library instead.
  • Keep in mind that CSS classes (via className) take lower specificity priority than inline styles in the cascade, so mixing the two on the same element can lead to surprising overrides.

Practice Exercises

  • Build a Badge component that accepts a status prop ("success", "warning", or "error") and uses an inline style object to render a different background color and text color for each status.
  • Create a component with a slider input (<input type="range">) whose value is stored in state, and use that state value to set the width of a colored div next to it, so the bar grows and shrinks as the slider moves.
  • Take the Highlighter example from the Common Mistakes section and extend it so clicking the paragraph cycles through three background colors (yellow, orange, pink) instead of just two, using the spread pattern shown to update state immutably each time.

Summary

  • The style prop takes a JavaScript object, not a CSS string; write property names in camelCase.
  • Bare numeric values are converted to pixels automatically for most properties, but a few properties (like opacity and zIndex) stay unitless.
  • Inline styles shine when a value must be computed from props or state, such as a color, width, or position that changes at runtime.
  • Treat style objects as immutable: build a new object (often with the spread operator) rather than mutating an existing one, especially when it lives in state.
  • Inline styles cannot express pseudo-classes, pseudo-elements, or media queries — use CSS files, CSS modules, or a styling library for those cases.