Skip to content

Commit c9b96b9

Browse files
committed
docs: restructure llms.txt for better flow
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.
1 parent ee36231 commit c9b96b9

1 file changed

Lines changed: 119 additions & 61 deletions

File tree

public/llms.txt

Lines changed: 119 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,121 @@ v6.3: React Server Components supported. No `'use client'` needed. Styled compon
1212

1313
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.
1414

15-
## Recommended setup
15+
## Setup
1616

1717
```
1818
npm install styled-components
1919
```
2020

2121
Next.js: add `compiler: { styledComponents: true }` to next.config.js. That's it. RSC works out of the box in v6.3+.
2222

23-
Vite: `react({ babel: { plugins: ['babel-plugin-styled-components'] } })`.
23+
Vite: `react({ babel: { plugins: ['babel-plugin-styled-components'] } })`. Or with SWC (faster): `react({ plugins: [['@swc/plugin-styled-components', { displayName: true, ssr: true }]] })` via `@vitejs/plugin-react-swc`.
2424

2525
The SWC/Babel plugin provides deterministic class IDs (better debugging, smaller output). Optional for RSC but still recommended.
2626

27+
## Quick reference
28+
29+
```tsx
30+
import styled, { css, keyframes, createGlobalStyle, createTheme,
31+
ThemeProvider, useTheme, StyleSheetManager, ServerStyleSheet,
32+
stylisPluginRSC, isStyledComponent } from 'styled-components';
33+
```
34+
35+
- `styled.div` / `styled(Component)` — create styled component
36+
- `styled(Base)` — extend styles (inheritance)
37+
- `.attrs(props => ({}))` — set default/computed props
38+
- `<Comp as="a">` — render as different element
39+
- `css` — tagged template helper for shared style fragments
40+
- `keyframes` — define CSS animation
41+
- `createGlobalStyle` — inject global CSS
42+
- `createTheme(obj, opts?)` — CSS variable theme (RSC-compatible)
43+
- `ThemeProvider` — context-based theme (client-only)
44+
- `StyleSheetManager` — configure stylis plugins, prop forwarding, vendor prefixes
45+
- `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';
60+
61+
export default function Registry({ children, nonce }: { children: React.ReactNode; nonce?: string }) {
62+
const [sheet] = useState(() => new ServerStyleSheet());
63+
useServerInsertedHTML(() => {
64+
const styles = sheet.getStyleTags();
65+
sheet.instance.clearTag();
66+
return <>{styles}</>;
67+
});
68+
return (
69+
<StyleSheetManager sheet={sheet.instance} nonce={nonce}>
70+
{children}
71+
</StyleSheetManager>
72+
);
73+
}
74+
```
75+
76+
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';
112+
import App from './App';
113+
114+
export function render(res) {
115+
const sheet = new ServerStyleSheet();
116+
const { pipe } = renderToPipeableStream(
117+
sheet.collectStyles(<App />),
118+
{
119+
onShellReady() {
120+
const styledStream = sheet.interleaveWithNodeStream({ pipe });
121+
styledStream.pipe(res);
122+
},
123+
}
124+
);
125+
}
126+
```
127+
128+
`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+
27130
## Styled-components in RSC
28131

