Server components, client components, streaming SSR, React Native: one API.
styled-components is largely maintained by one person. Please help fund the project for consistent long-term support and updates: Open Collective
Style React components with real CSS, scoped automatically and delivered only when needed. No class name juggling, no separate files, no build step required.
- Works everywhere React runs. Server components, client components, streaming SSR, and React Native: same API, automatic runtime detection.
- Full CSS, no compromises. Media queries, pseudo-selectors, nesting, keyframes, global styles. If CSS supports it, so does styled-components.
- TypeScript-first. Built-in types ship with the package. Props flow through to your styles with full inference: no
@typesinstall, no manual generics. - <13kB gzipped. Small enough to disappear in your bundle. No build plugin required.
npm install styled-componentspnpm / yarn
pnpm add styled-componentsyarn add styled-componentsVary styles based on component props. Prefix transient props with $ to keep them off the DOM.
import styled from 'styled-components';
const Button = styled.button<{ $primary?: boolean }>`
background: ${props => (props.$primary ? 'palevioletred' : 'white')};
color: ${props => (props.$primary ? 'white' : 'palevioletred')};
font-size: 1em;
padding: 0.25em 1em;
border: 2px solid palevioletred;
border-radius: 3px;
`;
<Button>Normal</Button>
<Button $primary>Primary</Button>Build variants on top of existing styled components.
const TomatoButton = styled(Button)`
background: tomato;
color: white;
border-color: tomato;
`;Swap the rendered element without changing styles.
// Renders a <a> tag with Button styles
<Button as="a" href="/home">
Link Button
</Button>Use & to reference the component's generated class name; works with pseudo-classes, pseudo-elements, and nested selectors.
const Input = styled.input`
border: 1px solid #ccc;
border-radius: 4px;
padding: 0.5em;
&:focus {
border-color: palevioletred;
outline: none;
}
&::placeholder {
color: #aaa;
}
`;CSS is emitted unprefixed by default. To support browsers that require prefixes for CSS such as appearance, user-select, or ::placeholder, add the opt-in prefixPlugin at the relevant subtree:
import { StyleSheetManager } from 'styled-components';
import { prefixPlugin } from 'styled-components/plugins';
<StyleSheetManager plugins={[prefixPlugin]}>
<App />
</StyleSheetManager>;The included prefix set targets Chrome 45, Firefox 36, Safari and iOS 9, and Edge 12, matching the browser floor for the JavaScript APIs required by React. For a different set, declare both the prefixed and standard forms yourself, or extend the plugin with one of your own:
import { StyleSheetManager } from 'styled-components';
import { prefixPlugin } from 'styled-components/plugins';
import type { SCPlugin } from 'styled-components/plugins';
const projectPrefixes: SCPlugin = {
name: 'project-prefixes',
decl: (prop, value) =>
prop === 'transform-style'
? [
{ prop: '-webkit-transform-style', value },
{ prop, value },
]
: undefined, // undefined passes the declaration through untouched
};
<StyleSheetManager plugins={[prefixPlugin, projectPrefixes]}>
<App />
</StyleSheetManager>;SCPlugin, DeclResult, DeclTransform, and SelectorTransform are exported only from styled-components/plugins, not the package root. Plugins apply left to right: a later decl runs on every declaration an earlier plugin emitted, so returning undefined on the miss path is what keeps a custom plugin cheap. Skip properties that already start with -, as prefixPlugin does, and plugins can compose in either order without double-prefixing. The full hook contract is documented under authoring custom plugins.
The enableVendorPrefixes prop from v6 has been removed in favor of this plugin.
Define @keyframes once, reference them across components. Names are scoped automatically.
import styled, { keyframes } from 'styled-components';
const rotate = keyframes`
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
`;
const Spinner = styled.div`
animation: ${rotate} 1s linear infinite;
width: 40px;
height: 40px;
border: 3px solid palevioletred;
border-top-color: transparent;
border-radius: 50%;
`;Share design tokens across your app via React context. Every styled component receives props.theme.
import styled, { ThemeProvider } from 'styled-components';
const theme = {
fg: 'palevioletred',
bg: 'white',
};
const Card = styled.div`
background: ${props => props.theme.bg};
color: ${props => props.theme.fg};
padding: 2em;
`;
<ThemeProvider theme={theme}>
<Card>Themed content</Card>
</ThemeProvider>;createTheme turns your tokens into CSS custom properties. Class name hashes stay stable across theme variants, so there's no hydration mismatch when switching light/dark.
import styled, { createTheme, ThemeProvider } from 'styled-components';
const { theme, GlobalStyle: ThemeVars } = createTheme({
colors: {
fg: 'palevioletred',
bg: 'white',
},
space: {
md: '1rem',
},
});
const Card = styled.div`
color: ${theme.colors.fg}; /* var(--sc-colors-fg, palevioletred) */
background: ${theme.colors.bg};
padding: ${theme.space.md};
`;
// Render <ThemeVars /> at the root to emit the CSS variable declarations
// Pass the theme to ThemeProvider for stable hashes
<ThemeProvider theme={theme}>
<ThemeVars />
<Card>Token-driven content</Card>
</ThemeProvider>;Tokens are placeholder references that resolve at render time, not raw values. Interpolate them anywhere a CSS value goes; don't combine them with JS arithmetic. For runtime composition use calc(), or reach for theme.raw.space.md when you genuinely need the original number in JS.
// works
padding: ${theme.space.md};
margin: ${theme.space.sm} ${theme.space.md};
top: calc(${insets.top}px + ${theme.space.md});
// breaks: JS `+` produces a malformed string the browser drops
top: ${insets.top + theme.space.md};See the createTheme API docs for the full composition rules.
Extract reusable style blocks to share across components or apply conditionally.
import styled, { css } from 'styled-components';
const truncate = css`
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
`;
const Label = styled.span`
${truncate}
max-width: 200px;
`;Wrap any component that accepts a className prop.
import styled from 'styled-components';
import { Link } from 'react-router-dom';
const StyledLink = styled(Link)`
color: palevioletred;
text-decoration: none;
&:hover {
text-decoration: underline;
}
`;Inject app-wide CSS like resets and font faces. Supports theming and dynamic updates.
import { createGlobalStyle } from 'styled-components';
const GlobalStyle = createGlobalStyle`
body {
margin: 0;
font-family: system-ui, sans-serif;
}
`;
// Render <GlobalStyle /> at the root of your appSet default or static HTML attributes so consumers don't have to.
const PasswordInput = styled.input.attrs({
type: 'password',
placeholder: 'Enter password',
})`
border: 1px solid #ccc;
padding: 0.5em;
`;The function form receives a second ast argument for bridging declarations or theme tokens into props on third-party components. peek reads a value; pop reads and removes it from the rendered style. Both accept either a CSS property name or a typed dot-separated theme path:
import { Path } from 'react-native-svg';
const Icon = styled(Path).attrs((_props, ast) => ({
fill: ast.pop('color'), // lift the CSS color decl
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. The lift happens at construction time when the callback's behavior is fully determined by static declarations, so renders pay nothing extra.
Contributing guidelines | Code of Conduct | awesome-styled-components
This project exists thanks to all the people who contribute.
Thank you to all our backers! [Become a backer]
Support this project by becoming a sponsor. [Become a sponsor]
This project builds on earlier work by Charlie Somerville, Nik Graf, Sunil Pai, Michael Chan, Andrey Popp, Jed Watson, and Andrey Sitnik. Special thanks to @okonet for the logo.
Licensed under the MIT License, Copyright © 2016-present styled-components contributors. See LICENSE for details.