styled-components – styled-components
Fast, expressive styling for React. Server components, client components, streaming SSR, React Native—one API.
关键指标一览
主题标签
README 详细介绍
Fast, expressive styling for React.
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;
}
`;
Vendor prefixes
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>;
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.
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.
