<style>
Renders an inline CSS stylesheet that React hoists into the document head and de-duplicates by href.
<style>{` p { color: red; } `}</style>- Added in React 19.0.0
Parameters
| Parameter | Type | Description |
|---|---|---|
$children | string | The contents of the stylesheet. Required. |
$precedenceoptional | string | Tells 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. |
$hrefoptional | string | Lets React de-duplicate styles that carry the same href. It should uniquely identify the stylesheet. |
$mediaoptional | string | Restricts the stylesheet to a media query. |
$nonceoptional | string | A cryptographic nonce that allows the resource under a strict Content Security Policy. |
$titleoptional | string | Names an alternative stylesheet. |
$blockingoptional | string | Not 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
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.
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.
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