#Inline styles
From my notes: inline styles are applied with the style attribute, and in React the value is a JavaScript object, not a CSS string. They're scoped to one element by definition and quick for simple cases, but they can't do pseudo-classes, media queries or other selector-based CSS, and they aren't reusable the way a class is.
The mental model: an inline style is CSS with no selector. Every rule in a stylesheet has a selector that says when it applies — .button:hover, @media (max-width: 600px) .button. An inline style only ever applies to "this element, now", so anything conditional on state the browser tracks (hover, focus, viewport width, dark mode) is out of reach.
export const MyComponent = () => {
const buttonStyle = {
backgroundColor: "blue", // camelCase, not background-color
color: "white",
padding: 10, // a number means px: "10px"
borderRadius: "5px",
lineHeight: 1.5, // unitless properties stay unitless
};
return <button style={buttonStyle}>Click me</button>;
};
// What inline styles can't do: there is no selector, so no :hover, no @media.
export const HoverAttempt = () => {
const style = {
backgroundColor: "blue", // works
":hover": { backgroundColor: "green" }, // does nothing useful
};
return <button style={style}>Hover me</button>;
};MyComponentbuilds a plain object and passes it asstyle. React writes each key onto the element'sstyle, converting camelCase to CSS names. The test prints<button style="background-color: blue; color: white; padding: 10px; border-radius: 5px; line-height: 1.5;">.padding: 10became10px: React appendspxto numbers for properties that take lengths.lineHeight: 1.5stayed1.5because React knowsline-height(likeopacity,zIndex,flex) is unitless. Use strings for anything else ("5px","2rem","50%").- In
HoverAttempt,backgroundColorworks, but":hover"isn't a CSS property. In the browser React just assigns it as a JavaScript property on the style object: the test shows the DOM as<button style="background-color: blue;">andbutton.style[":hover"]as[object Object]— silently ignored, no warning. - On the server it's worse:
renderToStringemitsstyle="background-color:blue;:hover:[object Object]", literally the object coerced to a string. Garbage CSS in your HTML, again with no warning. - This is the limit from my notes in action: hover, focus, media queries,
::before, keyframes and transitions between states all need a stylesheet.
Where inline styles are the right tool is values that are only known at runtime: a progress bar's width, a colour the user picked, the position of a dragged element. The modern way to combine that with real CSS is to pass the value as a CSS custom property and keep the selectors in a stylesheet:
// .chip { background: var(--accent); }
// .chip:hover { background: color-mix(in srgb, var(--accent), black 20%); }
export const Chip = ({ accent, children }) => (
<span className="chip" style={{ "--accent": accent }}>
{children}
</span>
);- The key
"--accent"starts with--, so React sets it withstyle.setProperty("--accent", value)instead of as a normal property. The test renders<Chip accent="tomato">and gets<span class="chip" style="--accent: tomato;">, andgetPropertyValue("--accent")returnstomato. - The stylesheet never changes.
.chipreads the variable for its background, and.chip:hoverderives a darker shade from it — the hover rule works because it's a real selector in real CSS. - Changing
accentonly changes one inline custom property; the browser recalculates the styles that use it. No new class names or rules are created (this matters for CSS-in-JS — see CSS-in-JS: costs and alternatives).