You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Move quick reference near the top for fast LLM access. Consolidate
SSR patterns (Next.js registry, Vite non-streaming, Vite streaming)
into a dedicated section. Promote color mode reference to standalone
section. Remove duplicated dark mode dual signal from Good to know.
@@ -12,18 +12,121 @@ v6.3: React Server Components supported. No `'use client'` needed. Styled compon
12
12
13
13
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.
14
14
15
-
## Recommended setup
15
+
## Setup
16
16
17
17
```
18
18
npm install styled-components
19
19
```
20
20
21
21
Next.js: add `compiler: { styledComponents: true }` to next.config.js. That's it. RSC works out of the box in v6.3+.
- `stylisPluginRSC` — fix child-index selectors in RSC
46
+
- `ServerStyleSheet` — SSR style collection
47
+
48
+
## Server-side rendering
49
+
50
+
### Next.js
51
+
52
+
Use a style registry to collect styles from client components during SSR. RSC-rendered styled components are handled automatically — the registry is only needed for the client tree.
53
+
54
+
```tsx
55
+
// lib/registry.tsx
56
+
'use client';
57
+
import { useState } from 'react';
58
+
import { useServerInsertedHTML } from 'next/navigation';
59
+
import { ServerStyleSheet, StyleSheetManager } from 'styled-components';
Mount `<Registry>` in your root layout. Pass a `nonce` prop if your app uses CSP (see CSP nonce section below).
77
+
78
+
Next.js 16 style deduplication: render the collected styles with `precedence="styled-components"` and a stable `href` so React deduplicates them across route segments.
79
+
80
+
### Vite — non-streaming
81
+
82
+
Simpler approach, works with any Vite SSR framework:
83
+
84
+
```tsx
85
+
// entry-server.tsx
86
+
import { renderToString } from 'react-dom/server';
87
+
import { ServerStyleSheet } from 'styled-components';
88
+
import App from './App';
89
+
90
+
export function render() {
91
+
const sheet = new ServerStyleSheet();
92
+
try {
93
+
const html = renderToString(sheet.collectStyles(<App />));
94
+
const styleTags = sheet.getStyleTags();
95
+
return { html, styleTags };
96
+
} finally {
97
+
sheet.seal();
98
+
}
99
+
}
100
+
```
101
+
102
+
Inject `styleTags` into `<head>`. If you use [Vike](https://vike.dev/), the `vike-react-styled-components` extension handles this automatically.
103
+
104
+
### Vite — streaming
105
+
106
+
v6.2+, works with `renderToPipeableStream`:
107
+
108
+
```tsx
109
+
// entry-server.tsx
110
+
import { renderToPipeableStream } from 'react-dom/server';
111
+
import { ServerStyleSheet } from 'styled-components';
`interleaveWithNodeStream` accepts both legacy `ReadableStream` and React 18's `PipeableStream`. It inserts `<style>` tags into the HTML stream as components render inside Suspense boundaries.
129
+
27
130
## Styled-components in RSC
28
131
29
132
Styled components work in server components with no `'use client'` directive. The main constraint is that `ThemeProvider` relies on React context, which doesn't exist in RSC — so `p.theme` is undefined. Use `createTheme()` instead.
@@ -159,7 +262,7 @@ const Card = styled.div`
159
262
`;
160
263
```
161
264
162
-
### Three-way color mode (light / dark / auto) without FOUC
265
+
## Three-way color mode (light / dark / auto) without FOUC
@@ -223,7 +326,7 @@ export default function RootLayout({ children }: { children: React.ReactNode })
223
326
224
327
**3. Toggle component**
225
328
226
-
Cycles light → dark → auto. Reads `localStorage` (not the DOM class) to distinguish "explicit dark" from "auto + system dark". Sets both `html.dark` class (for CSS) and `data-theme="dark"` (for third-party tools like DocSearch).
329
+
Cycles light -> dark -> auto. Reads `localStorage` (not the DOM class) to distinguish "explicit dark" from "auto + system dark". Sets both `html.dark` class (for CSS) and `data-theme="dark"` (for third-party tools like DocSearch).
227
330
228
331
```tsx
229
332
// components/ThemeToggle.tsx
@@ -282,19 +385,19 @@ export default function ThemeToggle() {
282
385
```
283
386
284
387
How it works together:
285
-
- **First visit, no preference stored:** blocking script does nothing → CSS `@media (prefers-color-scheme: dark)` handles it → no flash
286
-
- **User picks dark:** script adds `.dark` before paint → `html.dark` rule wins → no flash
287
-
- **User picks light on a dark-preference system:** script adds `.light` → `html:not(.light)` excludes the media query override → no flash
288
-
- **User picks auto after previously picking dark:** `localStorage` cleared → script falls through to system preference → correct on next load
388
+
- **First visit, no preference stored:** blocking script does nothing -> CSS `@media (prefers-color-scheme: dark)` handles it -> no flash
389
+
- **User picks dark:** script adds `.dark` before paint -> `html.dark` rule wins -> no flash
390
+
- **User picks light on a dark-preference system:** script adds `.light` -> `html:not(.light)` excludes the media query override -> no flash
391
+
- **User picks auto after previously picking dark:** `localStorage` cleared -> script falls through to system preference -> correct on next load
For Next.js, pass the nonce to the style registry (see the SSR section above). For Vite, use a `<meta>` tag or pass it to `ServerStyleSheet`'s constructor.
- `as` and `forwardedAs` props are now included in `React.ComponentProps` extraction for styled components (v6.4+).
414
495
- `createGlobalStyle` without interpolations only runs once at mount. With interpolations it re-evaluates per render, so prefer static rules where possible.
415
496
- Register animatable theme tokens with `@property`. CSS custom properties animate as strings by default — `transition: color 300ms` won't ease between `oklch(...)` values unless the property is registered: `@property --sc-color-bg { syntax: '<color>'; inherits: true; initial-value: oklch(0.99 0 0); }`. Put these in `createGlobalStyle` or a static CSS file loaded at root.
416
-
- Next.js 16 style deduplication: when using `ServerStyleSheet` inside a Next.js 16 registry, render the collected styles with `precedence="styled-components"` and a stable `href` so React deduplicates them across route segments.
417
-
- Dark mode dual signal: class plus `data-theme`. If your site integrates with tools like DocSearch that sniff a `data-theme="dark"` attribute, set both `html.dark` (for CSS `.dark { }` selectors) and `html[data-theme="dark"]` from the theme-init script. The styled-components CSS only needs the class; the data attribute is for third-party consumers.
0 commit comments