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.
Install
npm install styled-components
pnpm / yarn
pnpm add styled-components
yarn add styled-components
Quick examples
Dynamic props
Vary 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>
Extending styles
Build variants on top of existing styled components.
const TomatoButton = styled(Button)`
background: tomato;
color: white;
border-color: tomato;
`;
Polymorphic as prop
Swap the rendered element without changing styles.
// Renders a <a> tag with Button styles
<Button as="a" href="/home">
Link Button
</Button>
Pseudos and nesting
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;
}
`;
Animations
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%;
`;
Theming
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>;
RSC-compatible themes
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.
Shared styles with css
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;
`;
Styling third-party components
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;
}
`;
Global styles
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 app
Attrs
Set 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.
Documentation
Community
Contributing guidelines | Code of Conduct | awesome-styled-components
Contributors
This project exists thanks to all the people who contribute.
Backers
Thank you to all our backers! [Become a backer]
Sponsors
Support this project by becoming a sponsor. [Become a sponsor]
Acknowledgements
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.
License
Licensed under the MIT License, Copyright © 2016-present styled-components contributors. See LICENSE for details.