Paul is a Senior Software Engineer, Independent Developer Advocate and Technical Writer. More from Paul can be found on his site, paulie.dev.
Read more from Paul Scanlon
With React’s evolving ecosystem, particularly with Next.js shifting toward a “server-first” approach, applications using CSS-in-JS solutions like Emotion and styled-components are facing significant challenges. These CSS-in-JS libraries are inherently incompatible with the new React paradigm.
While one could argue these libraries could be updated to align with the new server-first direction, it’s important to remember that they’re maintained by unpaid open source contributors. Expecting them to completely overhaul their libraries to accommodate changes in React is a daunting and potentially unrealistic task.
Let’s look at why some CSS-in-JS solutions are unable to work in the new server-first world, then I’ll introduce you to one alternative, Linaria, which uses an almost identical API as styled-components.
This is going to turn into a long-winded explanation, but it’s important to understand how React used to work (pre-React Server Components) and how it works now (post-RSC).
So let’s start at the beginning. Pre-RSC, React rendered a “component” on both the server and the client. React has always rendered the static parts of the HTML page on the server and sent that to the client to be “hydrated.” The hydration phase is client-side JavaScript that’s responsible for dealing with refs or attaching event handlers, which typically allow for interactivity.
If it feels a bit dumb to do this work twice — once on the server, then replay it again on the client — that’s because it kinda is. I’ve discussed this before by comparing React’s hydration with Qwik’s resumable approach, which for me makes much more sense.
However, in a post-RSC world, all React components by default are server-only. React renders the static parts of the HTML page, but doesn’t hydrate them on the client. This reduces/eliminates any client-side JavaScript. This is an interesting approach because in a post-RSC world, we’re able to make server-side requests from any component, at any level in the tree, not just at the route level, which is how Next.js previously worked.
In a post-RSC world you can still enable client-side JavaScript and React specific methods such as useState
, useEffect
, etc., but you have to be explicit and add the use client
directive to the top of the file so React will behave in the same way it always has.
To add a little more context, it’s important to understand how, for want of a better word, legacy CSS-in-JS libraries work before you can understand why they don’t work in a post-RSC world.
Libraries like Emotion and styled-components do a lot of the heavy lifting at runtime, on the client using client-side JavaScript, which we’ve established no longer exists in RSCs, or “server-only” components.
You’ve probably seen this in effect. If you inspect any app or site that uses e.g. styled-components, you’ll have noticed the weird-looking class names.
This is because with styled-components, you, the developer, don’t write CSS classes, you write components — and styled-components (the library) deals with extracting the styles, converting them to CSS that the browser can understand and generating a random but unique class name, then applying it to a DOM element to link the two together.
For example, here’s how you might define a HTML anchor element using styled-components for use in a React component.
When styled-components runs, it’ll create the class name (as seen above), add it to the DOM element, and convert the CSS values as defined when the styled.a
was created into CSS the browser can understand, such as.:
But as mentioned, this all happens at runtime, on the client, using client-side JavaScript.
Sadly then, because RSCs are designed to be rendered on the server and then sent as HTML to the client, they have specific characteristics and limitations — particularly regarding client-side JavaScript that prevents libraries like styled-components from working.
There are a number of newer solutions to this problem that shift the heavy lifting from the client to the build. We already do a lot of heavy lifting here, converting TypeScript to “browser Js,” so doing CSS-in-JS stuff at this point seems like a great solution for the post-RSC world.
However, you may wonder: Won’t that mean developers the world over would need to rewrite all their styled.
declarations using some new fancy API? Because that would be a nightmare!
Thankfully, the creators of Linaria, which has actually been around since 2017, have opted to use a very similar API to that of styled-components. Pigment CSS from the MUI team has followed a similar pattern too, which is great news.
In the case of Linaria, it leverages CSS modules. If I use the styled.a
example again:
Instead of this turning into a random but unique class name, Linaria creates a [component name].module.css
file and converts the styled declaration into a real CSS named class, for instance:
During the build step of your application, an import to the CSS modules .css
file will be added to the component, and the styled declaration is converted into the underlying HTML element (in this case it’s an anchor element), and a reference to the class is added:
Since this all happens during the build step, there’s nothing for the browser to do — and thus, no client-side JavaScript required. It’s actually a pretty cool solution to the problem and all that’s required to migrate would be to uninstall styled-components, install Linaria, and perform a find and replace from import styled from 'styled-components';
to import styled from '@linaria/react';
.
So there you have it, when something as widely used as React completely changes the way it fundamentally works, many open source projects designed for it will need to change too — or, in some unfortunate cases, be retired to the history books. In JavaScript-land everything changes frequently, and all we can do is try to keep up!
That said, the dedication by the community to react (pun intended) and evolve when changes occur is incredible, and I’m glad I’m part of this crazy community. There’s never a dull moment!