CHAPTER 2SSG, SSR and SPAs
The React way is to write components, which are functions that produce UI given some data. React was invented for the browser, but Javascript functions which produce "HTML" can be run anywhere nowadays.
There are, in fact, three different moments in the lifetime of a web app in which you can run components:
-
At build time: you can load very slowly changing data, generate plain HTML and render it into files. This is called Static Site Generation.
-
On the server: if the data changes more frequently, it might be worthwhile to wait until a client requests a page to render it. This is called Server Side Rendering. Web applications which only implement this method are called MPAs, or Multi-Page Applications.
-
On the client: if you are on an SPA (Single Page Application), you don't want to reload the whole page and therefore you can load the data right in the client and reconstruct the DOM using the framework. This is called Client Side Rendering.
A brief history of MPAs, SPAs, and meta-frameworks
In the beginning the web was just documents, plain files. That was the "Static Web". It was like a library, with millions of documents linked together.
Then someone though that those pages could be generated by a program, and the "Dynamic Web" was born. Many languages and frameworks were developed that made it easier to load the necessary data and produce HTML for it (Ruby on Rails, Django, PHP, JSP, ASP, etc.). All applications back then were MPAs. Every page was generated by the server as pure HTML, and interactivity was very limited.
But then Javascript, which was a second rate language started to gain importance, since interactivity was the next frontier. As Javascript evolved and became more powerful, interactivity using the DOM became more and more difficult and libraries emerged to make it easier, React being the most loved.
To be able to use React to its full potential, developers started doing everything in React, so that the server didn't even have to render anything. Web apps made this way are called SPAs. The server sends an HTML file with an empty body, along with all the javascript, including the framework and the app, and all rendering happens on the client. To be able to do this, apps needed APIs, since the data that was put on the HTML by MPAs now had to be loaded from the client.
But then developers realized their bigger and bigger apps were getting slower and slower because they had to send huge chunks of Javascript to the client that weren't needed to render that first page, and made loading the first page very frustrating. Once loaded, switching pages was fast, but the first page was a problem. To render a store front, the client had to load all javascript, parse it, execute it and at execution time had to load the data from the API. All of this added seconds upon seconds until the user could see something or interact with it.
So developers started doing tricks, like code-splitting, tree-shaking and others to try to reduce the amount of Javascript that the server sent on every page, to try to reduce it to a minimum.
But eventually someone realized the obvious: by moving rendering back to the server, you could then send pre-rendered HTML to the user on the first page, and then after that render on the client could take over and render the rest. This was the best of both worlds. Meanwhile, frameworks which were specifically for documentation (11ty, Gatsby, Hugo, etc) didn't even produce HTML on request, they generated it at build time because the data that was rendered only changed from time to time. Those are the SSG frameworks. All of this was possible because, by that time, Javascript had been around for so long that it already was very strong both on the client and on the server.
All of that together gave rise to the current batch of meta-frameworks, which try to do all of the different techniques at once, combining them in the most uninstrusive way possible and letting developers "just" make the application they want.
React Server Components
React Server Components were announced by the React team in 2022 (and discussed before), and represent the consensus among the React community as to how should the framework evolve.
NextJS has, in fact, been the force behind the evolution of React (due to the close collaboration between both teams) towards the new model, and has implemented React Server Components ahead of every other framework. (In this respect, it seems the most natural React successor.)
Here is a React Server Component:
const loadBeer = async (id) => {
const repsonse = await fetch(`https://api.punkapi.com/v2/beers/1`);
const [beer] = await response.json();
return beer;
}
export default async function Beer() {
const beer = await loadBeer();
return (
<div className="beer">
<img src={beer.image_url} alt="Beer photo" />
<h2>{beer.name}</h2>
<p>{beer.description}</p>
</div>
)
}
The code reads like a typical React client component but in which the function awaits for the data to arrive and then paints, which is much simpler than the usual useState
and useEffect
pattern.
React Server Components can be asynchronous, and internally React renders things concurrently and assembles the results. This augments React and makes it as capable as PHP on the server, and gives us the flexibility to decide where to render components.
Consequences
What are the consequences of React spanning both server and client?
- Much of a page is actually static, or with data changing in a matter of days, so much of the code for generating that HTML can stay on the server. The initial page load is faster because of it.
- The initial page load is more predictable in size and doesn't grow with the app.
- The mental model for components is more consistent, even if we have to distinguish ourselves between server and client.
- The component generation process is asynchronous and therefore concurrent, which means that you can interleave static and dynamic components and "stream" them to the client (using
<Suspend>
).