Abstract
Jamstack, SSR, SSG, static rendering, Next.js, Gatsby, ISR, prerendering, edge handlers… JavaScript developers sure like making up new words!
But what do they mean, actually? In this talk, I’ll demystify those fancy concepts and propose a better definition of “server-side rendering” a web app.
You thought “server-side rendering” and “static rendering” were different things? That you cannot render authenticated, paid or personalized content at build-time? That per-request SSR costs an arm and a leg? Then this talk will blow your mind!
Based on this corrected definition of SSR, I’ll show you how you can solve the “rich guest/poor customer” issue that arises when you don’t use static rendering to its full potential. The result: ultra-personalized static websites, that cost nothing to host and deliver the best possible user experience.
Segment your renders and make your customers rich again!
Transcript
This transcript was autogenerated. To make changes, submit a PR.
Jamaica real
time feedback into the behavior of your distributed systems and
observing changes exceptions. Errors in real time
allows you to not only experiment with confidence, but respond instantly
to get things working again.
Those everybody,
and welcome to my talk. Treat your users right with segmented
rendering. Let me start by introducing myself.
My name is Eric Burel. I live in France, in Montpellier. I'm the
founder of a company based Elbecau. I'm a web developer.
I'm doing a lot of things. I'm doing consulting in public funding
for a company named Research to business. I'm those maintainer of
the open source framework vulcanjs that used to run on top
of meteor and is now working with NextJs.
I'm a member of the devographics collective who runs the
state of JavaScript costs and graphQL surveys created
by Sasha Graph and I'm a next JS teacher
at human coders. You can find me on Twitter and medium.
So this talk is essentially about
web personalization. So let's start by defining it.
I think the most common example of web personalization is
theming. It's just, for instance,
letting a user pick version
of the website they like. Here I
have free themes, firewater and grass. Of course, usually in
real life the theme will be about dark versus light
mode, for instance, but it could also be an adaptation to
a certain company, having your own logo or picking your own colors for
a website. So web personalization is anything
that adapts a website to who the user
is and usually knowing who
the user is. ISR actually implemented using
cookies. For instance, I will have a cookie
name starter with the values fire, water or
grass that will define the themes I picked.
But you are already used to use personalization
in a lot of different contexts. So for instance,
you have the same use case, but you have internationalization,
which is a form of web personalization because you adapt
a website to the language of the user that you
detect based on the request headers or cookies.
There is the paid versus unpaid content and
guest content. You have a b testing. That's one very advanced
example of web personalization where you change
a version of the website to make an experiment. For example,
you have half the user with a version can old version and the other
half with a newer version that you want to experiment and you
compare the statistics for both things is an example
of web personalization. So it's kind of ubiquitous
in web development. So let's focus on this
theme use case and let's try to render the right
those based on the user information.
So first let's define this idea of rendering. What do I mean by
prerendering? Nextjs is a good platform
to understand rendering because it embeds all modes of
prerendering of server rendering and client rendering.
So the first one, client rendering is just using javascript
or treat or whatever front end framework you are used to use when creating
single page application with a lot of interactivity and
javascript. That's good. Old client side prerendering,
server side rendering or per request server side rendering
is what you are used to do if you code
with PHP, Ruby, django, a language that is not
node JS, basically a
traditional web server. And static
prerendering is a variation of server rendering where you
render the page ahead of time and not for every request.
It's just like putting your server render into a cache or
just writing HTML ahead of time that is not adapted
to the current user, to the request.
So basically, client side prerendering and per request
server side rendering are dynamic patterns.
Why? Because they take the current users into account.
Serverside rendering happens for each request, so you
can adapt the answer based on the request content. So based
on the cookies, the user information and stuff like that.
Client side prerendering happens in the browser. So of course it's
adapted to the user because it's literally running on their
machine. Static treat generation
is, well a static pattern, but it has the
advantage of being faster since the page, the HTML
of the page is generated ahead of time. So there is
no new computation for a new request. But the problem
is that you cannot adapt the content to the request because it's
already on the end. It's too late when the request happens.
So what to use for personalization?
The answer is really the dynamic patterns. Of course
they are dynamic and they can adapt to the current request or user.
The problem ISR that they are slower because they need to do
a new computation for each request or for each
interaction between the user and the browser.
So ideally we would like to use static generation
to get better performances. And segmented rendering
is exactly about that. A way to use the static
pattern for personalized content.
Let me rephrase things idea in terms of
business concept. When you use dynamic
patterns for web personalization, you are
faced with what I call the rich guest per customer issue.
The problem is that you are using those faster static
renders for public content that is not personalized
and you are using the slower dynamic patterns for
personalized content. But personalized content
is usually for your customers, for the people, you know, for the
people that are logged in, for instance for the paid users of your application.
So people pay to access your website,
pay for your ecommerce website, they provide their data and so on.
And yet you treat them less.
You give them worse performances than the people
that are not connected, those guest of your website. That's the rich guest,
poor customers issue. So let's solve
this with segmented rendering.
So the most basic way to implement segmented
rendering is to use the URL,
those patterns words. Okay, for example here we could have
the theme firewater and grass as a URL,
a root parameter of the URL, an explicit parameter.
You may be used to this pattern. For example, for internationalization,
it's common to have fr en,
to say France, England or whatever, to have
the language in the URL. And it's okay for many use
cases. However, there are a few issues. First,
the user can change the URL that's okay for the language,
that's not okay for paid content. Of course they
can see the parameter okay for internationalization.
But if you are doing an A B test, you can throw it into
trash because if the user is aware of the bucket they are in,
it's useless. The test is just broken. So you
cannot do that for a b testing. It's not safe.
And finally, when you have a lot of parameters, the URL will become
quite long and ugly. You cannot show anything, any parameter in
a URL that's not those same way to personalize a website.
Okay, so let's have a second try
without the URL. This time I need
a very small addition to my architecture.
I need to add what we call a proxy
server, a tiny server that is just able to take your
request and to change the URL server
side. And then it will point to the right page.
This is a very subtle change compared to
just using the URL, but very important because this URL
rewrite is happening serverside and it isr not known by
the end user. So the user is just accessing
the website and the URL parameter is added
after server side. It's a kind of server side rerouting of
the request and it's very powerful because it
will solve all our issues in one
go. The user won't see the parameter, it's added
serverside. Those URL doesn't become ugly because
the parameter is not visible and you can do
everything securely. For example, adding a paid parameter securely
because it happens server side, the user cannot alter the parameters manually.
The parameter itself can be computed based on the request,
typically based on a user id you get from
the cookies session token that allows
to identify the user uniquely server side. But again it's
safe, which is great compared to the URL.
And this is just segmented rendering. That's it.
You just have to add this tiny redirection server and it opens
up this new pattern that lets you statically render personalized
content and get the best performance for your customers.
One URL per segment and a rope server.
This might sound a bit abstract to be fair.
So I wanted to show you now a possible implementation
with next JS middleware. Because next JS edge middlewares
can play the role of the proxy servers I've described
earlier. It's specifically running at the edge. Of course
it could work with more traditional architecture, for example with an
NginX proxy server. But in nextjs
the advantage is that it's built in the framework. You can
collocate this serverside and your nextjs code, and in
addition it's written in JavaScript, so it's more intuitive for front end developer
than say Nginx or configuring your AWS
gateway or any solution that is targeted at DevOps,
while nextjs middleware are targeted more at Javascript
developers. So let's proceed with the demonstration.
Let's get started by discovering the end result of segmented
rendering. So I have a simple demonstration website,
you can find the link in the slide and I will share that where
relevant. So basically I have this website and I let
the user explicitly select a theme between fire,
water and grass. Okay?
And the idea of segmented rendering is that I don't want
the theme to appear in the URL and I want the theme selection
process to be secure because for a theme there is no critical
issue of course, but I want this pattern to work also for paid
content, for secure content, for IB test, for whatever. I want a very
generic pattern. So here I have
no theme currently and I will select the fire
theme. Okay, the page refresh and
the current, those is fire. You see that the text becomes
red. So first thing to notice, it's slightly
small on my computer. But basically you see were that the URL is
not changed, I don't have a fire parameter, I don't need
it. Second, you can notice
in the network tab, for instance if I do a hard
refresh, that the result returned from
the server is already personalized
current those fire I don't have the css, so you won't see
the color, the actual color in the development tooling. But you see that
the text is correct. So this means that this was server
renders. However, this was statically
server rendering, meaning that I did not need a new computation.
I cannot prove that in the browser because you cannot
see what happened in the server, but I will show you the corresponding
code that is using only the static features of nextjs
and not per request. Server side rendering. This is very important.
This means that things personalization doesn't require a
new computation. If you have 3 million people using
the fire theme, there is only one
render and not one render for each of the those million request.
That's the big difference with dynamic patterns.
And it's faster because it's already renders as well.
So the current those ISR statically renders
to define and to remember the corrected theme.
I've set up a cookie, a theme cookie that can
be read by the server on each request to select the right
theme for the current user. As simple
as that. And this pattern is very interesting because as
said earlier, it applies to any kind of personalization
where you can define a segment where multiple
people may have the same
version of the website. Like can a b test where you have two
groups with many people in each group. Like a language where world
country will use the same language and stuff like that.
So let's take a look at the code. First,
it starts with the middleware, which is the most
important part of my segmented rendering
architecture. It's the proxy server I've shown earlier.
So the middleware ISR really simple when
I mean it's a light server. It's a light server.
There are more concepts than code here. So the
middleware will just take the request,
read the theme cookie, it can
check that the theme is valid. So if the user try to use
a those that doesn't exist, well they just cannot.
I can throw an error or I can have any kind of behavior I
want and I can do that server side so it cannot
be hacked by the user. So it would work very well with paid
content, for instance. Then finally I use a
URL rewrite to rewrite the URL of the request.
It's important to notice that it is not a URL reguration,
but can actual rewrite. The difference is just that a reguration
ISR visible to the end user. A rewrite is not,
it is internal to the server. And that's why the end user dont
see the personalization parameter. It's happening on those server
side. It's kind of rerouting a request on the server
to match another URL without the user knowing it.
Okay, the second part of the segment and
rendering architecture is the rendering part.
Here I have redirected the user by detecting
the right segment based on the cookies so the those
and corrected them to right variation of the website.
But I need to actually render the different versions of the
page, different themes for the same page.
So to do so I will use the
API of next js get static path and get static props.
If you are not familiar with nextjs, static path is
about defining which version of the page you want to statically renders.
So here for instance, I'm pre rendering a fire version,
a water version, a version without a those,
and I voluntarily omit graph because I
want to demo incremental static regeneration which isr basically
the ability of nextjs to create
a new version of the page on the go on the first request.
So for instance, if a combination of parameters is not
very common, you may not statically render it,
but instead render it on the first request.
Then it will be put in a cache and reused for further
request. But it's important for those who are already wondering
how we will be able to handlers a website with a lot of
variations. The answer is that you statically renders
the most important variations, a few of them,
and then you used this pattern, this incremental static regeneration
which exists also with
other names in other frameworks. There are other approach to do
so for other variations. Those idea is just that you put
everything in a cache to reuse them later, so that one
render is shared by all users of a segment.
Get static props for each path will compute
the data of the page. So here it's just the
theme itself. But we could also for instance get
more information about each theme in a database
or something like that. Then this
parameter is just paste to the page.
So the those component receive the those as props.
As simple as that in HS. And based on this we
can renders different themes. So for example here I'm selecting
different styles based on the theme,
the current theme value. So that's the
rendering part. I'm configuring my rendering framework. So nextjs gets
p whatever to statically render each
possible variation of the patch. This is not those hard part.
If you are used to static prerendering, things will feel very normal.
It's just static seed generation. It exists with
many technologies and those smart idea is just to
say I'm going to generate variations even
that are not really static, like paid content versus
free content. And I will put a
middleware in front of them to pick the right
variation. I've computed ahead.
I know it sounds a bit unsettling at first, but you
see from the demonstration and you will have access to the code.
Again from the slide there is a link. This is all
open source. It's actually really a few lines of code and
using approach that you already know. You already know
what a URL redirection, what a URL
is, you already know what a request is and you already know what static
rendering is if you are used to create full stack application
or to those frameworks. So I'm just combining the
simple approaches to create to achieve an optimal number
of renders. Small bonus.
I'm using an edge API route to select
the right theme. So I'm using an HTTP cookie basically
to set the theme. This can be useful if you need more
security on the cookie. So for example, based on those unique
identifier for an A B test bucket you might want to use
such patterns. So I'm switching the those using only server side
code and not client side Javascript. So this demonstration
should work without Javascript enabled.
That's the small bonus. It's not mandatory,
but that's the small bonus for this application.
So let's get back at the slides.
Now this has been a long ride, but here we are.
We have implemented segmented rendering and demonstrated
the implementation with next js. If you are exploring
the versel website, Versaille being those company behind Nextjs,
if you don't already know it,
they will use segmented prerendering but give them the name of edge
personalization. This is because in Versailles, for instance,
the proxy server is implemented at the edge.
The edge is just having a cload of tiny
servers that are close to the user. The idea is that the personalized is
very fast and because it happens near the
end users. And this is typically useful if you have a global
company for instance, that operates in multiple country, you may have
segments that are defined by regions, for instance.
So segmented rendering is very appropriate in this
context and it will be translated as
edge personalized at the costs level.
Because it happens at the edge. You will see this naming also
with competitors of Versailles, for example netlify is implementing
edge features. Eleventi is doing another kind
of edge personalized as well, which is
more about altering the HTML than using this redirection
approach. But that's something you might hear about in the ecosystem.
I've picked another name, segmented rendering because I still
want to have a definition
of this pattern that is not specific to the those stagnant rendering
is just my generalization and formalization of things
pattern that you may encounter in practice in the industry.
Vih personalization.
So I'd like to conclude on the future of SSR.
Actually maybe the current
of the SSR, because I'm registering things
talk right after the nextjs 13 release,
and if you don't know it already, it completely changes
the way next JS server side rendering is structured.
The segmented rendering demonstration still hold.
Those implementation is slightly different if you know, if you are
already aware of next JS inner workings.
Basically you have the headers and cookies function, and segmented
rendering is about avoiding to call those functions when
it's not needed. So that's something we
may have the chance to discuss, and I may
have the chance to talk more about if you follow
me on Twitter. Or you can meet me on the nextjs
discord and they are very often talking about such
patterns. But basically the
future of server side prerendering ISR about
caching, it's about forgetting this static
versus per request server side rendering idea and just thinking that it's
about caching. So static rendering is
just server side rendering cached at build time. And the idea of segmented
rendering is just that a tiny server lets you pick
the right cached value. It's just a cache
key actually, and per request serverside rendering is
when a value render cannot be cached.
It can happen in some situation when the data are heavily
personalized, for instance, you cannot put them
in cache. It works here because I have segments of users
that will have the same version of the page,
basically. So the cache key
is not just the URL, it's the full request,
and the cache value is the renders HTML. And the middleware
lets you compute the right key for each request,
translate the request to a URL that acts as an
intermediate cache key. So my bet
is that in the future we will have a unified API for server side rendering
that encompasses static, perrequest and everything in between.
It's a relatively safe bet, given that
again, the next GS 13 release is really about that they
actually started to unify the IPF or serverside rendering.
However, it's still heavily based on the URL more than the
request. So you still have to use this middleware trick to
implement segmented rendering. While I hope that
in an even further future,
instead of having defining the variation
of your page based on the URL, you could use those perrequest directly,
because this is actually what's happening under the URL is
just a simpler way of doing this, but it's actually
all about request.
I hope you enjoyed this talk.
I've put a lot of additional resources at
the end if you want to get more familiar with this
pattern with personalization which is very useful
in the industry. If you are dont ecommerce, a b test and
managing website at scale, you cannot avoid web personalization.
So I've put my preferred resources and a few articles that shows
various variation of segmented rendering like the megaparam
patterns that can let you scale it or how plasmic
use it in a no code approach which is also very interesting.
I hope you enjoyed this talk and I hope
you will enjoy the rest of the comfort conference.
See you soon. Thank you.