When I talk to people that work primarily on the backend, with a programming language that isn't JavaScript, I often encounter outdated and sometimes plain wrong ideas about how rendering with a modern frontend framework like React works.
They point to their Twig and Razor templates and tell me that React is alright and all that, but it can't compete with their server-side-rendered templates, because they're faster, and React sucks for SEO anyway.
So let's debunk that.
First, let's have a look at a chart that any developer of traditional backends will be familiar with. This is how a browser usually communicates with your backend to get the information it needs to render the website the user requested:
In this chart and all charts following this one, the X axis is frontend on the left, to backend on the right. The Y axis is time. Read the cart from the top to the bottom, following the arrows.
This works great, but I see a couple problems with it:
Or at least, it could be faster. When the backend receives a request, it has to talk to the database to get the data needed for rendering first. It might also have to talk to an external API. Then it has to actually render the page and send it back to the client. You can mitigate some of this with caching, avoiding a roundtrip to the DB or an external API, but you have to manage that yourself, and caching is easy to fuck up. As the infamous saying goes:
Additionally, if you want to prerender your templates for faster delivery, you'll most likely have to tightly couple your backend and its data sources, like a CMS. So now you not only have tightly coupled your frontend to your backend, vastly limiting what's possible in the frontend, now you've done the same to the CMS. I don't know what to say to that. Congratulations?
If you need help building your websites the traditional way, you can only hire developers that know both the frontend and whatever backend language you are working with, because any developer on your team has to understand the templates, and those are written in your preferred backend language.
What ends up happening is that you search for a "Full-Stack developer". Or at least that's what you think you're doing. In reality, you primarily look for someone who understands your backend. Because that's the complex part. Anyone can write some CSS and slap on some JS to round it off. Anyone can do the frontend.
So you hire a bunch of backend developers with some frontend experience, instead of hiring experienced backend developers and experienced frontend developers.
Because the backend and the frontend are tightly coupled at the template level, you can't easily reuse either for a new purpose. If you want to build another frontend, like a mobile app, with the same data, the same backend, you'll also have to do a lot of work on the backend to make it possible.
But, you argue, while the traditional SSR strategy might not be perfect, React isn't any better, because React works like this:
What this chart shows is Client Side Rendering, or CSR, and React can work like this, but it almost never makes sense to exclusively use it like this.
I think this misconception comes from two places:
- From people who have evaluated React ten years ago and have never used it since. While the methods I am about to describe in this post have been around for longer than that, they were a lot more difficult to implement back then, and it was much easier to go down the wrong path and end up with a website that is slow and sucks for SEO.
- From people who have never worked with a headless backend. They eventually come across a use case where they realize that React might make sense. For example, they have to build a website that needs a lot of user-interaction and state. But as they cling to their backend templates and don't want to spin up a Node.js server, they ship React to the client and only use it for a few complex components while most of their app keeps using their familiar stack.
In both scenarios, people end up client-side rendering all of their React stuff. But let me tell you: This is not the way. Don't do that. Because React is slow and does suck for SEO if you do it this way.
Instead, you server-side-render your components.
Usually at this point, backend developers I talk to about this start to laugh, and shortly after that the discussion moves on to a different topic, because we've been talking about half an hour about this to get to this point. They laugh and say things like "that's ironic, we've come full circle, we've been right all along", and they think they've been right all along and move on and I'm left with the feeling that I haven't convinced anybody because I haven't even gotten to my point yet.
So here's that point:
Have another look at the traditional SSR chart above. Wouldn't it be great if we could render the website before the user even requests it? This way, we'd only have to send back the static assets resulting from that render, saving a lot of time and server resources. With React, we can! It's called Static Site Generation, or SSG. It works like this:
You'll probably notice a glaring problem with this right away, we'll get to that, but let's have a look at how it helps us first:
Faster even than your beloved traditional SSR strategy. Because when a user requests a website, the only thing the backend has to do is send back the static assets. No database communication, no rendering. Just "Here's your assets, bye".
So next time we talk about how your preferred backend programming language is inherently faster than JavaScript because it is just built differently and can run a million for loops faster than JS or whatever, rest assured that I don't give a fuck, because for an architecture like this, it is completely irrelevant. Your incredibly fast code written in your incredibly fast language doesn't even get used, except when initially rendering the static assets.
And in this context, I as the developer might be waiting for the assets to be generated, but no end-user is waiting for a website to load, so the couple milliseconds you might (might!) save by using C# or Python or PHP or whatever you prefer on the backend vs. JavaScript at best slightly improves the developer experience in this particular scenario.
In fact, you don't even need anything to the right of the Node server in production. You could generate the static assets locally on your computer and only upload the static assets to your server.
If you do it this way, you don't even need a JavaScript server anymore, those static files can be served by any server you like.
Another major advantage here is that the frontend now talks to the backend via an API. This could be a REST API, or ideally a GraphQL or tRPC API. The API acts like a contract between the frontend and the backend teams. The two teams agree upon a schema. For example, they might agree that a product has a title, an image and a price. As long as neither team breaks that contract, neither team has to know anything about the work of the other team.
The backend team only has to know that they have to build an API endpoint that delivers products with those properties. Note also that the backend team can use any backend they prefer, the backend infrastructure does not have to be written in JavaScript.
The frontend team meanwhile knows that they'll get their product data at a specific endpoint. They can then do with that data whatever they want. Render a product detail page, or a teaser, or display it in a cart. They too, can use any tools they want, unencumbered by the choices of the backend team.
They could even build two entirely separate frontends, using the same data from the same API in two completely different contexts; On different websites or even on different platforms, like in a mobile app and a website.
I think those advantages are incredible, but of course there's an issue: If you use Static Site Generation, your site will be, gasp, static. Meaning it can't change. If you have a CMS where your client can change the content of the website, they can still change that in the CMS, but the website won't update.
If you want to update the website, you have to regenerate and redeploy it. This means that SSG is only really a viable strategy if you are fine with that. For example, the previous version of this very website was built like this. Before, I would only update it about once a year and when I did so, I'd usually also change some code. So I was fine with running npm run build
again before deploying it.
But for people that are not an active part of the development team of the website they want to change, or for people who simply prefer the convenience of a CMS, we need a different solution.
Here's that solution:
Incremental Static Regeneration, or ISR, allows you to automatically and partially rerender your static assets when the underlying data changes. It's a bit more complex than what we have discussed so far, but a decent frontend framework like Next.js will handle most of this automatically for you.
Let's have a look:
The first part at the top is just SSG, it works exactly the same and has all the same benefits. The second part is revalidation. Every time a user requests a particular website, the user immediately gets the statically generated response, just like SSG, but in addition to that, some of the times (for example at most once every 60 seconds if you have many users, though this is usually configurable) the JavaScript server will check the API (and all other data sources) for any changes. If there are no changes, that's it, nothing further happens. But if there are changes, for example because an editor changed the page in a CMS, the JavaScript server will regenerate just that one page and then send an update to the browser.
The next user that accesses the updated page will get the updated static assets right away.
Again, you can achieve similar results with caching and background services in your backend of choice, I do realize that. But then you have to manage all that yourself, and remember the quote?
Plus, if you do it yourself, you might get to the same level of speed as Next.js provides out of the box and with very little setup, but you do not get the separation of backend and frontend, which I think is an even bigger benefit.
The only slight disadvantage I see in this is that you need an additional server. But in most circumstances, you have several servers running already anyway; One for the backend and one for the database at the very least, likely additional ones for the CMS and auxiliary services like a queue, an auth or mailing server. So what's one more server?
You might want to display data that cannot be statically generated, for example a user profile or live data like financial charts. This is not a problem. With Next.js you can decide for each individual component how you want to render it. You can statically generate the entire page and then dynamically load some data for a specific component.
So with ISR we were able to fix the glaring problem of SSG, while keeping all of the benefits. Thus, in my opinion, this is the best way to render a website nowadays, providing both the best developer experience and the fastest server response times. Though of course it always depends on the circumstances.
There's many ways to render a website with React, and you can mix and match them to suit your specific needs. Unfortunately, many people less familiar with frontend frameworks assume there's only one way: Client Side Rendering. I hope I could convince you otherwise with this post.