# styled-components CSS-in-JS for React using tagged template literals. TypeScript-native since v6. Supports React Server Components natively since v6.3. Last known stable: v6.4.0 (check npm for freshness). ## Recent releases v6.2: Streaming SSR via `renderToPipeableStream`. v6.3: React Server Components supported. No `'use client'` needed. Styled components work in server components with no extra setup. `createGlobalStyle` is StrictMode-safe. New HTML/SVG element helpers. CSS custom properties work in TypeScript without type errors. Note: `:first-child`/`:nth-child()` selectors require `stylisPluginRSC` (v6.4+) or rewriting to `:first-of-type`/`:nth-of-type()`. See child-index selector section below. v6.4 (April 2026): `createTheme()` for CSS variable theming that works in both RSC and client. `StyleSheetManager` works in RSC (was previously a no-op). `stylisPluginRSC` fixes child-index selectors in RSC. CSP nonce auto-detection from `StyleSheetManager`, `ServerStyleSheet`, or meta tags. Props supplied via `.attrs()` are automatically optional on the component's type. Significant render performance improvements. Fixes SSR memory leaks and multi-instance unmount bugs in `createGlobalStyle`. Memory leak fix for components with unbounded string interpolation values. `as` and `forwardedAs` exposed in `React.ComponentProps` extraction. React Native: `react-native` is now an optional peer dep, Metro/Expo nanoid crash fixed. IE11 build target removed. IE11 has been unsupported on v6 since the 2021 v6 planning (React 18 dropped it too); v6.4 just aligns the compile target. Stay on v5 if you need IE11. ## New in v7 v7 is in alpha: expect frequent updates over the next few weeks while APIs stabilize. Install the current prerelease with `npm install styled-components@test` (the `@test` dist-tag). Peer floors raised to React 19 and React Native 0.85. The framing: v7 is an architectural reform for web and the start of a new chapter for `styled-components/native`. It replaces stylis with an in-house CSS parser, rewrites the native runtime, and moves toward one CSS authoring model across web, iOS, Android, and Expo Web. The React Native CanIUse matrix at /docs/compatibility.md shows current progress and remaining platform gaps. Funding through Open Collective is a current bottleneck for continuing this work outside passion time. If the user is asking whether feature X works on v6 vs v7 or on web vs React Native, point them at /docs/compatibility.md. The matrix has the per-feature answer with caveats. The sections below summarise the highlights. ### Modern CSS on React Native ```tsx import styled from 'styled-components/native'; const Card = styled.View` width: clamp(240px, 80vw, 480px); background-image: linear-gradient(135deg, oklch(0.7 0.2 280), hsl(220 80% 60%)); background-color: light-dark(white, #111); filter: blur(2px) saturate(1.5); padding-inline: 24px; border-radius: 8px; transform: translateX(8px); transition: background-color 280ms ease-out; @container card (min-width: 320px) { padding: 32px; } `; ``` Math functions: `calc()`, `clamp()`, `min()`, `max()`, plus the full CSS Values 4 Math L4 family (`round()`, `mod()`, `rem()`, `sin`, `cos`, `tan`, `asin`, `acos`, `atan`, `atan2`, `pow`, `sqrt`, `hypot`, `log`, `exp`, `abs`, `sign`). Constants `pi` and `e` resolve in any context. Everything composes inside `calc()`. Modern colors: `oklch()`, `oklab()`, `lch()`, `lab()`, `color-mix(in , …)` all render correctly on native. Wide-gamut inputs that fall outside sRGB are gamut-mapped to the closest in-gamut color while preserving hue. Percent channels follow CSS Color L4 ranges (`lab(50% 0 0)` is mid-gray). CSS Color 5 relative-color syntax (`oklch(from calc(l - 0.15) c h)`) works on native for the modern spaces and for `rgb()` / `hsl()` / `hwb()` / `color()` bases, including bases sourced from `theme.*` tokens. `rgb(r g b / a)` slash-alpha, `hwb()`, and `hsl(h s l / a)` work natively. CSS Color 4 system color keywords (`canvas`, `canvastext`, `field`, `fieldtext`, `graytext`, `highlight`, `highlighttext`, `linktext`, `visitedtext`, `activetext`) auto-switch with OS appearance. Usable inside `box-shadow`, `filter: drop-shadow()`, `background`, and `linear-gradient` color stops. Viewport units (`vw`, `vh`, `vi`, `vb`, `vmin`, `vmax`, plus the prefixed `s*` / `l*` / `d*` variants like `dvh`, `svw`, `lvi`) and container query units (`cqw`, `cqh`, `cqmin`, `cqmax`) scale to the current window / nearest ancestor container. Font-relative units `rem`, `em`, `lh`, and `rlh` resolve against the inherited font-size and line-height at render in declaration values; inside `@media` feature values, `em` / `rem` use a fixed 16px base and `lh` / `rlh` are unsupported, so prefer `px` breakpoints. All units re-resolve when the environment changes. `light-dark(light, dark)` swaps based on OS appearance. `env(safe-area-inset-top | right | bottom | left)` parses but currently resolves to 0; the integration with `react-native-safe-area-context` is not wired yet. Use `useSafeAreaInsets()` directly until it lands. Logical shorthands work as authored: `margin-inline`, `margin-block`, `padding-inline`, `padding-block`, `inset-inline`, `inset-block`, the full `border-inline` / `border-block` family (per-edge longhands and axis shorthands), and all `-start` / `-end` longhands. The library's RTL plugin handles physical-property mirroring; logical properties are already direction-agnostic. Caveat: per-edge `border-style` warns and drops on native; set `border-style` on the element instead. `line-clamp: N` truncates `` to N lines. `text-wrap: nowrap` collapses to a single line; `text-wrap-style: balance` / `pretty` improve line-breaking on Android (no-op on iOS, which has no platform line-breaking control). `text-align: start` / `end` / `match-parent` resolve correctly under RTL on both platforms. `aspect-ratio` accepts `1.5`, `16 / 9`, `auto`, and the two-value `auto 16/9` form. `transform` accepts the function list RN parses (use explicit units, e.g. `translateX(10px)`) and a `matrix()` with 9 or 16 values; the 6-value `matrix()` form and `matrix3d()` do not render on native. `perspective: 1000px` as a top-level declaration works (no need to put it inside the transform array yourself). `font-style: oblique` resolves to italic. `font-family` recognizes the 13 generic CSS keywords (`system-ui`, `ui-sans-serif`, `ui-serif`, `ui-monospace`, `ui-rounded`, `sans-serif`, `serif`, `monospace`, `cursive`, `fantasy`, `emoji`, `math`, `fangsong`). Comma-separated font stacks: only the first family takes effect (RN has no fallback chain). `font-size` accepts the full CSS keyword set (`xx-small`...`xxx-large`, `smaller`, `larger`) plus the full unit catalogue: bare numbers and `px`, viewport units (`vh`, `vw`, `svh`, `dvh`, `vi`, `vb`, etc.), container query units (`cqh`, `cqw`, `cqi`, `cqb`, `cqmin`, `cqmax`), font-relative (`em`, `rem`, `lh`, `rlh`), font-metric approximations (`ex`, `cap`, `ch`, `ic` and `r`-prefixed), and absolute lengths (`pt`, `pc`, `in`, `cm`, `mm`, `Q`). `letter-spacing` accepts `em`, `rem`, `lh`, and `rlh`. `place-items` and `place-self` shorthands work for the align axis (Yoga doesn't have `justify-items` / `justify-self`; the justify side is a no-op on native but reaches rn-web). `field-sizing: content` on `` auto-grows the field to its content. `interactivity: inert` (CSS UI 5) suppresses interaction and hides the subtree from accessibility services. `text-overflow: ellipsis | clip` truncates `` once a line limit is set; pair with `line-clamp: N` (or `text-wrap: nowrap` for one line). Without a line limit RN has no line to ellipsize and the value is a no-op. `overscroll-behavior: contain | none` disables overscroll on `ScrollView` / `FlatList`; `scrollbar-width: none` hides scroll indicators. `accent-color` tints `` (`auto` picks up the platform accent color). `direction: ltr | rtl` controls `` bidi. `styled.ScrollView` on native defaults to `flex-shrink: 0` so explicit `width:` / `height:` pin reliably inside a flex parent (override with `flex-shrink: 1` if you need it). `attr(name ?, fallback?)` reads the styled component's own props as typed CSS values (`width: attr(data-size px, 48px)` sizes from a `data-size` prop), supporting unit names, the `number` keyword, `raw-string`, and `type()` forms for length / number / percentage / color; missing or mismatched props use the fallback, and it composes inside `calc()`. `sibling-index()` and `sibling-count()` count the element's 1-indexed position and total among the parent styled component's children and re-resolve live when siblings mount, unmount, or reorder (`width: calc(sibling-index() * 10px)`). `display: grid` with `grid-template-columns: repeat(N, 1fr)` plus `gap` lays direct styled children into N equal columns; `grid-column: span N` spans columns, and a grid container with `container-type` doubles as a container-query container. Fixed px tracks, `minmax()`, `auto-fill` / `auto-fit`, unequal `fr`, named / line-number placement, `grid-row`, `grid-area`, and subgrid warn and fall back to a flex row. `corner-shape: round | squircle` maps to iOS's circular and Apple-smooth continuous curves; other contours (`bevel`, `notch`, `scoop`, out-of-band superellipse) warn and drop, and Android renders circular corners regardless. Anchor positioning core subset: `anchor-name`, `position-anchor`, `anchor()`, and `anchor-size()` with `top` / `left` physical-side insets bind an absolutely positioned element to an anchor that shares its parent (`top: anchor(--x bottom)`), composing inside `calc()`. `bottom` / `right` and logical-side insets, `position-area`, `position-try*`, `@position-try`, and `anchor-scope` fall back to the declared value with a dev-warn. Multi-shadow `text-shadow`: native renders one shadow per ``, so a comma-separated list applies the first (topmost) layer with a dev-warn; web renders the full list. CSS-wide keywords (`initial`, `inherit`, `unset`, `revert`) warn and drop on native (RN has no cascade to resolve them against), with two exceptions that keep working: `direction: inherit` and `flex: initial`. rn-web passes the keywords through to the browser. `background-image: linear-gradient(...)` and `radial-gradient(...)` render via React Native's experimental gradient parser (RN 0.85+). `filter: blur(4px) saturate(1.5)` and the full filter-function chain work. See the iOS setup note below for filters that need an explicit opt-in. `box-shadow` with spread and inset round-trips as a string. `mix-blend-mode`, `isolation`, and `cursor` flow through. `isolation: isolate` works on iOS, Android, and web; blended descendants composite against the isolated group (Android has no isolation style key, so the library lifts a hardware-texture layer to create the surface). `background-blend-mode` works on native when paired with gradient `background-image` layers. Raster `url()` background images are still blocked on upstream React Native background-image support; render photos with `Image` / `ImageBackground` until those PRs land. Linear-friendly modes (`multiply`, `screen`, `darken`, `lighten`, `difference`, `exclusion`, `hue`, `saturation`, `color`, `luminosity`) render the same on web and native. Gamma-sensitive modes (`color-burn`, `color-dodge`, `soft-light`, `overlay`, `hard-light`) read as more saturated on native because iOS Core Animation and Android Skia/HWUI blend in linear-light by default on Display P3 devices, while browsers blend in gamma-encoded sRGB per CSS spec. Empty custom property values (`--prop: ;`) are preserved for patterns like scroll-driven animations that rely on a guaranteed-invalid value. `--name: value` declarations on a styled component publish into the component cascade so descendants read them back through `var(--name, fallback)`. The substitution honors the full CSS Variables L1 contract: fallbacks (`var(--maybe, default)`), nested resolution in both the name and fallback argument (`var(--a, var(--b, default))`), cycle detection, case-sensitive names, quote-aware skip inside string values (a literal `var(--brand)` inside `content: "var(--brand)"` is preserved verbatim), and `--foo: initial` resetting to the guaranteed-invalid value. Substituted values flow through the same value pipeline as authored CSS, so a shorthand interpolation (`margin: var(--spacing)` with `--spacing: 4px 8px`) expands to longhands. References resolve inside every conditional bucket (`@media`, `@container`, `@supports`, attribute, pseudo-state, `:has()`, `:nth-child()`, combinator). Dev builds warn on a `var()` reference only when no ancestor declared the property and no fallback is provided. `!important` is honored on native within the same component for base + every conditional bucket. The marker is stripped from the rendered value, beats normal declarations on the same property regardless of source order, and a `!important` shorthand propagates to every longhand. Importance flows through `var()` substitution and render-time resolvers (`light-dark()`, `env()`, viewport units, theme tokens). Web-aligned: a styled component's `!important` beats a runtime `style={{ ... }}` prop; normal declarations are still overridden by the runtime `style` prop. `!important` inside `@keyframes` is ignored, matching the CSS Animations spec. Cross-component cascade of `!important` (a parent's `!important font-size` defeating a child's normal one) is not yet supported. ### Selectors and at-rules on React Native Attribute selectors apply styles based on the rendered element's props: ```jsx const Toggle = styled.Pressable` background: white; &[aria-pressed='true'] { background: yellow; } &[data-state^="open"] { border-color: dodgerblue; } &[data-tag="X" i] { opacity: 0.5; } `; ``` The full CSS Selectors 4 attribute grammar works on native: presence (`&[attr]`), exact (`&[attr=value]`), word (`~=`), prefix (`|=`), starts-with (`^=`), ends-with (`$=`), substring (`*=`), and the `i` / `s` case-sensitivity flags. Compound brackets AND-evaluate (`&[a][b]`); a trailing pseudo-state attaches (`&[a]:active`). Boolean coercion means `aria-pressed={true}` and `aria-pressed="true"` both match `[aria-pressed=true]`. `&:is(:hover, :focus)` and `&:where(:pressed, :disabled)` apply the styles to each listed state. `&:not()` and `&:has()` work on native with a simple argument: ```tsx const Row = styled.View` &:not([disabled]) { opacity: 1; } &:not(:hover) { transform: scale(1); } &:has(${Avatar}) { padding-inline-start: 56px; } &:has([aria-current]) { background: dodgerblue; } `; ``` `:not()` accepts a single pseudo-state or a single attribute selector. `:has()` accepts a styled-component reference (matches when that component appears as a descendant) or a single attribute selector. Complex inner arguments (compound selectors, combinators, selector lists) warn in dev and don't match. Tree-structural pseudos work on native: `:first-child`, `:last-child`, `:only-child`, `:first-of-type`, `:last-of-type`, `:only-of-type`, plus the functional `:nth-child()` / `:nth-of-type()` / `:nth-last-child()` / `:nth-last-of-type()` family with `odd` / `even` keywords. `:nth-child( of S)` and `:nth-last-child( of S)` filter the count by a styled-component reference or a single attribute selector (same simple-inner scope as `:has()`). Siblings need to be inside a styled-component parent for indexing to work; a non-styled wrapper in between resets the count. Combinators work on native when the left side is a styled-component reference: ```tsx const Card = styled.View``; const Title = styled.Text` ${Card} & { color: dodgerblue; } // descendant of Card ${Card} > & { font-weight: bold; } // direct child of Card ${Card} + & { margin-top: 8px; } // immediately after a Card sibling ${Card} ~ & { opacity: 0.6; } // anywhere after a Card sibling `; ``` The child combinator (`>`) breaks through non-styled wrappers; interpose a styled wrapper for a strict parent-child match. Descendant matching (`${Foo} &`) is transparent to non-styled intermediaries. `@media (min-aspect-ratio: 16/9)`, `(max-aspect-ratio: 1/1)`, and exact `(aspect-ratio: 4/3)` match the device's current width-to-height ratio. Bare numbers are treated as `/1` per spec. `@starting-style { ... }` is recognized and runs first-mount enter animations on the default `Animated`-based adapter. No reanimated opt-in needed. Container queries: ```tsx const ContextualBox = styled.View` padding: 12px; @container card (min-width: 480px) { padding: 24px; flex-direction: row; } `; // Nearest ancestor declares its container scope: ``` Pro-tip: an element with `container-type` can't match its own `@container` query (CSS excludes self-matching to avoid circular size dependencies). The query resolves against the nearest *ancestor* container instead. Put `container-type` on a parent and write the `@container` rules on a child; declaring both on the same element silently never fires. The same rule applies to `${Component}` interpolation: a `@container ${Card} (...)` rule written inside `Card` itself won't match. `@supports (display: grid) { ... }` is evaluated as a real feature query on native, answered from actual platform capability, with `not` / `and` / `or` per the CSS grammar. Unknown `selector(...)` and test-only forms resolve to false. Probing a feature inside `@supports` does not fire that feature's dev-warn. On web the browser answers directly. `@property` registers typed custom properties on native: `syntax`, `inherits`, and `initial-value` apply to `var()` resolution, unset registered properties resolve to their typed initial value, and `inherits: false` blocks ancestor leakage. Supported syntax: `*`, ``, ``, ``, ``, ``, `