When I started frontend development in 2006, there wasn't much choice in rendering HTML. Server-side rendering was the way to go, and I would churn out PHP powered websites like nobody's business. JavaScript was still considered a toy language by most people; JQuery was in its infancy.
Today things look quite a bit different. You can render your HTML at varying stages of your release process. This goes from "at build time" to "after a client received a response." This doesn't just allow you to do things at different times, but also on different computers.
In this article, I will explain three ways to get your HTML rendered. These include:
- 📱 Client-side rendering (CSR)
- 🏗️ Build time rendering (BTR)
- ⚙️ Server-side rendering (SSR)
Every one of the approaches here has different pros & cons you should keep in mind when choosing one, but some frontend frameworks allow you to use multiple methods in one application.
📱 Client-Side Rendering
- Hosting: Cheap & Easy
- Complexity: Medium
- Performance: Okay
- API Access: CORS APIs only
- Real-time: Yes
CSR web apps can be seen as the Ikea of apps because the client needs to assemble the app themselves after delivery.
CSR means you only send minimal HTML to the client and do everything with JavaScript.
CSR web apps often come in the form of "single-page applications," or SPAs for short, but they don't have to. You can link together a bunch of HTML files that get rendered on the client with JavaScript.
CSR is easy and cheap to host, for the server can treat all files as static. It doesn't matter which file type they have.
Since everything is rendered in the browser, there isn't much reasoning about where what happens. You usually don't care about your webserver version, all that counts is the browsers that execute your JavaScript.
If you get into performance issues here, things can get a bit messy, because you need to optimize your JavaScript delivery with some tooling, before you can deploy them on a webserver. Terms like minification, tree-shaking, or chunking will become essential and raise the complexity of your development process.
On the other hand, many things can't be done without the client-side JavaScript. If you need some complex interactions, animations, or even real-time features in your app, you don't get around JavaScript on the client.
Then there is user-specific content—for example, private messages. If you have to deliver data to one client only, it doesn't make sense to render everything at build time; sometimes, it doesn't even make sense to render it on the server-side.
If only one user needs the data, why should a machine required by all of your users do the work and not the users' system?
API access can be an issue here too.
First, some companies offer their API only via server-side access. You can't call them from a browser because they are hosted on different domains, and don't let you configure CORS headers for your domain.
Second, they cost you money. Even if you can access a database securely from your browser, paying for all the clients can get pricey quick! If you have user-specific data, this isn't an issue, but if you have a blog post, for example. That post is stored in some database and looked at by all of your users. You don't want to pay database access 10,000 times. In that case, it could be a better idea to load it once at build time, render it to a static HTML file and serve it cheaply to everyone who wants it.
As for performance before, there still can be valid reasons to call an API directly from browsers. Real-time games, animated maps, or chats could be a good reason to do so.
🏗️ Build Time Rendering
- Hosting: Cheap & Easy
- Complexity: Low
- Performance: Great
- API Access: Unrestricted
- Real-time: No
BTR is more like a custom carpenter who builds furniture at their workshop and delivers the finished product right to your home.
HTML gets rendered to static files once on your build server and will be deployed "as is" on some webserver. This has a host of interesting properties.
First, the vital code runs on your machines, so you have full control over them. You can rent a big machine for a few minutes to render the whole page and only pay a few cents.
Since everything runs on your system, you can access APIs without restrictions and often save money because you only hit the API one time to render a page that thousands of users view.
The deployment is as easy as with client-side rendered apps because you only have static assets that can be easily distributed with a webserver.
The HTML is available right after the response hits the client; no assembly required. If you got big lists of data and stream the HTML to the client, the browser can even start rendering the page before they finished receiving it.
Real-time features aren't available in this case. While it has the best rendering performance for data that doesn't often change, once the frequency of change gets too high, you're better off rendering client-side again.
User-specific data can become a problem. If you have to render private message pages for thousands of users and keep them around for them to read them, things can get expensive. Often user-specific data isn't even accessed by most of your users, so you can save quite some money when only rendering it on-demand.
⚙️ Server-Side Rendering
- Hosting: Expensive & complex
- Complexity: Medium
- Performance: Okay
- API Access: Unrestricted
- Real-time: partially
SSR sits between CSR and BTR.
SSR is the way it was with Java, Ruby, Python, and PHP before CSR's rise with JavaScript.
You render a website on-demand on the webserver, right when a user needs it.
The hosting is more expensive, as your app is executed on a server. This means the server has to offer some runtime environment for your back-end language, which is more complicated to maintain than just a web server that delivers a bunch of static files.
Cloud providers make hosting such apps easier every day. First, you could rent a server and install your software to run it; later, you could get pre-installed virtual serves, who were available quicker and cheaper.
Soon we had runtime specific offerings like "PHP webspace" or platform as a service offering, where you paid by the hour or even minute.
Now we have managed container orchestration and "function as a service" providers that give use more fine granular ways to execute our server-side code.
Caching plays a massive part in reducing the bill and improving performance. All the routes that are read by multiple users that don't change too often can be cached somewhere between the server and the client, so the "one server has to do the work for all the clients multiple times" down-side isn't that bad.
Sites like Dev.to still rely on SSR and do pretty well here serving thousands of users a day.
API access is a no-brainer here because you only access APIs from a machine you control. Typically, this adds some latency because everything has now gone through another node in the network, but if you don't have real-time data requirements, this isn't an issue.
About the real-time requirements, well technically, it's possible to build real-time apps with SSR technology, but if you want to avoid client-side code, you are a bit restricted here. With HTTP server push and streaming you can keep a connection to your client open and if you got new data, push it over to them, and the browser can act on it, but that's only a one-way route and getting data back to the server without JavaScript on the client is a bit of a struggle.
When to use What?
First, look at the requirements, if you need real-time features or have data that gets updated frequently it could lead to a saner system architecture if you simply stick to CSR. Also, if most of your data is specific to one or small groups of users, it could also be a good idea to keep things on the client.
It's also important to look at tech you or your team knows. If you need to get up and running quickly, it could be good enough to use what you already know. Most apps need multiple iterations anyway, so build the first releases as quick as you can and then refine them. Switching your stack because of a hunch won't help you, so monitor what you doing.
Important note here: You don't have to stick to one rendering method in one app. You can aways sprinkle CSR in your BTR or SSR app if needed. So if you are a RoR pro and need a real-time feature here and there, don't throw the baby out with the bathwater! In fact, almost all sites on the web use multiple approaches.
Some frameworks also offer multiple or even all of these approaches, so you can switch parts of your app according to the requirements in the future.
What do you prefer and why? Tell me in the comments!