styled-components is the result of wondering how we could enhance CSS for styling React component systems. By focusing on a single use case we managed to optimize the experience for developers as well as the output for end users.
Apart from the improved experience for developers, styled-components provides:
You get all of these benefits while still writing the CSS you know and love, just bound to individual components.
Installing styled-components only takes a single command and you're ready to roll:
# with npm
npm install styled-components
# with yarn
yarn add styled-components
If you use a package manager like yarn that supports the "resolutions" package.json field, we also highly recommend you add an entry to it as well corresponding to the major version range. This helps avoid an entire class of problems that arise from multiple versions of styled-components being installed in your project.
In package.json
:
{ "resolutions": { "styled-components": "^5" } }
It's highly recommended (but not required) to also use the Babel plugin. It offers many benefits like more legible class names, server-side rendering compatibility, smaller bundles, and more.
If you're not using a module bundler or package manager we also have a global ("UMD") build hosted on the unpkg CDN. Simply add the following <script>
tag to the bottom of your HTML file:
<script src="https://unpkg.com/styled-components/dist/styled-components.min.js"></script>
Once you've added styled-components
you will have access to the global window.styled
variable.
const Component = window.styled.div` color: red; `
This style of usage requires the react CDN bundles and the react-is
CDN bundle to be on the page as well (before the styled-components script.)
styled-components
utilises tagged template literals to style your components.
It removes the mapping between components and styles. This means that when you're defining your styles, you're actually creating a normal React component, that has your styles attached to it.
This example creates two simple components, a wrapper and a title, with some styles attached to it:
Hello World!
This is a live editor, so play around with the code to get a feel for what it's like to work with styled-components!
The CSS rules are automatically vendor prefixed, styled-components takes care of that for you!
Styled components uses stylis.js package under the hood for prefixing the css rules.
For additional information about the supported prefixes in stylis.js visit their repository.
You can pass a function ("interpolations") to a styled component's template literal to adapt it based on its props.
This button component has a primary state that changes its color. When setting the $primary
prop to true, we are swapping out its background and text color.
Quite frequently you might want to use a component, but change it slightly for a single case. Now, you could pass in an interpolated function and change them based on some props, but that's quite a lot of effort for overriding the styles once.
To easily make a new component that inherits the styling of another, just wrap it in the styled()
constructor. Here we use the button from the last section and create a special one, extending it with some color-related styling:
We can see that the new TomatoButton
still resembles Button
, while we have only added two new rules.
In some cases you might want to change which tag or component a styled component renders. This is common when building a navigation bar for example, where there are a mix of anchor links and buttons but they should be styled identically.
For this situation, we have an escape hatch. You can use the "as" polymorphic prop
to dynamically swap out the element that receives the styles you wrote:
This works perfectly fine with custom components too!
The styled
method works perfectly on all of your own or any third-party component, as long as they attach the passed className
prop to a DOM element.
If you are using react-native
keep in mind to use style
instead of className
.
You can also pass tag names into the styled()
factory call, like so: styled("div")
. In fact, the styled.tagname
helpers are just aliases that do the same.
If the styled target is a simple element (e.g. styled.div
), styled-components passes through any known HTML attribute to the DOM. If it is a custom React component (e.g. styled(MyComponent)
), styled-components passes through all props.
This example shows how all props of the Input component are passed on to the DOM node that is mounted, as with React elements.
Note how the $inputColor
prop is not passed to the DOM, but type
and defaultValue
are? The styled
function is smart enough to filter non-standard attributes automatically for you.
If you're familiar with importing CSS into your components (e.g. like CSSModules) you'll be used to doing something like this:
import React from 'react'; import styles from './styles.css'; export default class Counter extends React.Component { state = { count: 0 }; increment = () => this.setState({ count: this.state.count + 1 }); decrement = () => this.setState({ count: this.state.count - 1 }); render() { return ( <div className={styles.counter}> <p className={styles.paragraph}>{this.state.count}</p> <button className={styles.button} onClick={this.increment}> + </button> <button className={styles.button} onClick={this.decrement}> - </button> </div> ); } }
Because a Styled Component is the combination of the element and the rules that style it, we'd write Counter
like this:
import React from 'react'; import styled from 'styled-components'; const StyledCounter = styled.div` /* ... */ `; const Paragraph = styled.p` /* ... */ `; const Button = styled.button` /* ... */ `; export default class Counter extends React.Component { state = { count: 0 }; increment = () => this.setState({ count: this.state.count + 1 }); decrement = () => this.setState({ count: this.state.count - 1 }); render() { return ( <StyledCounter> <Paragraph>{this.state.count}</Paragraph> <Button onClick={this.increment}>+</Button> <Button onClick={this.decrement}>-</Button> </StyledCounter> ); } }
Note that we added a "Styled" prefix to StyledCounter
so that the React component Counter
and the Styled Component StyledCounter
don't clash names but remain easily identifiable in the React Developer Tools and Web Inspector.
It is important to define your styled components outside of the render method, otherwise it will be recreated on every single render pass. Defining a styled component within the render method will thwart caching and drastically slow down rendering speed, and should be avoided.
Write your styled components the recommended way:
const StyledWrapper = styled.div` /* ... */ `; const Wrapper = ({ message }) => { return <StyledWrapper>{message}</StyledWrapper>; };
Instead of:
const Wrapper = ({ message }) => { // WARNING: THIS IS VERY VERY BAD AND SLOW, DO NOT DO THIS!!! const StyledWrapper = styled.div` /* ... */ `; return <StyledWrapper>{message}</StyledWrapper>; };
Recommended reading: Talia Marcassa wrote a great review of real-world usage, featuring lots of solid practical insights and comparisons with alternatives, in Styled Components: To Use or Not to Use?
The preprocessor we use, stylis, supports scss-like syntax for automatically nesting styles.
Through this preprocessing, styled-components supports some advanced selector patterns:
&
a single ampersand refers to all instances of the component; it is used for applying broad overrides:
Hello world!How ya doing?The sun is shining...Pretty nice day today.Don't you think?Splendid.
&&
a double ampersand refers to an instance of the component; this is useful if you're doing conditional styling overrides and don't want a style to apply to all instances of a particular component:
&&
a double ampersand alone has a special behavior called a "precedence boost"; this can be useful if you are dealing with a mixed styled-components and vanilla CSS environment where there might be conflicting styles:
I'm blue, da ba dee da ba daa
If you put selectors in without the ampersand, they will refer to children of the component.
To avoid unnecessary wrappers that just pass on some props to the rendered component or element, you can use the .attrs
constructor. It allows you to attach additional props (or "attributes") to a component.
This way you can for example attach static props to an element or pass a third-party prop like activeClassName
to React Router's Link component. Furthermore, you can also attach more dynamic props to a component. The .attrs
object also takes functions, that receive the props that the component receives. The return value will be merged into the resulting props as well.
Here we render an Input
component and attach some dynamic and static attributes to it:
As you can see, we get access to our newly created props in the interpolations, and the type
attribute is passed down to the element.
Notice that when wrapping styled components, .attrs
are applied from the innermost styled component to the outermost styled component.
This allows each wrapper to override nested uses of .attrs
, similarly to how CSS properties defined later in a stylesheet override previous declarations.
Input
's .attrs
are applied first, and then PasswordInput
's .attrs
:
This is why PasswordInput
is of a 'password'
type, but still uses the $size
attribute from Input
.
CSS animations with @keyframes
aren't scoped to a single component but you still don't want them to be global to avoid name collisions. This is why we export a keyframes
helper which will generate a unique instance that you can use throughout your app:
< 💅🏾 >
Keyframes are not supported by react-native
. Instead, use the ReactNative.Animated
API.
Keyframes are lazily injected when they're used, which is how they can be code-split, so you have to use the css
helper for shared style fragments:
const rotate = keyframes`` // ❌ This will throw an error! const styles = ` animation: ${rotate} 2s linear infinite; ` // ✅ This will work as intended const styles = css` animation: ${rotate} 2s linear infinite; `
This used to work in v3 and below where we didn't code-split keyframes. If you're upgrading from v3, make sure that all your shared style fragments are using the css
helper!
styled-components can be used with React Native in the same way and with the same import. Try this example with Snack by Expo.
import React from 'react' import styled from 'styled-components/native' const StyledView = styled.View` background-color: papayawhip; ` const StyledText = styled.Text` color: #BF4F74; ` class MyReactNativeComponent extends React.Component { render() { return ( <StyledView> <StyledText>Hello World!</StyledText> </StyledView> ) } }
We also support more complex styles (like transform
), which would normally
be an array, and shorthands (e.g. for margin
) thanks to
css-to-react-native
!
Note that the flex
property works like CSS shorthand, and not the legacy
flex
property in React Native. Setting flex: 1
sets flexShrink
to 1
in addition to setting flexGrow
to 1
and flexBasis
to 0
.
Imagine how you'd write the property in React Native, guess how you'd transfer it to CSS, and you're probably right:
const RotatedBox = styled.View` transform: rotate(90deg); text-shadow-offset: 10px 5px; font-variant: small-caps; margin: 5px 7px 2px; `
Some of the differences to the web-version are, that you cannot use the
keyframes
and createGlobalStyle
helpers since React Native doesn't support
keyframes or global styles. We will also warn you if you use media queries or
nest your CSS.
In v2 we support percentages. To make this possible we need to enforce units for all shorthands. If you're migrating to v2, a codemod is available.
If you'd prefer to just import styled-components
instead of styled-components/native
, you can add a resolverMainFields
configuration that includes "react-native"
. This used to be supported in metro by default (and currently does work in haul) but appears to have been removed at some point.