20 March 2018 (updated: 2 December 2024)
Chapters
Learn alternative way to style React applications in JS.
UPDATED (20.03.2018)
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.
At first, I was sceptical of this idea. In general, I am not comfortable with writing styles in JavaScript. But hey, when is the right time to try out new things?
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.
Let’s assume you are a CSS beginner and you heard about this new, popular library called “styled-components”. After a quick research, you will find out that they are yet another approach to writing styles with CSS, in JavaScript. Let’s start off by saying that this library was created with React in mind, but the idea is quite reusable and was already implemented for vue.js, React Native, and even for traditional DOM. These are not as popular as the initial implementation for React. Having that out of the way, we can look at the gist of what styled-components are and the three biggest problems, that they try to solve.
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.
If you want to learn more about Tagged Template Literals, check out MDN documentation or this article about the magic behind styled-components, that was written by one of the creators of this library, Max Stoiber.
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.
CSS modules allow us to import our CSS or SCSS files into JavaScript, and refer to our class names as keys in an imported object. It may sound complicated, but behind the scenes it all boils down to concatenating our simple class names, like .button
, with some random strings, like .button_sRgs42
. Thanks to that we can benefit from local scoping. Styled-components also generate random strings for class names. What’s powerful is that they do it behind the scenes, without any need for importing stylesheets and such. It leads us to the second problem.
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.
Because we are writing our styles in JavaScript, we can dynamically change them. So instead of creating two different classes for our .default
and .disabled
button, we can simply use a ternary operator in our styles, which will look something like: color: ${disabled ? 'grey' : 'black'};
. Where ${}
is yet another ES6 feature, called “string interpolation”. If it’s new to you, you can read more about it on MDN.
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 button
element. 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 button
component 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.
insertRule
API.After using styled-components for about a month, I can say that I’m not yet convinced about their superiority over creating actual stylesheets. However, I can definitely see a huge potential here. Even if using them on the web still feels a bit experimental, I had the greatest experience, while working with them in a React Native project. They work way better than writing CSS as JavaScript objects.
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.
21 November 2024 • Mariusz Heyda