Releases: styled-components/styled-components
styled-components@6.4.2-prerelease-20260516150535
d20b88e Fix a TypeScript error when wrapping a component whose props include an as prop with a non-string type (such as Next.js Link's as?: Url). The styled component now accepts either the styled-components polymorphism value or the wrapped component's own as type, so spreading the wrapped component's props onto the styled component is assignable again.
Full Changelog: styled-components@6.4.2-prerelease-20260514185513...styled-components@6.4.2-prerelease-20260516150535
styled-components@6.4.2-prerelease-20260514185513
Internal changes only.
Full Changelog: styled-components@6.4.2-prerelease-20260514184932...styled-components@6.4.2-prerelease-20260514185513
styled-components@6.4.2-prerelease-20260514184932
No new changes since the previous release. (Debugging the changelog generation in the prerelease builds.)
Full Changelog: styled-components@6.4.2-prerelease-20260514182414...styled-components@6.4.2-prerelease-20260514184932
styled-components@6.4.2-prerelease-20260514182414
Patch Changes
-
9945904: Restore TypeScript prop autocomplete inside the JSX of a styled component once the first attribute is typed.
-
9945904: Apply all chain levels' styles when an extended styled component renders with the
asprop under Preact'sreact-compat. -
9945904: Respect a custom
toStringon plain value objects (e.g. design tokens) when interpolated into a styled component, rather than walking the object's keys as CSS declarations. -
9945904: Fix a TypeScript error when wrapping a component whose props include an
asprop with a non-string type (such as Next.jsLink'sas?: Url). The styled-componentsasandforwardedAsprops now consistently override the wrapped component's same-named props instead of colliding with them. -
9945904: Restore reliable styling in production browser bundles built without a runtime
processglobal.
Full Changelog: styled-components@6.4.1...styled-components@6.4.2-prerelease-20260514182414
styled-components@7.0.0-prerelease-20260513173704
No new changes since the previous release.
Full Changelog: styled-components@7.0.0-prerelease-20260513034901...styled-components@7.0.0-prerelease-20260513173704
styled-components@7.0.0-prerelease-20260513034901
Minor Changes
-
feacf77: React Native:
:not(...)now works for simple selectors. Rules such as:not(:hover),:not(:focus),:not([disabled]), and:not([data-state='loading'])apply when the condition inside:not()is not true. More complex forms, including multiple selectors or nested descendant selectors, still show a development warning and are ignored on native. -
feacf77:
em,lh, andrlhlength units now work on React Native. Values likepadding: 1em,gap: 0.5lh, andmin(10px, 5em)resolve against the current text size and line height, so typography-based spacing can be shared across web and native without rewriting everything to pixels.text-align: start,text-align: end, andtext-align: match-parentnow resolve correctly under both left-to-right and right-to-left writing directions on React Native. Authors get the same direction-aware behavior they get on the web; the previous fall-back-to-autowarning is removed.Components whose CSS declares
font-size,line-height, ordirectionpass the resolved value to descendants, so one text size at the top of a card can drive the relative spacing inside it. -
feacf77: React Native: descendant and child combinator selectors now work across styled components.
const Card = styled.View` padding: 16px; `; const Title = styled.Text` font-size: 14px; ${Card} & { font-size: 18px; } `; <Card> <Title>Bigger inside a Card</Title> </Card> <Title>Default size when standalone</Title>
The descendant form
${Card} &matches whenever the component is rendered anywhere insideCard. The child form${Card} > &only matches whenCardis the nearest styled parent. Regular React Native components can sit between styled components without breaking selector matching.The same selectors also work on web. This also fixes a web bug where
${Component} { ... }rules placed after another declaration could lose the component selector and target too broadly. -
feacf77: React Native:
field-sizing: contentmakes aTextInputautosize to its content.const Note = styled.TextInput` field-sizing: content; min-height: 44px; padding: 8px 12px; border: 1px solid #ddd; `; <Note placeholder="Start typing…" />
The field grows in height as the user types, no controlled height state, no
onContentSizeChangewiring. Passmultiline={false}explicitly to keep a fixed single-line field (a dev-time message points out that autosize is off in that case).On
react-native-webthe declaration is handed straight to the browser, which has supportedfield-sizingnatively since Chrome 123. -
feacf77: React Native:
:has(<simple>)selector now works.const Card = styled.View` padding: 16px; &:has(${Icon}) { padding-left: 48px; } &:has([data-state='active']) { background-color: tomato; } `; <Card> <Icon /> </Card>
The rule checks the component's children at render time and applies when any descendant matches. Two forms are supported on native:
${Component}: matches when the referenced styled component appears anywhere inside.[attr]and[attr=value]: match when any descendant has the named prop. Value checks compare the rendered prop value as text, soaria-pressed={true}andaria-pressed="true"both match[aria-pressed='true'].
More complex selectors inside
:has(), such as descendant chains, sibling selectors, and nested:has()calls, are not supported on native yet. -
feacf77: Relative color syntax now works in React Native styles. You can write values like
oklch(from #f00 calc(l - 0.15) c h)to derive a new color from a base color, and styled-components converts the result to a color React Native can render consistently on iOS, Android, and the web. This works withoklch,oklab,lch, andlab.The base color can be a literal color, another modern color function, or a theme value such as
oklch(from ${theme.colors.brand} calc(l - 0.15) c h). That makes it possible to build lighter, darker, or more transparent variants from one source color without maintaining a separate shade table. -
feacf77: React Native: sibling combinator selectors and the
:nth-childfamily now work.const Card = styled.View` padding: 16px; `; const Divider = styled.View` height: 1px; ${Card} + & { margin-top: 8px; } `; const ListItem = styled.View` padding: 8px; &:nth-child(odd) { background-color: gainsboro; } &:first-child { border-top-width: 0; } `;
Supported selector forms include:
- Adjacent sibling:
${Component} + &, which applies when the previous styled sibling is the referenced component. - General sibling:
${Component} ~ &, which applies when any earlier styled sibling is the referenced component. :first-child,:last-child,:only-child.:nth-child(N),:nth-child(an+b),:nth-child(odd),:nth-child(even).:nth-last-child(...)(same syntax as:nth-child, counting from the end).:first-of-type,:last-of-type,:only-of-type,:nth-of-type(...), and:nth-last-of-type(...), which count siblings of the same element type.
These selectors follow the component's JSX position among its siblings. Regular React Native components can sit between styled components without breaking selector matching.
- Adjacent sibling:
-
feacf77: System color keywords such as
Canvas,CanvasText,Field,FieldText,GrayText,Highlight, andLinkTextnow work on React Native. Values likecolor: CanvasTextandbackground-color: Canvasadapt to the user's appearance and platform color settings where React Native exposes them, with readable fallbacks for unsupported native semantics. The browser still handles these keywords directly on the web build.Keywords match regardless of casing. Values inside larger shorthands, such as
border: 1px solid Canvas, are not covered in this release; use the matching standalone color property for the same result.
Patch Changes
-
feacf77:
aspect-ratioon React Native now accepts the same common forms as CSS:16 / 9,auto,auto 16 / 9, and16 / 9 auto. Whenautois combined with a ratio on a component that does not have its own natural dimensions, styled-components uses the ratio and shows a one-time development warning explaining that theautopart only applies to image-like elements. -
feacf77:
background-size: coverandbackground-size: containon React Native no longer crash the app when applied to gradient backgrounds. Gradients now paint across the full element area as expected.react-native-webstill receives the original keyword so the browser can handle it directly.background-positionvalues like0 0,50% 50%, andtop leftalso no longer trigger areact-native-webwarning. -
feacf77: The
backgroundshorthand now works on React Native. A single declaration can set image, position, size, repeat behavior, attachment, origin, clipping, and color. Multiple background layers,position / size, and a final background color are supported.When React Native does not expose a matching behavior, such as fixed background attachment or non-default background origin and clipping, styled-components shows a development warning instead of silently dropping the unsupported part.
react-native-webcontinues to let the browser handle the full shorthand. -
feacf77:
direction: ltranddirection: rtlnow work in React Native styles. Logical edges such asmargin-inline-startandpadding-inline-endfollow that direction, so the same declaration can support left-to-right and right-to-left layouts on iOS, Android, and the web. -
feacf77:
font-style: obliquenow maps toitalicon React Native. If an angle is provided, such asoblique 14deg, styled-components shows a one-time development warning because React Native cannot control the exact slant.react-native-webstill passes the declaration to the browser.line-heightvalues React Native cannot apply, such as percentages,em, andrem, now show a one-time development warning with the specific value and a suggested replacement.letter-spacingvalues written withem,rem, or percentages also now warn on React Native and point to pixel or unitless values instead. -
feacf77: Generic
font-familykeywords such asserif,sans-serif,monospace,system-ui,ui-rounded,emoji, andmathnow map to an appropriate platform font on iOS and Android.react-native-webstill passes the keyword to the browser. When a React Native font list contains multiple comma-separated families, styled-components uses the first one and shows a one-time development warning because React Native accepts only one font family. -
feacf77:
interactivity: inertnow applies on React Native: the styled component and its subtree stop responding to touch, no longer accept D-pad / keyboard focus, and are hidden from screen readers (VoiceOver on iOS, TalkBack on Android). One known gap surfaces via a one-time dev warning on Android: a focusable child rendered inside an inert subtree may still receive focus, because React Native does not propagate that flag down the tree.react-native-weblets the browser honor the property natively via the HTMLinertattribute. -
feacf77: Logical border shorthands now expand on React Native:
- Per-edge color, style, and width declarations such as
border-inline-start-colorandborder-block-end-width. - Axis shorthands such as
border-inline-color,border-block-width,border-inline, andborder-block. - Single-edge shorthands such as
border-inline-startandborder-block-end.
Width and color apply to the matching logical edge. Per-edge border styles now show a one-time development warning on React Native, because the platfor...
- Per-edge color, style, and width declarations such as
styled-components@7.0.0-prerelease-20260511203830
Patch Changes
- a53bad8:
vertical-align: top | middle | bottomon a styled<Text>now positions text content within the Text's box onreact-native-web, matching the visual semantic of React Native'sverticalAlignon Android (textAlignVertical). Othervertical-alignvalues pass through unchanged so the browser's native baseline-shifting semantics still apply.
Full Changelog: styled-components@7.0.0-prerelease-20260511181437...styled-components@7.0.0-prerelease-20260511203830
styled-components@7.0.0-prerelease-20260511181437
Major Changes
-
c5e1c54: React Native:
createTheme()now works exactly the way it does on web. Pass the returned object toThemeProvider, reference leaves in your styled components, and the current theme resolves automatically.import styled, { createTheme, ThemeProvider } from 'styled-components/native'; const theme = createTheme({ colors: { bg: '#ffffff', text: '#111111' }, }); const Card = styled.View` background-color: ${theme.colors.bg}; border-color: ${theme.colors.text}; `; <ThemeProvider theme={{ colors: { bg: '#111', text: '#eee' } }}> <Card /> </ThemeProvider>
Nested
ThemeProviders on React Native deep-merge their theme objects so an inner override that only touches one leaf keeps the siblings it inherited; a child provider that setscolors.textkeepscolors.bgfrom the ancestor. Web behavior is unchanged. -
c5e1c54: Styled components no longer honor
defaultProps. React 19 removeddefaultPropssupport from function components, so styled components can no longer inherit a parent'sdefaultPropseither.Migration: use
.attrs()for prop defaults. The object form always wins over user-provided props (this is intentional, see the attrs FAQ). The function form lets user-provided props override the default:// Before (v6, no longer applies in v7) const Button = styled.button``; Button.defaultProps = { type: 'button' }; // After (object form always wins) const Button = styled.button.attrs({ type: 'button' })``; // After (user-provided overrides allowed) const Button = styled.button.attrs<{ type?: string }>(p => ({ type: p.type ?? 'button', }))``;
For a default theme, wrap the tree in
<ThemeProvider theme={...}>instead. -
c5e1c54: Removed the
disableCSSOMInjectionprop on<StyleSheetManager>and theSC_DISABLE_SPEEDY/REACT_APP_SC_DISABLE_SPEEDYenvironment variables. Added a newextractCSSexport.Browser builds now always use the same fast injection path that production has used by default for years. There's no longer a knob to switch into a slower text-based mode at runtime, and dev and production now behave identically.
If you were using the toggle to make CSS visible as text (for static-render pipelines, micro-frontend cloning, embedding into iframes or Shadow DOM, or extraction tooling), call the new
extractCSS()function after render to get the current CSS as a plain string:import { extractCSS } from 'styled-components'; // after rendering const css = extractCSS();
The result is plain CSS without the rehydration markers used by
ServerStyleSheet, so it can be injected directly into another document, stamped into a cloned DOM tree, or written to disk. -
c5e1c54: Mounting the same
createGlobalStylecomponent multiple times now emits its CSS only once. Previously each mount produced its own copy of the stylesheet rules. Rendering output stays byte-stable across SSR, client, and Server Components. -
c5e1c54: React Native: the CSS-to-style-object translation layer is now built in. Several long-standing limitations go away on the native path.
transform: matrix(...)/matrix3d(...)work.transform: translateX(10)(bare number, no unit) works.background-image: linear-gradient(...)/radial-gradient(...)work.filter: blur(4px) saturate(1.5)and the full filter-function chain work.- Modern color notations pass through to React Native's color parser unchanged:
rgb(r g b / a)slash-alpha,hwb(),hsl()all work. box-shadowwith spread and inset pass through as CSS strings.mix-blend-mode,isolation,cursorflow through.
import styled from 'styled-components/native'; const Tile = styled.View` background-image: linear-gradient(135deg, hsl(220 80% 60%), hsl(280 70% 50%)); filter: blur(2px) saturate(1.5); box-shadow: 0 4px 12px rgb(0 0 0 / 0.2); transform: matrix(1, 0, 0, 1, 8, 0); `;
The transform layer also fixes
border: noneemittingborder-style: solidon native; it now emitsborder-style: noneto match the rest of the ecosystem.iOS setup note for filters: in React Native 0.85, the
filterprimitivesblur,saturate,hue-rotate,grayscale,contrast, anddrop-shadowonly render when your iOS app opts into the SwiftUI-based filter backend. SetReactNativeReleaseLeveltoexperimentalin your iOSInfo.plist(orios.infoPlistinapp.jsonfor Expo) to enable it.brightnessandopacitywork without this flag. -
c5e1c54: Raised peer dependency floors:
reactandreact-domnow require>= 19.0.0(was>= 16.8).react-nativenow requires>= 0.85.0(was>= 0.68).css-to-react-nativeis no longer a peer dependency. Apps that listed it solely for styled-components can drop it from theirpackage.json.- The
enableVendorPrefixesprop on<StyleSheetManager>and the runtime vendor prefixer have been removed. Modern browser targets handle prefixing natively; for the few properties that still need them (e.g.-webkit-backdrop-filteron Safari), declare both the prefixed and unprefixed forms in your CSS, or run a build-time PostCSS transform.
Older React / React Native projects should stay on styled-components v6.
-
c5e1c54: Plugins moved to a dedicated
styled-components/pluginssubpath, and a first-party RTL plugin ships with the library.import { StyleSheetManager } from 'styled-components'; import { rtlPlugin, rscPlugin } from 'styled-components/plugins'; <StyleSheetManager plugins={[rtlPlugin]}> <App /> </StyleSheetManager>
rtlPluginreplacesstylis-plugin-rtlfor users coming from v6: it swaps physical side properties (padding-left↔padding-right), flipsleft/rightkeyword values onfloat/clear/text-align/caption-side, and mirrors 4-value shorthand positions. Logical properties likemargin-inline-startpass through unchanged.The
stylisPluginsprop on<StyleSheetManager>is nowplugins, and the top-levelstylisPluginRSCexport has moved into the new subpath asrscPlugin.Migration:
-import { rtl, stylisPluginRSC } from 'styled-components'; +import { rtlPlugin, rscPlugin } from 'styled-components/plugins'; -<StyleSheetManager stylisPlugins={[rtl, stylisPluginRSC]}> +<StyleSheetManager plugins={[rtlPlugin, rscPlugin]}>
Custom plugins authored against the v6 stylis contract need to port to the narrower plugin interface, which exposes
rw(selector rewrite) anddecl(declaration rewrite) hooks; implement either or both. Plugins are tree-shaken out of any app that doesn't import them.import type { SCPlugin } from 'styled-components'; // `rw` runs on every fully-resolved selector after `&` substitution and // namespace prepending. Return a new selector string. const scopePlugin: SCPlugin = { name: 'scope', rw: selector => `.app ${selector}`, }; // `decl` runs on every emitted `prop: value` pair (top-level decls, decl-body // at-rules, keyframe frames). Return `{ prop, value }` to rewrite, or `void` // to leave the pair unchanged. const remToPxPlugin: SCPlugin = { name: 'rem-to-px', decl: (prop, value) => { const match = value.match(/^(-?\d*\.?\d+)rem$/); return match ? { prop, value: `${parseFloat(match[1]) * 16}px` } : undefined; }, };
The
namefield is required and identifies the plugin so different plugin sets across nested<StyleSheetManager>trees stay isolated.
Minor Changes
-
c5e1c54: Added a second argument to function-form
attrs((props, ast) => ...)callbacks for bridging styles into props on third-party components. Theastaccessor exposespeek(read a value) andpop(read and remove from the rendered style), and accepts either a CSS property name or a typed dot-separated theme path (e.g.'color.red.500'). Path autocomplete and value-type inference flow from your augmented theme.import { Path } from 'react-native-svg'; const Icon = styled(Path).attrs((_props, ast) => ({ fill: ast.pop('color'), // lift CSS decl into a prop stroke: ast.peek('palette.brand'), // read from theme via typed path }))` color: red; `;
Both methods take an optional fallback as the second argument, returned when the value is missing. Works on web and native. When the callback's behavior is fully determined by static declarations the work folds into a one-time computation at construction so renders pay nothing extra.
-
c5e1c54: Modern CSS functions now work in React Native styles. Static expressions resolve up front; values that depend on the device environment (viewport, container, safe area, color scheme) re-resolve as the environment changes.
import styled from 'styled-components/native'; const Card = styled.View` width: clamp(240px, 80vw, 480px); background-color: light-dark(white, #111); padding-top: env(safe-area-inset-top); border-radius: 8px; @container card (min-width: 320px) { padding: 24px; } `;
clamp(10px, 50%, 400px)/min(100px, 50vw)/max(200px, 100vh)/calc(100vw - 40px)with any mix of static and runtime-resolvable arms.oklch(...),oklab(...),lch(...),lab(...)resolve to a color React Native can render. Wide-gamut inputs that fall outside sRGB are mapped to the closest in-gamut color while preserving hue, so the rendered result stays as close as possible to what was written.color-mix(in <space>, …)mixes through the requested space (srgb,oklab,oklch,lab,lch) and converts back to sRGB for display.- Viewport units
vw/vh/vmin/vmax/dvh/svh/lvhscale to the current window dimensions. - Container query units
cqw/cqh/ `cqmi...
styled-components@7.0.0-prerelease-20260511181111
Major Changes
-
c5e1c54: React Native:
createTheme()now works exactly the way it does on web. Pass the returned object toThemeProvider, reference leaves in your styled components, and the current theme resolves automatically.import styled, { createTheme, ThemeProvider } from 'styled-components/native'; const theme = createTheme({ colors: { bg: '#ffffff', text: '#111111' }, }); const Card = styled.View` background-color: ${theme.colors.bg}; border-color: ${theme.colors.text}; `; <ThemeProvider theme={{ colors: { bg: '#111', text: '#eee' } }}> <Card /> </ThemeProvider>
Nested
ThemeProviders on React Native deep-merge their theme objects so an inner override that only touches one leaf keeps the siblings it inherited; a child provider that setscolors.textkeepscolors.bgfrom the ancestor. Web behavior is unchanged. -
c5e1c54: Styled components no longer honor
defaultProps. React 19 removeddefaultPropssupport from function components, so styled components can no longer inherit a parent'sdefaultPropseither.Migration: use
.attrs()for prop defaults. The object form always wins over user-provided props (this is intentional, see the attrs FAQ). The function form lets user-provided props override the default:// Before (v6, no longer applies in v7) const Button = styled.button``; Button.defaultProps = { type: 'button' }; // After (object form always wins) const Button = styled.button.attrs({ type: 'button' })``; // After (user-provided overrides allowed) const Button = styled.button.attrs<{ type?: string }>(p => ({ type: p.type ?? 'button', }))``;
For a default theme, wrap the tree in
<ThemeProvider theme={...}>instead. -
c5e1c54: Removed the
disableCSSOMInjectionprop on<StyleSheetManager>and theSC_DISABLE_SPEEDY/REACT_APP_SC_DISABLE_SPEEDYenvironment variables. Added a newextractCSSexport.Browser builds now always use the same fast injection path that production has used by default for years. There's no longer a knob to switch into a slower text-based mode at runtime, and dev and production now behave identically.
If you were using the toggle to make CSS visible as text (for static-render pipelines, micro-frontend cloning, embedding into iframes or Shadow DOM, or extraction tooling), call the new
extractCSS()function after render to get the current CSS as a plain string:import { extractCSS } from 'styled-components'; // after rendering const css = extractCSS();
The result is plain CSS without the rehydration markers used by
ServerStyleSheet, so it can be injected directly into another document, stamped into a cloned DOM tree, or written to disk. -
c5e1c54: Mounting the same
createGlobalStylecomponent multiple times now emits its CSS only once. Previously each mount produced its own copy of the stylesheet rules. Rendering output stays byte-stable across SSR, client, and Server Components. -
c5e1c54: React Native: the CSS-to-style-object translation layer is now built in. Several long-standing limitations go away on the native path.
transform: matrix(...)/matrix3d(...)work.transform: translateX(10)(bare number, no unit) works.background-image: linear-gradient(...)/radial-gradient(...)work.filter: blur(4px) saturate(1.5)and the full filter-function chain work.- Modern color notations pass through to React Native's color parser unchanged:
rgb(r g b / a)slash-alpha,hwb(),hsl()all work. box-shadowwith spread and inset pass through as CSS strings.mix-blend-mode,isolation,cursorflow through.
import styled from 'styled-components/native'; const Tile = styled.View` background-image: linear-gradient(135deg, hsl(220 80% 60%), hsl(280 70% 50%)); filter: blur(2px) saturate(1.5); box-shadow: 0 4px 12px rgb(0 0 0 / 0.2); transform: matrix(1, 0, 0, 1, 8, 0); `;
The transform layer also fixes
border: noneemittingborder-style: solidon native; it now emitsborder-style: noneto match the rest of the ecosystem.iOS setup note for filters: in React Native 0.85, the
filterprimitivesblur,saturate,hue-rotate,grayscale,contrast, anddrop-shadowonly render when your iOS app opts into the SwiftUI-based filter backend. SetReactNativeReleaseLeveltoexperimentalin your iOSInfo.plist(orios.infoPlistinapp.jsonfor Expo) to enable it.brightnessandopacitywork without this flag. -
c5e1c54: Raised peer dependency floors:
reactandreact-domnow require>= 19.0.0(was>= 16.8).react-nativenow requires>= 0.85.0(was>= 0.68).css-to-react-nativeis no longer a peer dependency. Apps that listed it solely for styled-components can drop it from theirpackage.json.- The
enableVendorPrefixesprop on<StyleSheetManager>and the runtime vendor prefixer have been removed. Modern browser targets handle prefixing natively; for the few properties that still need them (e.g.-webkit-backdrop-filteron Safari), declare both the prefixed and unprefixed forms in your CSS, or run a build-time PostCSS transform.
Older React / React Native projects should stay on styled-components v6.
-
c5e1c54: Plugins moved to a dedicated
styled-components/pluginssubpath, and a first-party RTL plugin ships with the library.import { StyleSheetManager } from 'styled-components'; import { rtlPlugin, rscPlugin } from 'styled-components/plugins'; <StyleSheetManager plugins={[rtlPlugin]}> <App /> </StyleSheetManager>
rtlPluginreplacesstylis-plugin-rtlfor users coming from v6: it swaps physical side properties (padding-left↔padding-right), flipsleft/rightkeyword values onfloat/clear/text-align/caption-side, and mirrors 4-value shorthand positions. Logical properties likemargin-inline-startpass through unchanged.The
stylisPluginsprop on<StyleSheetManager>is nowplugins, and the top-levelstylisPluginRSCexport has moved into the new subpath asrscPlugin.Migration:
-import { rtl, stylisPluginRSC } from 'styled-components'; +import { rtlPlugin, rscPlugin } from 'styled-components/plugins'; -<StyleSheetManager stylisPlugins={[rtl, stylisPluginRSC]}> +<StyleSheetManager plugins={[rtlPlugin, rscPlugin]}>
Custom plugins authored against the v6 stylis contract need to port to the narrower plugin interface, which exposes
rw(selector rewrite) anddecl(declaration rewrite) hooks; implement either or both. Plugins are tree-shaken out of any app that doesn't import them.import type { SCPlugin } from 'styled-components'; // `rw` runs on every fully-resolved selector after `&` substitution and // namespace prepending. Return a new selector string. const scopePlugin: SCPlugin = { name: 'scope', rw: selector => `.app ${selector}`, }; // `decl` runs on every emitted `prop: value` pair (top-level decls, decl-body // at-rules, keyframe frames). Return `{ prop, value }` to rewrite, or `void` // to leave the pair unchanged. const remToPxPlugin: SCPlugin = { name: 'rem-to-px', decl: (prop, value) => { const match = value.match(/^(-?\d*\.?\d+)rem$/); return match ? { prop, value: `${parseFloat(match[1]) * 16}px` } : undefined; }, };
The
namefield is required and identifies the plugin so different plugin sets across nested<StyleSheetManager>trees stay isolated.
Minor Changes
-
c5e1c54: Added a second argument to function-form
attrs((props, ast) => ...)callbacks for bridging styles into props on third-party components. Theastaccessor exposespeek(read a value) andpop(read and remove from the rendered style), and accepts either a CSS property name or a typed dot-separated theme path (e.g.'color.red.500'). Path autocomplete and value-type inference flow from your augmented theme.import { Path } from 'react-native-svg'; const Icon = styled(Path).attrs((_props, ast) => ({ fill: ast.pop('color'), // lift CSS decl into a prop stroke: ast.peek('palette.brand'), // read from theme via typed path }))` color: red; `;
Both methods take an optional fallback as the second argument, returned when the value is missing. Works on web and native. When the callback's behavior is fully determined by static declarations the work folds into a one-time computation at construction so renders pay nothing extra.
-
c5e1c54: Modern CSS functions now work in React Native styles. Static expressions resolve up front; values that depend on the device environment (viewport, container, safe area, color scheme) re-resolve as the environment changes.
import styled from 'styled-components/native'; const Card = styled.View` width: clamp(240px, 80vw, 480px); background-color: light-dark(white, #111); padding-top: env(safe-area-inset-top); border-radius: 8px; @container card (min-width: 320px) { padding: 24px; } `;
clamp(10px, 50%, 400px)/min(100px, 50vw)/max(200px, 100vh)/calc(100vw - 40px)with any mix of static and runtime-resolvable arms.oklch(...),oklab(...),lch(...),lab(...)resolve to a color React Native can render. Wide-gamut inputs that fall outside sRGB are mapped to the closest in-gamut color while preserving hue, so the rendered result stays as close as possible to what was written.color-mix(in <space>, …)mixes through the requested space (srgb,oklab,oklch,lab,lch) and converts back to sRGB for display.- Viewport units
vw/vh/vmin/vmax/dvh/svh/lvhscale to the current window dimensions. - Container query units
cqw/cqh/ `cqmi...
styled-components@7.0.0-prerelease-20260511073743
Major Changes
-
26a3151: React Native:
createTheme()now works exactly the way it does on web. Pass the returned object toThemeProvider, reference leaves in your styled components, and the current theme resolves automatically.import styled, { createTheme, ThemeProvider } from 'styled-components/native'; const theme = createTheme({ colors: { bg: '#ffffff', text: '#111111' }, }); const Card = styled.View` background-color: ${theme.colors.bg}; border-color: ${theme.colors.text}; `; <ThemeProvider theme={{ colors: { bg: '#111', text: '#eee' } }}> <Card /> </ThemeProvider>
Nested
ThemeProviders on React Native deep-merge their theme objects so an inner override that only touches one leaf keeps the siblings it inherited; a child provider that setscolors.textkeepscolors.bgfrom the ancestor. Web behavior is unchanged. -
26a3151: Styled components no longer honor
defaultProps. React 19 removeddefaultPropssupport from function components, so styled components can no longer inherit a parent'sdefaultPropseither.Migration: use
.attrs()for prop defaults. The object form always wins over user-provided props (this is intentional, see the attrs FAQ). The function form lets user-provided props override the default:// Before (v6, no longer applies in v7) const Button = styled.button``; Button.defaultProps = { type: 'button' }; // After (object form always wins) const Button = styled.button.attrs({ type: 'button' })``; // After (user-provided overrides allowed) const Button = styled.button.attrs<{ type?: string }>(p => ({ type: p.type ?? 'button', }))``;
For a default theme, wrap the tree in
<ThemeProvider theme={...}>instead. -
26a3151: Removed the
disableCSSOMInjectionprop on<StyleSheetManager>and theSC_DISABLE_SPEEDY/REACT_APP_SC_DISABLE_SPEEDYenvironment variables. Added a newextractCSSexport.Browser builds now always use the same fast injection path that production has used by default for years. There's no longer a knob to switch into a slower text-based mode at runtime, and dev and production now behave identically.
If you were using the toggle to make CSS visible as text (for static-render pipelines, micro-frontend cloning, embedding into iframes or Shadow DOM, or extraction tooling), call the new
extractCSS()function after render to get the current CSS as a plain string:import { extractCSS } from 'styled-components'; // after rendering const css = extractCSS();
The result is plain CSS without the rehydration markers used by
ServerStyleSheet, so it can be injected directly into another document, stamped into a cloned DOM tree, or written to disk. -
26a3151: Mounting the same
createGlobalStylecomponent multiple times now emits its CSS only once. Previously each mount produced its own copy of the stylesheet rules. Rendering output stays byte-stable across SSR, client, and Server Components. -
26a3151: React Native: the CSS-to-style-object translation layer is now built in. Several long-standing limitations go away on the native path.
transform: matrix(...)/matrix3d(...)work.transform: translateX(10)(bare number, no unit) works.background-image: linear-gradient(...)/radial-gradient(...)work.filter: blur(4px) saturate(1.5)and the full filter-function chain work.- Modern color notations pass through to React Native's color parser unchanged:
rgb(r g b / a)slash-alpha,hwb(),hsl()all work. box-shadowwith spread and inset pass through as CSS strings.mix-blend-mode,isolation,cursorflow through.
import styled from 'styled-components/native'; const Tile = styled.View` background-image: linear-gradient(135deg, hsl(220 80% 60%), hsl(280 70% 50%)); filter: blur(2px) saturate(1.5); box-shadow: 0 4px 12px rgb(0 0 0 / 0.2); transform: matrix(1, 0, 0, 1, 8, 0); `;
The transform layer also fixes
border: noneemittingborder-style: solidon native; it now emitsborder-style: noneto match the rest of the ecosystem.iOS setup note for filters: in React Native 0.85, the
filterprimitivesblur,saturate,hue-rotate,grayscale,contrast, anddrop-shadowonly render when your iOS app opts into the SwiftUI-based filter backend. SetReactNativeReleaseLeveltoexperimentalin your iOSInfo.plist(orios.infoPlistinapp.jsonfor Expo) to enable it.brightnessandopacitywork without this flag. -
26a3151: Raised peer dependency floors:
reactandreact-domnow require>= 19.0.0(was>= 16.8).react-nativenow requires>= 0.85.0(was>= 0.68).css-to-react-nativeis no longer a peer dependency. Apps that listed it solely for styled-components can drop it from theirpackage.json.- The
enableVendorPrefixesprop on<StyleSheetManager>and the runtime vendor prefixer have been removed. Modern browser targets handle prefixing natively; for the few properties that still need them (e.g.-webkit-backdrop-filteron Safari), declare both the prefixed and unprefixed forms in your CSS, or run a build-time PostCSS transform.
Older React / React Native projects should stay on styled-components v6.
-
26a3151: Plugins moved to a dedicated
styled-components/pluginssubpath, and a first-party RTL plugin ships with the library.import { StyleSheetManager } from 'styled-components'; import { rtlPlugin, rscPlugin } from 'styled-components/plugins'; <StyleSheetManager plugins={[rtlPlugin]}> <App /> </StyleSheetManager>
rtlPluginreplacesstylis-plugin-rtlfor users coming from v6: it swaps physical side properties (padding-left↔padding-right), flipsleft/rightkeyword values onfloat/clear/text-align/caption-side, and mirrors 4-value shorthand positions. Logical properties likemargin-inline-startpass through unchanged.The
stylisPluginsprop on<StyleSheetManager>is nowplugins, and the top-levelstylisPluginRSCexport has moved into the new subpath asrscPlugin.Migration:
-import { rtl, stylisPluginRSC } from 'styled-components'; +import { rtlPlugin, rscPlugin } from 'styled-components/plugins'; -<StyleSheetManager stylisPlugins={[rtl, stylisPluginRSC]}> +<StyleSheetManager plugins={[rtlPlugin, rscPlugin]}>
Custom plugins authored against the v6 stylis contract need to port to the narrower plugin interface, which exposes
rw(selector rewrite) anddecl(declaration rewrite) hooks; implement either or both. Plugins are tree-shaken out of any app that doesn't import them.import type { SCPlugin } from 'styled-components'; // `rw` runs on every fully-resolved selector after `&` substitution and // namespace prepending. Return a new selector string. const scopePlugin: SCPlugin = { name: 'scope', rw: selector => `.app ${selector}`, }; // `decl` runs on every emitted `prop: value` pair (top-level decls, decl-body // at-rules, keyframe frames). Return `{ prop, value }` to rewrite, or `void` // to leave the pair unchanged. const remToPxPlugin: SCPlugin = { name: 'rem-to-px', decl: (prop, value) => { const match = value.match(/^(-?\d*\.?\d+)rem$/); return match ? { prop, value: `${parseFloat(match[1]) * 16}px` } : undefined; }, };
The
namefield is required and identifies the plugin so different plugin sets across nested<StyleSheetManager>trees stay isolated.
Minor Changes
-
26a3151: Added a second argument to function-form
attrs((props, ast) => ...)callbacks for bridging styles into props on third-party components. Theastaccessor exposespeek(read a value) andpop(read and remove from the rendered style), and accepts either a CSS property name or a typed dot-separated theme path (e.g.'color.red.500'). Path autocomplete and value-type inference flow from your augmented theme.import { Path } from 'react-native-svg'; const Icon = styled(Path).attrs((_props, ast) => ({ fill: ast.pop('color'), // lift CSS decl into a prop stroke: ast.peek('palette.brand'), // read from theme via typed path }))` color: red; `;
Both methods take an optional fallback as the second argument, returned when the value is missing. Works on web and native. When the callback's behavior is fully determined by static declarations the work folds into a one-time computation at construction so renders pay nothing extra.
-
26a3151: Modern CSS functions now work in React Native styles. Static expressions resolve up front; values that depend on the device environment (viewport, container, safe area, color scheme) re-resolve as the environment changes.
import styled from 'styled-components/native'; const Card = styled.View` width: clamp(240px, 80vw, 480px); background-color: light-dark(white, #111); padding-top: env(safe-area-inset-top); border-radius: 8px; @container card (min-width: 320px) { padding: 24px; } `;
clamp(10px, 50%, 400px)/min(100px, 50vw)/max(200px, 100vh)/calc(100vw - 40px)with any mix of static and runtime-resolvable arms.oklch(...),oklab(...),lch(...),lab(...)resolve to a color React Native can render. Wide-gamut inputs that fall outside sRGB are mapped to the closest in-gamut color while preserving hue, so the rendered result stays as close as possible to what was written.color-mix(in <space>, …)mixes through the requested space (srgb,oklab,oklch,lab,lch) and converts back to sRGB for display.- Viewport units
vw/vh/vmin/vmax/dvh/svh/lvhscale to the current window dimensions. - Container query units
cqw/cqh/ `cqmi...