Software as a Service is a very common way to distribute software. Instead of selling CDs like our ancestors did, companies create complex web applications that you can access through the browser: Gmail, Microsoft Office 360, Figma…
But there’s a problem: SaaS applications are a terrible context for build-time pre-rendering. Therefore they tend to break the Jamstack principles.
That’s because a SaaS app is the inverse of a blog or an e-commerce for instance. In a blog or e-commerce, you have tons of public pages with public data: that’s the best fit for build-time rendering, aka static site generation.
In a software, you have few pages with tons of private data. At best, you can pre-render an empty shell of the page, following Blitz.js saying “secure data, not pages”, and Vercel’s dashboard architecture. But the shell is only filled with client-side data, in the user’s browser: those pages will always feel slower than pages with only public data.
Static rendering technologies have not been created to pre-render private data. That makes sense, because to render private data, you need to have a rough idea of who owns those data, and thus who is going to use your website. Yet you can’t know that in advance, at build-time. Also, it means you need a server to secure the pages for each request, while some frameworks like Gatsby do not provide a server at all.
But that was before Rainbow Rendering.
There is definitely an intermediate between a page that strongly relies on user data, and thus needs client-side rendering or request-time rendering (aka SSR), and a totally public page that allows build-time rendering (aka static rendering or SSG) .
A famous use-case where you need to render multiple variations of the same page is internationalization. You build one version of your page per language available in your application.
Another one: say you want to have a dark and light theme for your website. It would be cool to be able to pre-render both versions.
Even better: a pretty common use-case for SaaS applications is “multi-tenancy”, when your app is used by multiple organizations. A company may want to customize some elements, like showing its logo on the top of the pages. All the employees of the company that use your app should see the logo of their company.
So, parameters like theme, language or company the user belongs to are good candidates for build-time rendering, so that the user gets the right display immediately.
The common strategy to render such variations of a page in advance, statically, is to create one URL per possible variation.
For example “/ Home-page / company-acme / theme-light / fr” would be the home page of ACME, in French, with a white background, while “/ Home-page / company-foobar / theme-dark / it” would be the home page for Foobar Inc., in Italian, with a black background.
You see the problem? Static generation relies too much on the URL. What about cookies? Request headers? Query params? They could help to know which version of the page to display without bloating the URL.
Currently, when facing those scenarios, developers end up either relying on client-side rendering, which implies a loading time for the user, or per-request server-side rendering (SSR), which is extremely expensive and requires a powerful server. If only we could do static rendering instead, it would be cheaper and faster!
Recently, I’ve tried to prove more formally that static generation is currently used below its maximum capabilities because of this limitation, in terms of “all the possible variations you could build in theory for a given webpage” versus what existing frameworks like Gatsby or Next.js actually let you build ”. Click at your own risk, there are some maths involved: https://tinyurl.com/ssr-theory.
I’ve made up a word to describe a new pattern that will solve those issues: Rainbow Rendering. Why Rainbow? Because it means you can render the same page in multiple colors.
Rainbow Rendering is static rendering based on request parameters such as cookies, headers, query parameters… instead of just the URL.
As simple as that, it’s an extension to Static Site Generation, static rendering, Jamstack or whatever you call it.
Some of you might think “yes, but… there is no request at build-time, during the static render! You cannot rely on the request headers or cookies during a static render. ”
No, but actually yes. By building one page per URL, you are implicitly supposing that HTTP request will be sent to those pages. Yes, there is actually a request at build-time!
The confusion comes from the fact that we are used to use only 10% of this “implicit” request, the URL, and throw away the 90% left during the static render, in particular cookies and headers. What a waste!
I’ve described Rainbow Rendering from the developer standpoint. There is a small condition for it to work in practice: you need a micro-server to process the request, so that cookies and headers are set correctly.
It is important that this request processing step happens server-side in order to secure the request content.
With Rainbow Render, you can render private data statically, not just public pages like you are used to in Gatsby or Next.js. For example, you could pre-render data that belongs to a given company. You certainly don’t want to send those data to employees of another competing company! An attacker could craft fake cookies very easily. For instance, they could say they belong to company A while they actually depend on company B, and see some data from company A. You need to forbid that.
Thus, you must absolutely prevent unauthorized user to access those private pages.
This issue did not exist when we relied only on the URL during static rendering, because you cannot “lie” on the URL. It also did not exist with server-side rendering, because you can do some security check while you process the request (though, for Next.js users, doing this check in
getServerSideProps is in my opinion at best an escape hatch, at worst an anti-pattern).
That’s exactly the role of the upfront mini-server in the “Unicorn scheme of Rainbow Rendering” below (the unicorn horn). This micro-server has exactly 2 roles:
- securing the request
- redirecting to the right variation of the page.
This server can have the same declarative API as Next.js rewrites for instance, plus some logic to check authentication. But it could also be a more advanced server, using Node and middlewares to process the request.
Ahem… Rainbow Rendering is currently theoretical. I believe implementation in Next.js would become possible provided that:
Instead of just providing route parameters, you should also be able to return a combination of cookies, headers and so on. The syntax could be similar to route rewrites for instance.
- Next.js adds a middleware system
Currently you would have to do the security checks (authentication of the user for instance) in the getServerSideProps method, an anti-pattern that raises a lot of issues. In order to implement Rainbow Rendering security checks correctly, extending Next.js route rewriting capabilities could be a good starting point for public data (like handling a dark / light mode statically). However, a more advanced middleware system is mandatory for rainbow rendering private data, as custom code is required to authenticate users.
It’s not that easy to design such a feature in practice, because it’s difficult to understand for developers and implementation has to be very secure. Yet, I hope we will see new solutions emerge in the future to enable Rainbow Rendering in major Jamstack frameworks 🙂
UPDATE: Next 12 finally landed middlewares! Rainbow Rendering is finally possible, check our follow-up article for more details: https://blog.vulcanjs.org/render-anything-statically-with-next-js-and-the-megaparam-4039e66ffde