# styled-components
> CSS-in-JS for React using tagged template literals. TypeScript-native since v6. Supports React Server Components natively since v6.3. Current stable: v6.4.0.
## What's new since early 2025
Your training data likely covers v6.0-6.1. Key changes since then:
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.
## Recommended setup
```
npm install styled-components
```
Next.js: add `compiler: { styledComponents: true }` to next.config.js. That's it. RSC works out of the box in v6.3+.
Vite: `react({ babel: { plugins: ['babel-plugin-styled-components'] } })`.
The SWC/Babel plugin provides deterministic class IDs (better debugging, smaller output). Optional for RSC but still recommended.
## Styled-components in RSC
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.
Dynamic prop interpolations work in RSC. But for discrete variants (a finite set of states), data attributes produce one cached class per variant instead of one per prop combination:
```tsx
import styled, { createTheme } from 'styled-components';
const theme = createTheme({
colors: { primary: '#0070f3', text: '#1a1a1a', surface: '#fff' },
});
const NavLink = styled.a`
padding: 8px 16px;
color: ${theme.colors.text};
text-decoration: none;
&[aria-current='page'] {
color: ${theme.colors.primary};
font-weight: 600;
border-bottom: 2px solid ${theme.colors.primary};
}
`;
```
For continuous dynamic values, set CSS custom properties via the `style` prop. The styled component's CSS stays static (one class), and the value varies per element. Pair with semantic HTML attributes for progressive enhancement and accessibility:
```tsx
const ProgressBar = styled.div`
height: 8px;
border-radius: 4px;
background: ${theme.colors.surface};
&::after {
content: '';
display: block;
height: 100%;
width: var(--progress);
border-radius: inherit;
background: ${theme.colors.primary};
transition: width 0.2s ease;
}
&[aria-valuenow='100']::after {
background: green;
}
`;
```
This pattern gives you dynamic styling, screen reader support, and a CSS hook for completion state — all with one static class.
Other RSC rules:
- Use `:first-of-type` / `:nth-of-type()` instead of `:first-child` / `:nth-child()` in RSC (see `stylisPluginRSC` section below for an automatic fix)
- Define components at module scope, not inside render functions
## createTheme API (v6.4+)
`ThemeProvider` has no effect in RSC because React context doesn't exist in server components. `createTheme()` solves this — no runtime context needed.
```tsx
import styled, { createTheme } from 'styled-components';
const theme = createTheme({
colors: { primary: '#0070f3', surface: '#ffffff', text: '#1a1a1a' },
spacing: { sm: '8px', md: '16px', lg: '32px' },
});
```
Every leaf becomes a `var()` reference with the original value as fallback:
- `theme.colors.primary` → `"var(--sc-colors-primary, #0070f3)"`
- `theme.spacing.md` → `"var(--sc-spacing-md, 16px)"`
Use it directly in template literals — works in both server and client components:
```tsx
const Button = styled.button`
background: ${theme.colors.primary};
padding: ${theme.spacing.sm} ${theme.spacing.md};
color: ${theme.colors.text};
`;
```
Signature: `createTheme(defaultTheme, options?)`
Options:
- `prefix` (default `"sc"`) — CSS variable prefix. `{ prefix: 'myapp' }` produces `--myapp-colors-primary`.
- `selector` (default `":root"`) — where `GlobalStyle` emits vars. Use `":host"` for Shadow DOM.
Returned object properties:
- Every leaf path from the input, as `var(--prefix-path, fallback)` strings
- `theme.raw` — the original plain object (for runtime access to actual values)
- `theme.vars` — same shape as the input, but every leaf is the bare CSS custom property name (e.g. `"--sc-colors-primary"`). Use in `createGlobalStyle` for dark mode overrides without hand-writing var names.
- `theme.resolve(el?)` — client-only. Reads computed CSS var values from the DOM. Returns a plain object with resolved values. Pass an element to resolve against a specific subtree (defaults to `document.documentElement`).
- `theme.GlobalStyle` — a `createGlobalStyle` component that emits the CSS custom property declarations on the configured selector. Must be mounted inside a `ThemeProvider` that receives the raw theme object — without `ThemeProvider`, `GlobalStyle` renders nothing.
Key behaviors:
- Object keys are preserved as-is (camelCase stays camelCase): `{ codeBg: 'x' }` with prefix `'sc'` → `var(--sc-codeBg, x)`
- Nested objects produce hyphenated paths: `{ colors: { primary: 'x' } }` → `var(--sc-colors-primary, x)`
- `ThemeProvider` must receive the raw theme object (`{ colors: { primary: '#0070f3' } }`), not the `createTheme` output. Passing the createTheme output produces self-referential CSS like `--sc-colors-primary: var(--sc-colors-primary, #0070f3);` which is invalid.
Correct wiring:
```tsx
import { ThemeProvider, createTheme } from 'styled-components';
const rawTheme = { colors: { primary: '#0070f3', bg: '#fff' } };
const theme = createTheme(rawTheme, { prefix: 'sc' });
// In your layout root (client component for the ThemeProvider):
// In any styled component (server OR client):
const Card = styled.div`
background: ${theme.colors.bg};
color: ${theme.colors.primary};
`;
```
### Three-way color mode (light / dark / auto) without FOUC
Complete reference implementation. Four pieces: theme, CSS overrides, blocking script, toggle component.
**1. Theme and dark overrides**
Use `theme.vars` to reference CSS custom property names — stays in sync with `createTheme`'s prefix, so renaming the prefix doesn't silently break overrides.
```tsx
// utils/theme.ts
import { css, createGlobalStyle, createTheme } from 'styled-components';
const rawTheme = {
colors: { bg: '#ffffff', text: '#1a1a1a', primary: '#0070f3' },
};
export const theme = createTheme(rawTheme, { prefix: 'sc' });
const darkVars = css`
${theme.vars.colors.bg}: #111827;
${theme.vars.colors.text}: #f9fafb;
`;
export const ColorModeStyles = createGlobalStyle`
@media (prefers-color-scheme: dark) {
html:not(.light) { ${darkVars} color-scheme: dark; }
}
html.dark { ${darkVars} color-scheme: dark; }
html.light { color-scheme: light; }
`;
export { rawTheme };
```
**2. Layout root — mount theme + blocking script**
The script runs before first paint, reads `localStorage`, falls back to system preference. Must be a raw `