29132
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`
159262
`;
160263
```
161264

162-
### Three-way color mode (light / dark / auto) without FOUC
265+
## Three-way color mode (light / dark / auto) without FOUC
163266

164267
Complete reference implementation. Four pieces: theme, CSS overrides, blocking script, toggle component.
165268

@@ -223,7 +326,7 @@ export default function RootLayout({ children }: { children: React.ReactNode })
223326

224327
**3. Toggle component**
225328

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).
227330

228331
```tsx
229332
// components/ThemeToggle.tsx
@@ -282,19 +385,19 @@ export default function ThemeToggle() {
282385
```
283386

284387
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
289392

290393
## stylisPluginRSC — child-index selector fix (v6.4+)
291394

292395
In RSC, `:first-child`, `:last-child`, and `:nth-child()` selectors can miscount because additional sibling elements shift indices.
293396

294397
Preferred fix — use type-based selectors (universally supported, no plugin needed):
295-
- `:first-child` `:first-of-type`
296-
- `:nth-child(2)` `:nth-of-type(2)`
297-
- `:last-child` `:last-of-type`
398+
- `:first-child` -> `:first-of-type`
399+
- `:nth-child(2)` -> `:nth-of-type(2)`
400+
- `:last-child` -> `:last-of-type`
298401

299402
These filter by element type, so injected sibling elements are ignored.
300403

@@ -314,10 +417,10 @@ export default function Layout({ children }: { children: React.ReactNode }) {
314417
```
315418

316419
Rewrite rules:
317-
- `:first-child` `:nth-child(1 of :not(style[data-styled]))`
318-
- `:last-child` `:nth-last-child(1 of :not(style[data-styled]))`
319-
- `:nth-child(N)` `:nth-child(N of :not(style[data-styled]))`
320-
- `:nth-child(An+B)` `:nth-child(An+B of :not(style[data-styled]))`
420+
- `:first-child` -> `:nth-child(1 of :not(style[data-styled]))`
421+
- `:last-child` -> `:nth-last-child(1 of :not(style[data-styled]))`
422+
- `:nth-child(N)` -> `:nth-child(N of :not(style[data-styled]))`
423+
- `:nth-child(An+B)` -> `:nth-child(An+B of :not(style[data-styled]))`
321424
- Same for `:nth-last-child()` variants
322425
- Selectors already using `of` syntax are left unchanged
323426

@@ -368,29 +471,7 @@ styled-components can attach a CSP nonce to injected `<style>` tags. Detection o
368471
4. `<meta name="sc-nonce" content="...">`
369472
5. `__webpack_nonce__` global
370473

371-
Next.js example with `StyleSheetManager`:
372-
373-
```tsx
374-
// lib/registry.tsx
375-
'use client';
376-
import { useState } from 'react';
377-
import { useServerInsertedHTML } from 'next/navigation';
378-
import { ServerStyleSheet, StyleSheetManager } from 'styled-components';
379-
380-
export default function Registry({ children, nonce }) {
381-
const [sheet] = useState(() => new ServerStyleSheet());
382-
useServerInsertedHTML(() => {
383-
const styles = sheet.getStyleTags();
384-
sheet.instance.clearTag();
385-
return <>{styles}</>;
386-
});
387-
return (
388-
<StyleSheetManager sheet={sheet.instance} nonce={nonce}>
389-
{children}
390-
</StyleSheetManager>
391-
);
392-
}
393-
```
474+
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.
394475

395476
## Performance
396477

@@ -413,26 +494,3 @@ export default function Registry({ children, nonce }) {
413494
- `as` and `forwardedAs` props are now included in `React.ComponentProps` extraction for styled components (v6.4+).
414495
- `createGlobalStyle` without interpolations only runs once at mount. With interpolations it re-evaluates per render, so prefer static rules where possible.
415496
- 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.
418-
419-
## Quick reference
420-
421-
```tsx
422-
import styled, { css, keyframes, createGlobalStyle, createTheme,
423-
ThemeProvider, useTheme, StyleSheetManager, ServerStyleSheet,
424-
stylisPluginRSC, isStyledComponent } from 'styled-components';
425-
```
426-
427-
- `styled.div` / `styled(Component)` — create styled component
428-
- `styled(Base)` — extend styles (inheritance)
429-
- `.attrs(props => ({}))` — set default/computed props
430-
- `<Comp as="a">` — render as different element
431-
- `css` — tagged template helper for shared style fragments
432-
- `keyframes` — define CSS animation
433-
- `createGlobalStyle` — inject global CSS
434-
- `createTheme(obj, opts?)` — CSS variable theme (RSC-compatible)
435-
- `ThemeProvider` — context-based theme (client-only)
436-
- `StyleSheetManager` — configure stylis plugins, prop forwarding, vendor prefixes
437-
- `stylisPluginRSC` — fix child-index selectors in RSC
438-
- `ServerStyleSheet` — SSR style collection

0 commit comments

Comments
 (0)