Learn alternative way to style React applications in JS.
Recently, I was starting a new project and the time came, when I had to decide on my approach to styling. Of course, I could go with traditional BEM, but I was leaning towards CSS modules, since they go well with React. I was just about to start, when my co-worker asked me if I had considered using styled-components.
Styled-components are quite popular on github (10.8k, at the time of writing) and have already been used in production. Without any further consideration, I decided to give it a try.
What are Styled-Components?
Creating the First Component
Since styled-components are generated on runtime, we don’t have to modify our bundler config. Wohoo! All you need to do to start using them in your project is running npm install styled-components. Then, import them into your component. Let’s take a look at a real-life example:
Here, we created a component that renders HeaderText, which is a styled h1 element. If we pass large prop, the button will be rendered with bigger font-size. Normally, let’s say in BEM, we would have to create two classes and apply them conditionally.
Let’s take a look at the same thing, but using traditional approach:
The second example is, obviously, more complex. We have to remember about passing className from props, as well as all the other props, like onClick or other events.
The whole functionality of styled-components is backed on, rather unpopular and little known, ES6 feature, called Tagged Template Literals. To show how they work, we are going to create a simple function log. It will log all passed arguments to the console and use them in two different scenarios.
If we invoke it as a standard function, it will simply put out all the arguments passed into it, one by one. Although, if we invoke this function using tagged template literals, we are going to get an array of all strings from template literal as the first argument; followed by variables or functions that were passed between interpolation brackets.
In the past (which is not that long ago, but come on, this is JS…) you had to use one of these more popular methodologies to keep your styles clean and reusable. BEM was really popular, but we also had OOCSS, SMACSS, or even Atomic CSS. They actually still are relevant and extremely useful, but there are new kids on the block that solve the issue of scoping styles in another manner.
Connecting Styles with Components
With styled-components, when you want to style an element, you create a new one first and then apply styles to it. The library automatically creates a random string and applies the generated class name to our React , vue.js, DOM, or other component.
Dynamic Style Changes
Behind the Scenes
Let’s look into some inner workings of styled-components, to better understand what are we actually using. Let’s do a step-by-step walkthrough of what happens when you create a styled component.
First, we specify that we want to create a component from a buttonelement. Styled-components create a new React Component. When componentWillMount function is invoked, it checks if component will change dynamically based on props. If not, the engine doesn’t try to re-generate those styles. Similar thing happens in componentWillReceiveProps. If it’s dynamic, the engine re-calculates styles by invoking setState function, in which the needed calculations are made. When componentWillUnmount is invoked, the engine simply removes any listeners for changes, so that it doesn’t re-calculate any unused styles.
When we render the component, the engine takes the array of strings and all the arguments returned from tagged template literal. Based on buttoncomponent from the above example, arguments look like this:
As we saw earlier, the first argument is an array of all strings between curly interpolation brackets and the following arguments (in above example there is only one) are variables/functions passed in between interpolation brackets.
Then the component checks if any of the arguments is a function. If so, it applies this function to a string from the array with correct id (first function is applied to string with index 0). Next, it passes the component props into this function as arguments. It appends the result of the function onto a chosen string. At the end, it joins all the strings and injects them into our page head tag.
This description is really simplified, but it shows the main mechanism that stands behind styled-components. It allows to write actual CSS and supports 100% of its features, which hadn’t been done before (or just wasn’t as popular).
Below, I prepared a small demo that aims to mimic the behaviour of styled-components (without actually injecting the styles; I only generate CSS string).
From version 3.1.0, released at the end of January 2018, styled-components have started using lesser known insertRule API in production. This small change allows to shave off hundreds of milliseconds from Time-To-First-Interactive in most applications, and in library stress-tests (not a ‘real world’ implementation) it resulted in 10x-20x speed increase. Removal of that bottle-neck makes styled-components more appealing to use also in larger applications, not only in side-projects.
There are a few tools that were designed to help you while using styled-components. The most useful by far is the babel plugin. It allows to specify some configuration, that really helps with development, such as server side rendering and using pretty class names (really helps with debugging). Thanks to that we can also specify preprocessing and minification options.
Styled-components are not perfect (yet). I encountered many problems, some small, some larger. Some of them probably will be solved soon, since this project is being actively developed by many contributors. These are the things, that you should know, before starting a project that is using styled-components.
There are some performance problems. Lack of understanding of how they work can lead to unnecessary re-rendering of components and poor animation performance. This was partially solved by recent introduction of insertRule API.
If anything goes wrong with styled-components, the whole app crashes.
They cannot be extracted into a static CSS file.
Linting is buggy, e.g. it has problems with linting whitespace, if there are any rules that specify the amout of it, when using conditional blocks of styles.
Tight coupling with react. It’s hard to migrate from s currently used framework or reuse an existing component in another project.
It’s still a new technology with not so many well-defined methodologies and approaches.
Despite any flaws, I think styled-components are an amazing piece of work and another great step in the evolution of modern CSS. Hat’s off to Glen Maddern and Max Stoiber, who are the creators of this library.