React has been around for a while, and it is currently the leading frontend framework (ok, library). It somehow manages to stay ahead of the pack despite tough competition. The React community is massive and that in itself is a good enough reason to expect a framework to stay popular as the adoption curve cycle slowly plays out. This sort of drift, however, is definitely not what’s happening with React. In fact, React stays popular because it keeps reinventing itself in the attempt to answer the changing needs of UI engineering (and, for the most part, does so gracefully, without compromising backwards compatibility).
This article looks at React's latest attempt to chart a new course, this time by laying out a bold vision around extending the component ecosystem into the server with React Server Components. The technology is not yet fully production ready, but you can already test early implementations offered by Next.js (and Remix might soon follow suit), and so it is probably mature enough that decision makers in companies using React (or considering using React) should start paying attention.
First let’s understand the challenges the React team is trying to address and then look at potential adoption challenges and how the way we build and structure teams of React developers going forwards would change should the vision be brought to fruition.
Why is React evolving towards the server side?
It may come as a surprise if you’ve worked with react for years, but the React team now recommends to start new React projects through fullstack React metaframeworks:
The emphasis on using a framework is such that they felt it necessary to preempt the question of whether React is still its own thing.
This is quite a shift, considering that for the longest time the recommended solution was to use CreateReactApp - a tool built by the react team which spins up a React SPA (i.e. a client side only app). What explains that change? In short, React Server Components. Answering this question in a bit more detail is still rather challenging though, because – while the authors know the answer very well – they’re still figuring out the best way to communicate this to the outside world in a way that would capture people’s imagination. Luckily, Dan Abramov from the React core team has lately given us a sneak peak of their thinking by testing some pitch ideas for React Server Components on his private twitter account.
You can read the whole thread here:
But here’s my top 3.
Best pitch for Server Side Components: 3rd place
This one, Dan readily agrees, is a little hyperbolic - but then most good sales pitches are. It gets third place because it’s rather inaccurate, but it does communicate the promise of having to think about getting data as part of architecting your UI much less often.
Currently the data fetching pattern the react team has converged on is all about favoring collocation of components and data fetching, i.e. each component knows what data it needs and fetches it as required. There are many benefits of this approach, full composability being probably the largest. The drawback is that, since all that fetching happens on the client, each component triggers its fetching logic only once it gets rendered, which depends on the ancestors having been rendered and having fetched their data etc. - a waterfall of queries ensues, and the need to cache and dedupe requests on the client presents itself very quickly as the app grows.
In Meta (React’s mother company) that is handled by the Relay framework, which facilitates stitching together a top level GraphQL query based on data dependencies declared locally by every component. It then takes care of optimizing a single top level query, which includes deduplication (the same data requested by multiple components gets requested once). Relay is a very powerful solution, check out this talk by Robert Balicki from the React Data Team to learn more: https://www.youtube.com/watch?v=lhVGdErZuN4
While RSC and Relay are very different things and might be used in conjunction, they do have an overlap when it comes to solving request orchestration and deduplication, i.e. in both cases developers worry less about optimizing data fetching.
Here’s another tweet from Dan, which sadly didn’t make it to the top three: “read data on the server, pass it down by props”. This means developers would essentially be able to “think in components” also on the server. With React Server Component you can simply await a promise inside a (server) react component instead of fetching from the browser.
The best part is that you’re not limited to treating your server components as “all knowing parents” responsible for getting all the data children need (which is what route level fetching in Multiple Page Applications is). Instead, you can be as granular as you want. You might choose to place a server component at the bottom of the React tree even if it has client rendered ancestors. Hopefully this illustrates why React developers in the future might worry less about using APIs and instead focus on the best ways to combine components, each of which would handle their own fetching concerns, with the actual fetching benefiting from some optimizations higher up.
Best pitch for Server Side Components: 2st place
This one is the key to understanding why building server first React components sits well specifically with React. The concept of facilitating the component based UI architecture paradigm both on the frontend and the backend is not limited to React but React’s unidirectional data flow is especially well suited to play well with such a setup.
React treats the UI primitives like they are just values. You can keep them in variables, pass them around and compose them into larger groupings that behave predictably precisely because data flows through them only one way.
Subsections of the component tree get selectively rerendered as required in order to reflect changes in data. It is therefore important to control at which level of the virtual dom tree the data changes are detected and reacted to. React developers know this problem as “lifting state up”, i.e. moving a piece of data as high up as necessary (closest common ancestor), but not higher (to avoid unnecessary rerenders).
Now thanks to React Server Components developers can design their state architecture the same way but they can now decouple the problem of component structure from the challenges of sourcing the data in a streamlined fashion.
With RSC, what arrives in the browser is a serialized json describing the react tree with the server side components already broken down into base html tags containing the results of any data crunching that happened on the server (it also contains elements acting as placeholders for client side rendering). There is no rerender triggered by the fetched data suddenly arriving somewhere higher up in the tree. In this sense, the server data orchestration serves as the top level of the unidirectional data flow, even if the components themselves are deeply nested in the react tree structure and have client rendered ancestors. Powerful stuff.
Best pitch for Server Side Components: 1st place
This pitch idea hits the nail on the head, I think. It’s all about still thinking in components but extending the composability. SRC is an entry point to the future where react components can be published on NPM and have their own data fetching concerns fully met out of the box rather than having to rely on app developers to understand what data is needed.
This is the benefit that is much more interesting to CEOs than the details of how this is actually achieved. Imagine how much more streamlined the work of large react teams becomes if components can handle their own data in a way that allows you to put them together in more complex groups without footgunning yourself.
That vision is definitely appealing. So, what are the drawbacks and challenges we might expect should it be realized?
Fullstack React: added complexity vs metaframework lock-in
The already mentioned emergence of react meta-frameworks, i.e. frameworks built on top of React, is not a problem in itself - on the contrary - next.js, remix and other frameworks have a lot to contribute.
That said, React team deferring to these frameworks as a way to start an application tells you something about the fact that delivering on the vision outlined above no longer sits solely within the remit of React. The bits frameworks care about now become an integral part of delivering your UI to your users. What follows is that you might have to acknowledge that choosing React as your UI technology will now have potentially further reaching consequences and may come with added complexity. In other words, choosing React is no longer just choosing one of many flavors of V in MVC.
This sounds like a lot of additional headache, doesn’t it? It doesn’t have to be! The whole point of the shift towards starting with a slightly more complex setup than just a SPA means that you’re not locking yourself into a SPA - and that’s a huge benefit as it saves you some serious grief in the future. Adding all the server-y bits to a bloated SPA is a complex endeavor. To avoid it, you may want to start with running RSC at build time (so react server components but – sort of – without a server) and then shift towards using whatever mode makes sense on route by route basis (and yes, there is a Dan Abramov tweet to back this up too).
Does it mean that you should marry a metaframework? Maybe, and maybe not - either way, it’s now up to you to understand what such a framework does for you, and the answer is no longer simply based on whether you need SEO and hence need SSR. By the way, RSC is not the same as SSR. Server side rendering can be set up to play nicely with React Server Components though, and that’s partially also what metaframeworks do for you.
One thing to consider is that server rendering runtimes are now a thing. As your UI app no longer lives solely in the browser, hosting becomes potentially more complex than just dropping a SPA bundle on a CDN. UI architecture considerations would now need to extend to deciding what your SRC server rendering environment requirements are. For example, Next.js now offers three different server rendering runtimes (node, node serverless, edge) - each with its own characteristics when it comes to security, latency, scalability and IO access and each with its own tradeoffs (e.g. code ran on the edge cannot exceed a certain size etc.). If you don’t want to be married to Vercel (the hosting company behind Next.js) - you need to roll your own. If you don’t mind tying yourself to a hosting provider, that has consequences too.
Another thing to consider is that with SRC bundlers play an increasingly important role. Your bundler would now be tasked with generating serializable “module reference” objects, i.e. creating the server side placeholders for client rendered components, and then helping the client stitch the tree back together following the client rendering step. If you bootstrapped your project with a metaframework, this won’t be an issue. If you’ve got your own bundler setup, it might be something to look into.
What is obvious already, however, is that all of the above potentially impact the way you should source technical talent for react teams. Let’s look at why that is.
What fullstack react means for sourcing React talent
If the react community does embrace the metaframework first approach, one immediate consequence is that React skills will become less transferable. Familiarity with React via Next is not necessarily going to equate to familiarity with React via Remix - not to mention that we can expect to see meta-metaframeworks, i.e. more domain specific frameworks to be built on top of metaframeworks (e.g. Shopify hydrogen 2.0 is built on top of Remix). Each metaframework offers different abstractions and each is going to be incentivized to differentiate its offering. Competition is always great for end users, but bear in mind skill transferability may suffer. In your hiring you may therefore be well advised to pay more attention to the understanding of the underlying server related concepts, not framework trivia.
Another consequence is that - metaframework or not - the mental model of react stretched across the network boundary becomes increasingly complex when it comes to debugging, optimization etc. Also, marrying the current client-heavy toolset with SRC may prove challenging. For starters, here is Jack Harrington building a toy Next.js 13 app using redux toolkit both on the client and the server and initializing the client store with values from the server store:
This is to say that spreading React both on the client and the server may yield new challenges which you may want to test for or upskill towards.
At this stage you may ask: is it worth all the hussle of revamping your HR practices just because one framework came up with a new feature? My answer in this particular case would be yes, if you believe this trend is wider than RSC - and I believe it is.
Now with .net eight on the horizon there is a new flavor of Blazor in town, called Blazor United, which promises to give developers the best of both worlds by allowing them to define which components should render on the server and which should be treated as client components. Sounds familiar? Here’s Jon Hilton explaining the details of how Blazor United came to be, and comparing it to Remix: https://jonhilton.net/blazor-united/https://jonhilton.net/blazor-united/
This is not to say that server heavy UI frameworks are somehow a historical inevitability. The split between frontend and backend development is a valid one - at least for larger organizations. However, what seems to be more and more evident is that this split doesn’t align with the split between the server and the browser as neatly as we once thought. Instead the fault line seems to be between UI related concerns (spanning both server and frontend) and non UI related concerns. The talent sourcing consequences are pretty obvious if you look at it this way, and they go well beyond React.
It’s still early days for RSC but it seems like it’s time to start looking at the details of what server-first React means for our organisations.
About the author
Jan Bełezina is a software engineer and guest blogger for Bitnoise, based in Oxford. He specializes in React, Three.js, Next.js, and TypeScript. As a former product manager and business analyst, he has a wide overview of the development process and strategies.