<style>

Renders an inline CSS stylesheet that React hoists into the document head and de-duplicates by href.

Signature
<style>{` p { color: red; } `}</style>
  • Added in React 19.0.0

Parameters

ParameterTypeDescription
$childrenstringThe contents of the stylesheet. Required.
$precedenceoptionalstringTells React where to rank this node relative to other stylesheets in <head>, which determines what can override what. Precedence values discovered first are treated as lower, later ones as higher. Shared with <link> stylesheets and preinit.
$hrefoptionalstringLets React de-duplicate styles that carry the same href. It should uniquely identify the stylesheet.
$mediaoptionalstringRestricts the stylesheet to a media query.
$nonceoptionalstringA cryptographic nonce that allows the resource under a strict Content Security Policy.
$titleoptionalstringNames an alternative stylesheet.
$blockingoptionalstringNot recommended with React. Set to "render", it tells the browser not to paint until the stylesheet loads; Suspense gives finer control.

Return value

Renders nothing in place. Given href and precedence, React inserts a single deduplicated <style> node into the document <head>.

Changelog

  • PHP 19.0.0A <style> carrying href and precedence is hoisted into the document <head> and de-duplicated by href. Before 19 it was rendered in place wherever it appeared in the tree.
  • PHP 19.0.0When precedence is supplied, React drops every prop other than href and precedence.

Render a <style> from anywhere in the tree and, given href and precedence, React lifts it into the document <head>. That lets a component ship the CSS it depends on next to its own markup instead of relying on a global sheet that some other file has to remember to import.

href de-duplicates, precedence orders

Two are independent jobs. href is an identity, not a URL to fetch: render the same component ten times and React inserts one node, because all ten declare the same href. precedence decides insertion order inside <head>, and therefore which rules win a specificity tie. React ranks precedence values by the order it first encounters them, so a single value like "default" is fine when your rules are atomic; introduce a second only when one layer must reliably override another.

What trips people up

The content is frozen after the first render. Deriving the CSS string from state and expecting it to update will not work — React ignores prop changes on an already-rendered style and warns in development. Toggle a className instead, or render a second sheet at a higher precedence. Supplying precedence also strips every other prop except href, so media and nonce are silently dropped alongside it.

Example

Try it yourself
Loading editor…
Loading React…

When to use it

  • Shipping a widget's CSS inside the component so it cannot be imported without its styles
  • Injecting critical above-the-fold rules into the head during a server render
  • Layering a theme override on top of base rules by giving it a higher precedence
  • Emitting the small set of rules a design-system primitive needs, deduplicated across every instance

Frequently asked questions

Why is my inline stylesheet not moving to the head?

Hoisting requires both href and precedence — with either missing, React renders the <style> in place, exactly where it sits in the tree. That still works in the browser, since CSS applies wherever the tag lands, but you lose de-duplication and ordering control, so ten instances of the component produce ten identical style nodes.

Does changing the CSS string re-render the style tag?

No. React ignores every prop change on a <style> after it has been rendered and warns about it in development. Interpolating state into the stylesheet body freezes it at whatever the first render produced. Put dynamic values on the element's style prop, or swap classNames and let a second, higher-precedence sheet override the first.

What is the difference between the style tag and the style prop?

The <style> component emits a real stylesheet — full CSS with selectors, pseudo-classes and media queries — that React hoists into <head> and shares across every instance. The style prop sets inline styles on one element, as a camelCase object, and cannot express :hover or @media. Use the tag for rules, the prop for one-off computed values.

More examples

Precedence decides which rule wins

The button renders red on white. Both rules have identical specificity, so the later node in <head> wins — and React orders the nodes by precedence, not by where the components sit in the tree. DangerTheme is rendered first here yet still lands after BaseTheme, because React saw the precedence value "base" first and therefore ranks "overrides" above it. Swap the two precedence strings and the button turns grey.

Example · jsx
function BaseTheme() {
  return (
    <style href="theme-base" precedence="base">{`
      .btn { background: #e2e8f0; color: #0f172a; padding: 8px 14px; }
    `}</style>
  );
}

function DangerTheme() {
  return (
    <style href="theme-danger" precedence="overrides">{`
      .btn { background: #b91c1c; color: white; }
    `}</style>
  );
}

export default function ThemedButton() {
  return (
    <div style={{ fontFamily: "system-ui, sans-serif", padding: 16 }}>
      <DangerTheme />
      <BaseTheme />
      <button type="button" className="btn">Delete</button>
    </div>
  );
}

Why state-driven CSS strings do not update

Clicking the button moves the second bar and leaves the first one stuck at 25%. BrokenBar interpolates state into the stylesheet body, and React ignores changes to an already-rendered style — in development it also logs a warning about the changed props. The fix is the split WorkingBar shows: everything static goes in the sheet, and the one value that changes rides on the style prop of the element itself.

Example · jsx
import { useState } from "react";

// Broken: React ignores prop changes on a style it has already rendered.
function BrokenBar({ percent }) {
  return (
    <>
      <style href="bar" precedence="default">{`
        .bar-fill { width: ${percent}%; background: #2563eb; height: 12px; }
      `}</style>
      <div className="bar-fill" />
    </>
  );
}

// Works: static rules in the sheet, the dynamic value inline.
function WorkingBar({ percent }) {
  return (
    <>
      <style href="bar-static" precedence="default">{`
        .bar-track { background: #e2e8f0; height: 12px; }
        .bar-fill-2 { background: #2563eb; height: 12px; }
      `}</style>
      <div className="bar-track">
        <div className="bar-fill-2" style={{ width: percent + "%" }} />
      </div>
    </>
  );
}

export default function ProgressDemo() {
  const [percent, setPercent] = useState(25);
  return (
    <div style={{ fontFamily: "system-ui, sans-serif", padding: 16 }}>
      <p>Frozen at its first value:</p>
      <BrokenBar percent={percent} />
      <p>Updates correctly:</p>
      <WorkingBar percent={percent} />
      <button type="button" onClick={() => setPercent(percent === 25 ? 80 : 25)}>
        Toggle to {percent === 25 ? 80 : 25}%
      </button>
    </div>
  );
}

Discussion

  • Be the first to comment on this lesson.