When I first started using styled-components, it seemed like magic ✨.
Somehow, using an obscure half-string-half-function syntax, the tool was able to take some arbitrary CSS and assign it to a React component, bypassing the CSS selectors we’ve always used.
Like so many devs, I learned how to use styled-components, but without really understanding what was going on under the hood.
Knowing how it works is helpful. You don’t need to understand how cars work in order to drive, but it sure as heck helps when your car breaks down on the side of the road.
Debugging CSS is hard enough on its own without adding in a layer of tooling magic! By demystifying styled-components, we’ll be able to diagnose and fix weird CSS issues with way less frustration.
In this blog post, we’ll pop the hood and learn how it works by building our own mini-clone of ???? styled-components.
wrote a blog post about how they added support for CSS-in-JS libraries like styled-components to the devtools.
Link to this heading
Generating helpers with functional programming
Earlier, we created an h1
factory function to emulate styled.h1
:
This works, but we need many many more helpers! We need buttons and links and footers and asides and marquees.
There’s another problem, too. As we’ll see, styled
can be called as a function directly:
The styled
object is both a function and an object. Bewilderingly, this is a totally valid thing in JavaScript. We can do stuff like this:
Let’s update our styled-components clone to support these new requirements. We can borrow some ideas from functional programming to make it possible.
This code uses a technique known as currying. It allows us to “preload” the Tag
argument.
If you haven’t seen it before, currying can be a bit mindbending. But it’s helpful in this case, as it allows us to easily create many shorthand helper methods:
For more information on currying, check out this lovely blog post by Aphinya Dechalert.
“The styled-components Happy Path”.
Remember earlier, when I said you could think of these two things as equivalent?
When we start supporting interpolations, they stop being equivalent.
Tagged template literals are wild, and it would take an entirely separate blog post to explain how they work and what they’re doing here.
The important thing to know is that all of those little interpolation functions are called when the component is rendered, and used to “fill in the gaps” in our style string.
Here’s a quick sketch:
When we render our component, reconcileStyles
is able to invoke each of those interpolated functions with the data passed through props. In the end, we’re left with a plain ol’ string with populated values.
When the props change, the process is repeated, and a new CSS class is generated.
This blog post has been a journey. Perhaps more than any other post I’ve written, this one covers some pretty treacherous terrain. This stuff is not easy to wrap our mind around.
If parts of this post didn’t make much sense to you, that’s alright! It might require a bit of percolation. As you continue to use styled-components, hopefully the ideas in this post will become clear.
In addition to the practical benefits of understanding your tools, I hope that this post also helps you appreciate what the styled-components team has accomplished. Building and maintaining open-source software is never easy, and it’s especially fraught when working on a CSS-in-JS tool, in a community where the entire category is contentious.
If you found this post helpful, you might be interested in something else I’m working on. It’s called CSS for JavaScript Developers. It’s a comprehensive online course specifically created for JS framework devs who find CSS confusing or frustrating. We learn exactly how CSS works as a language, from first principles, building a robust mental model that can be used to solve just about every layout challenge you encounter.
I’m planning to launch the course in September of this year. You can sign up for updates on the official site!
Special thanks to Phil Pluckthun and Max Stoiber for reviewing this blog post.