CSS-in-JS essentials
June 25, 2019 • 5 min read
Pitfalls of CSS and common solutions
CSS is not easy, contrary to the popular belief. It was designed many years ago for styling single document that served only presentation purposes. Back then we didn't have highly interactive web apps. We didn't think in terms of the structure of many isolated components interacting with each other. The web was small and simple. Now we have web apps that are being created by teams of many developers working on big codebases. In the modern age, CSS is still the only way to style our app.
In the big project comprised of many developers, CSS might become a mystery at some point. The web of inner connected codependent styles makes us afraid of changing or removing anything.
CSS shortcomings:
- global scope
- difficulty in representing the component state in CSS
- no shared variables between JS an CSS
- specificity problems
- hard to achieve dead code elimination
- single selector might refer to many elements
- a single element might have classes targeted by many selectors
- order of selectors might matters (problems with non-deterministic styling)
- selectors have access to the entire document
What is CSS-in-JS and what it isn't
CSS-in-JS is not a particular library. It's about solving issues connected with CSS by using JavaScript for writing styles. There are many approaches to the implementation of CSS-in-JS. In the span of the last two years, we saw the birth of many libraries, each addressing a different subset of problems in different ways. All of them share fundamental rule: they use API instead of conventions and they use JavaScript for writing styles.
Wait. Styling in JavaScript? Isn't it an anti-pattern?
No. Writing styles in JS does not imply creating inline styles.
1styles in JavaScript !== inline styles
Popularity of CSS-in-JS libraries, compatibility with different frameworks
Many libraries were created in a recent couple of years. They differ in many areas. There is a great repo that compares most major CSS-in-JS libraries out there, it's worth to take a look https://github.com/MicheleBertoli/css-in-js.
Currently, the most popular library for CSS-in-JS is styled-components. With over 24k stars on GitHub and 240 contributors, it's the go-to library when starting with CSS-in-JS. I will concentrate mostly on styled-components in this article, as it's the most popular and most stable at the moment. Here are some honorable mentions:
- Emotion - biggest rival of styled-components, also very popular
- Linaria - inserts styles during a build instead of at runtime
How styled-components works under the hood
Let's discuss how exactly styled-components translates javascript into css styles.
In short: at runtime your styled components will be used to generate set of css classes and blocks of css code. Then this code and classes are inserted into the DOM to create a valid CSS that can be processed by the browser. So basically styled-components runs right when your app starts and inserts styles for components that are about to be rendered.
1. App startup
Styled-components has an internal counter that counts how many components were created. Every styled component gets its unique id that is based on this counter.
1componentID = `sc-${createHash('sc', counter)}`
Right in the beginning, all components ids are added to the style tag in the head of the document.
1<style data-styled-component>
2 /* sc-component-id: sc-fd5dreQ */
3</style>
2. Evaluate tagged template literal
Let's say that our app started and now React wants to render some styled component. The first thing that happens is a translation from a tagged template literal to the string that is evaluated based on interpolations from the tagged template.
So we go from this:
1const Description = styled.p`
2 font-size: 16px;
3 color: ${({ primary }) => primary ? 'blue' : 'white'};
4
5 &:hover {
6 border: 1px solid red;
7 }
8`;
To this:
1font-size: 16px;
2color: red;
3&:hover {
4 border: 1px solid red;
5}
This is not a valid CSS code yet, but we're getting close.
3. Generate CSS class and preprocess styles
To create a valid CSS code for our styled component, we need a class name. The class name is generated based on unique component id (that I described earlier) and evaluated tagged template. This is an important part: both component id and evaluated template have an impact on the class name (this will be important later on).
1className = crateClassName(componentId, evaluatedTaggedTemplate)
Once we have a class name, we can preprocess our evaluated string. Styled-components use stylis preprocessor as for the moment of writing this article.
1.gdRt46sC {
2 font-size: 16px;
3 color: red;
4}
5.gdRt46sC:hover {
6 border: 1px solid red;
7}
4. Inject styles and class into the DOM
Finally, we have a valid CSS code and a proper class for our component. At this point styled-components adds class to the proper element:
1<p class="sc-fd5dreQ gdRt46sC">I am some description</p>
Site note: apart from class styled-components also injects component id. This is because SC allows referring to other styled components in our selectors.
The CSS code is injected into the style tag in the head, right under comment with the component id:
1<style data-styled-component>
2 /* sc-component-id: sc-fd5dreQ */
3 .gdRt46sC {font-size: 16px;color: red;}
4 .gdRt46sC:hover {border: 1px solid red;}
5</style>
Pitfalls and future
CSS-in-JS solves a lot of problems connected with CSS but like everything also has its tradeoffs. Honestly, it was hard to come up with a list, but here are some things that came to my mind:
- New dependencies
- Longer onboarding time for new developers as it requires to learn a new approach to styling
- Bigger JS bundle due to styles
- In most libraries styles are added at runtime
- styled-components and emotion are fairly new so there is no concrete data how well do they scale in very big projects
Future of CSS-in-JS
CSS-in-JS is very popular among the frontend community.
I think that this trend will continue and libraries like styled-components will be adopted by a large number of projects. They come with a set of tradeoffs but at the end of the day, they solve more issues than any other approach.
Styled-components is a mature library with large community support, used by some big companies e.g. InVision, Atlassian, TypeForm, Vimeo, Tinder. I went all the way from just using SASS, through BEM and CSS-modules to styled-components and can't image going back to the old ways. It changes how you look at styling React components.
It's worth to give it a try.
All posts
Personal blog by Bartek Józwowiak. I write mostly about new frontend tools and web development related stuff.