Styled Components
By Flavio Copes
Learn Styled Components 6: write CSS-in-JS scoped to a single React component, style it with transient props, extend components, and add vendor prefixes.
- A brief history
- Introducing Styled Components
- Installation
- Your first styled component
- Using props to customize components
- Extending an existing Styled Component
- It’s Regular CSS
- Vendor prefixes
A brief history
Once upon a time, the Web was really simple and CSS didn’t even exist. We laid out pages using tables and frames. Good times.
Then CSS came to life. After some time it became clear that frameworks could help a lot, especially with grids and layouts. Bootstrap and Foundation played a big part in this.
Preprocessors like Sass helped slow down the adoption of frameworks, and helped organize the code. Conventions like BEM and SMACSS grew in use, especially within teams.
Conventions are not a solution to everything, and they are hard to remember. As JavaScript and build processes made their way into every frontend project, CSS found its way into JavaScript too. We call it CSS-in-JS.
New tools explored new ways of doing CSS-in-JS, and a few of them got popular: React Style, jsxstyle, Radium and more.
Today you have more options for the same problem, keeping styles scoped to a component. CSS Modules, utility classes with Tailwind, and libraries that extract CSS at build time, like StyleX, all do it. Styled Components does it in JavaScript, and it generates the CSS while your app runs.
Introducing Styled Components
One of the most popular CSS-in-JS tools for React is Styled Components.
It’s meant to be a successor to CSS Modules (more on CSS Modules here and here).
Both let you write CSS that’s scoped to a single component, so it doesn’t leak to any other element in the page. CSS Modules keep that CSS in separate .css files, while Styled Components puts it right inside your component file.
You write plain CSS in your components, and you don’t have to worry about class name collisions.
Installation
Install styled-components using npm:
npm install styled-components
This installs version 6, the current major release. It works with React 16.8 or later, the version that introduced hooks.
It also ships its own TypeScript types, so you don’t need @types/styled-components. If an older project still has it, remove it.
Now add this import:
import styled from 'styled-components'
Your first styled component
With the styled object imported, you can start creating Styled Components. Here’s the first one:
const Button = styled.button`
font-size: 1.5em;
background-color: black;
color: white;
`
Button is now a React Component in all its greatness.
We created it with a function of the styled object, called button in this case, passing some CSS properties in a template literal.
You can use this component like any other React component:
export default function App() {
return <Button>Save</Button>
}
Styled Components offers functions to create other components too, not only button: section, h1, input and many others.
The syntax with the backtick might look weird at first. It’s called Tagged Templates, it’s plain JavaScript, and it’s a way to pass an argument to the function.
Using props to customize components
When you pass props to a Styled Component, it passes them down to the DOM node it renders.
For example, here’s how we pass the placeholder and type props to an input component:
const Input = styled.input`
font-size: 1em;
padding: 0.5em;
`
export default function App() {
return (
<div>
<Input placeholder="Your email" type="email" />
</div>
)
}
This will do what you think, inserting those props as HTML attributes.
Props can also customize the style of a component, based on their value.
Here we want a primary prop that changes the colors of a button. It exists only for styling, so we don’t want it to end up in the HTML. That’s why we call it $primary, with a dollar sign in front:
const Button = styled.button`
background: ${props => (props.$primary ? 'black' : 'white')};
color: ${props => (props.$primary ? 'white' : 'black')};
`
export default function App() {
return (
<div>
<Button>A normal button</Button>
<Button>A normal button</Button>
<Button $primary>The primary button</Button>
</div>
)
}
Setting the $primary prop changes the color of the button.
A prop that starts with $ is a transient prop. Styled Components uses it to build the CSS and then drops it, so it never reaches the <button>.
Without the $, the prop goes straight to the DOM element. Write primary instead of $primary and you’ll see this warning in the console:
styled-components: it looks like an unknown prop "primary" is being sent through to the DOM, which will likely trigger a React console error.
React then logs its own error about a non-boolean attribute. Version 5 filtered out unknown props for you, but version 6 doesn’t, so use the $ prefix.
If you can’t rename a prop, for example in a big codebase you’re upgrading from version 5, use shouldForwardProp. It’s a function that decides which props reach the DOM:
const Button = styled.button.withConfig({
shouldForwardProp: prop => prop !== 'primary',
})`
color: ${props => (props.primary ? 'white' : 'black')};
`
You can also set it once for the whole app with the StyleSheetManager component. The styled-components FAQ shows how to restore the version 5 behavior that way, using the @emotion/is-prop-valid package.
Extending an existing Styled Component
If you have one component and you want to create a similar one, styled slightly differently, pass it to styled():
const Button = styled.button`
color: black;
font-size: 1.5em;
`
const WhiteButton = styled(Button)`
color: white;
`
export default function App() {
return (
<div>
<Button>A black button, like all buttons</Button>
<WhiteButton>A white button</WhiteButton>
</div>
)
}
WhiteButton keeps all the styles of Button and overrides color.
Older tutorials, including an earlier version of this one, used Button.extend. That method doesn’t exist anymore, so use styled(Button).
styled() works with your own components too, as long as they pass the className prop down to a DOM element.
It’s Regular CSS
In Styled Components, you can use the CSS you already know and love. It’s plain CSS. It’s not pseudo CSS, nor inline CSS with its limitations.
You can use media queries, nesting and anything else you might need.
Here’s an example of a media query:
const Button = styled.button`
color: green;
@media screen and (max-width: 800px) {
color: black;
}
`
When you nest a pseudo-class like :hover, start it with &:
const Button = styled.button`
color: green;
&:hover {
color: black;
}
`
The & stands for the component itself. If you leave it out, version 6 reads :hover as a descendant selector, and the rule applies to the elements inside the button instead of the button.
Vendor prefixes
Version 6 doesn’t add vendor prefixes like -webkit- by default. Modern browsers rarely need them, and leaving them out means less CSS on the page.
If you need them, wrap your app in StyleSheetManager to turn them back on:
import { StyleSheetManager } from 'styled-components'
export default function Root() {
return (
<StyleSheetManager enableVendorPrefixes>
<App />
</StyleSheetManager>
)
}
Now a rule like user-select: none also outputs -webkit-user-select, -moz-user-select and -ms-user-select.
Want me to talk about your product? You can sponsor this site.