A presentation at VueConf US in in New Orleans, LA, USA by Phil Hawksworth
Render Time Render when, where, how, what, and why. Phil Hawksworth, Netlify
Oh, hello Phil Hawksworth Developer Experience, Netlify
Oh, hello @ PhilHawksworth Developer Experience, Netlify
Oh, hello @ PhilHawksworth @ indieweb.social Developer Experience, Netlify
Oh, hello PhilHawksworth . NAMING IS HARD hawksworx.com Developer Experience, Netlify
A slight change of pace after Evan Some foundations for talks to come
Rendering
HTML / DOM
“…something something rendering on the server…” — Phil Hawksworth, 2013
“…ha ha ha do you just mean serving HTML?” — The person listening to Phil Hawksworth, 2013
Server Client
Server Client
Server Client
Robustness
The rule of least power https://www.w3.org/2001/tag/doc/leastPower.html
Server Client
Build Server Client
Build Server Edge Client
“by thunder, there are a lot of ways to approach rendering!” — Phil Hawksworth, May 2023
Confusion
A place for rendering
A time and a place for rendering
What / Where / When / How / Why
A few mins 15 mins 10 mins / Into and twaddle / Examining rendering / Questioning / Applying techniques / Example / Lessons
SECTION 1 Examining rendering What even do these acronyms mean?
NAMING IS HARD Terminology
G / ISR / ODB / SPA / MPA / ESR PA / MPA / SSR / CSR / SSG / D / SPA / MPA / SSR / CSR / SSG / SSR / CSR / SSG / DPR / DSG SSR / CSR / SSG / DPR / DSG / ESR / ISR / ODB / SPA / MPA / NAMING IS HARD
RENDERING SSG Static Site Generation Build Server Edge Client
CI/CD Continuous Integration / Continuous Deployment
RENDERING SSG Static Site Generation Build Server Edge Client
TRADITIONAL STACK LOAD BALANCERS WEB SERVERS DATABASES VARIOUS INCREASINGLY POPULAR PROVIDERS ASSETS ON CDN BUILD AHEAD OF TIME CODE & CONTENT
RENDERING CSR Client side rendering Build Server Edge Client
When… the assets have arrived the user interacts
RENDERING SSR Server side rendering Build Server Edge Client
the-other-side Build Server Edge Client-side Client
Build Server Server-side Client-side Edge Client
Build Server Server-side? Client-side Edge Client
not-Client-side Build Server Edge Client-side Client
RENDERING SSR Server side rendering Build Server Edge Client
RENDERING ESR Edge side rendering Build Server Edge Client
TRADITIONAL STACK LOAD BALANCERS WEB SERVERS DATABASES VARIOUS INCREASINGLY POPULAR PROVIDERS SERVERLESS RUNTIME ASSETS ON CDN SERVERLESS RUNTIMES BUILD CODE & CONTENT
“so which one is best, Phil?” — Some of you, possibly, May 2023
“It depends” — Phil Hawksworth, May 2023
“Yeah. Great. Thanks.” — Some of you, possibly, May 2023
“It depends on what?” — Some of you, hopefully, May 2023
“I’m glad you ask” — Me again, May 2023
The rule of least power https://www.w3.org/2001/tag/doc/leastPower.html
Best shovel for digging a hole? https://unsplash.com/photos/qG6QtyOaOGQ
Serverside, doesn’t have to mean Serverful. If I can do things in advance, I will. If I can’t, but can do things serverless, I will. If I can’t, but can add a server, I will.
Increments DPR / ODB / DSG / ISR / FFS
Increments DPR / ODB / DSG / ISR
RENDERING DPR Distributed Persistent Rendering RENDERING RENDERING ODB DSG On-demand Builders Deferred Static Generation
DGIITBWUTFTIRTGIAATWWBE NAMING IS HARD Don’t generate it in the build. Wait until the first time it’s requested, then generate it and add it to what was built earlier.
TRADITIONAL STACK LOAD BALANCERS WEB SERVERS DATABASES VARIOUS INCREASINGLY POPULAR PROVIDERS SERVERLESS RUNTIME ASSETS ON CDN SERVERLESS RUNTIMES BUILD CODE & CONTENT
Increments DPR / ODB / DSG / ISR
RENDERING ISR Incremental Static Regeneration
RENDERING ISR Incremental Static Regeneration SWR Stale While Revalidate
“Which is better, ISR or DPR”? — Some of you, possibly, May 2023
“It depends”
Deciding demands questions
What are the requirements?
SECTION 2 Example Applying different rendering techniques
EXAMPLE Social posts stash Self-hosting thousands of posts
Some requirements A URL for each of the 24,000 tweets Index pages listing each tweet with its URL Ability to search the tweets Retain a reasonable build time Avoid client-side rendering if possible A logical model that fits in my head SSG ODB ESR
JSON
/ Social index pages / Content pages Build Assets User JSON & Templates
www.hawksworx.com/notes/1/
Build duration 2 seconds
/ Post page view / Social index pages / Content pages Build JSON & Templates Assets ODB User
www.hawksworx.com/note/mstdn/109913367394738833
/ Post page view / Social index pages / Content pages Build JSON & Templates Assets ODB ESR / Search results page User
www.hawksworx.com/notes/search/?str=palo+alto www.hawksworx.com/notes/search/?str=render
The rule of least power https://www.w3.org/2001/tag/doc/leastPower.html
Vitepress Generates pages SSG / CSR
Vitepress Generates pages SSG / CSR + ODB / ESR
At build time JSON At request time
Source archive of data into Vitepress tweets.data.js export default { load() { return { LOTS OF JSON } } } `
Source archive of data into Vitepress tweets.data.js const tweets = require(“./tweets.json”); export default { load() { return tweets } } `
ODB function — render a tweet netlify/functions/view-tweet.js const { builder } = require(‘@netlify/functions’); const notes = require(‘../../tweets.json’); const pageTemplate = require(‘./page/template.js’); const handler = async(event) =>” { const id = event.path.split(“tweet/”)[1]; const note = notes[id]; ` return { statusCode: 200, headers: {“Content-Type”: “text/html”}, body: pageTemplate(note) } } exports.handler = builder(handler);
ODB function — render a tweet netlify/functions/view-tweet.js const { builder } = require(‘@netlify/functions’); const notes = require(‘../../tweets.json’); const pageTemplate = require(‘./page/template.js’); const handler = async(event) =>” { const id = event.path.split(“tweet/”)[1]; const note = notes[id]; ` return { statusCode: 200, headers: {“Content-Type”: “text/html”}, body: pageTemplate(note) } } exports.handler = builder(handler);
ODB function — render a tweet netlify/functions/view-tweet.js const { builder } = require(‘@netlify/functions’); const notes = require(‘../../tweets.json’); const pageTemplate = require(‘./page/template.js’); const handler = async(event) =>” { const id = event.path.split(“tweet/”)[1]; const note = notes[id]; ` return { statusCode: 200, headers: {“Content-Type”: “text/html”}, body: pageTemplate(note) } } exports.handler = builder(handler);
ODB function — render a tweet netlify/functions/view-tweet.js const { builder } = require(‘@netlify/functions’); const notes = require(‘../../tweets.json’); const pageTemplate = require(‘./page/template.js’); const handler = async(event) =>” { const id = event.path.split(“tweet/”)[1]; const note = notes[id]; ` return { statusCode: 200, headers: {“Content-Type”: “text/html”}, body: pageTemplate(note) } } exports.handler = builder(handler);
ODB function — render a tweet netlify/functions/view-tweet.js const { builder } = require(‘@netlify/functions’); const notes = require(‘../../tweets.json’); const pageTemplate = require(‘./page/template.js’); const handler = async(event) =>” { const id = event.path.split(“tweet/”)[1]; const note = notes[id]; ` return { statusCode: 200, headers: {“Content-Type”: “text/html”}, body: pageTemplate(note) } } exports.handler = builder(handler);
ODB function — render a tweet netlify/functions/view-tweet.js const { builder } = require(‘@netlify/functions’); const notes = require(‘../../tweets.json’); const pageTemplate = require(‘./page/template.js’); const handler = async(event) =>” { const id = event.path.split(“tweet/”)[1]; const note = notes[id]; ` return { statusCode: 200, headers: {“Content-Type”: “text/html”}, body: pageTemplate(note) } } exports.handler = builder(handler);
ODB function — render a tweet netlify/functions/view-tweet.js const { builder } = require(‘@netlify/functions’); const notes = require(‘../../tweets.json’); const pageTemplate = require(‘./page/template.js’); const handler = async(event) =>” { const id = event.path.split(“tweet/”)[1]; const note = notes[id]; ` return { statusCode: 200, headers: {“Content-Type”: “text/html”}, body: pageTemplate(note) } } exports.handler = builder(handler);
Nuxt Supports several rendering patterns SSG / SSR / CSR / ESR
Nuxt’s route-based rendering configuration nuxt.config.ts export default defineNuxtConfig({ routeRules: { // Homepage pre-rendered at build time ‘/’: { prerender: true }, // Product page generated on-demand, revalidates in background ‘/products/$’: { swr: true }, ` // Blog post generated on-demand once until next deploy ‘/blog/$’: { isr: true }, } }) // Admin dashboard renders only on client-side ‘/admin/$’: { ssr: false },
SECTION 3 Lessons Amid the confusion, what did we learn?
There is no one right way
Do work ahead of time if you can
You can mix and match rendering methods
Never choose an approach until you understand the requirements
Not all of Phil’s tweets are pure gold
NAMING IS HARD
For more netlify.com/blog/tutorials nuxt.com/docs/guide/concepts/rendering twitter.com/philhawksworth Phil Hawksworth, Netlify
Thank you! Grab me for questions (or to say hello) Phil Hawksworth, Netlify
The way we choose to populate and deliver the pages of our sites and applications can have profound effects on performance, accessibility, discoverability, and complexity. And there is a seemingly endless number of options available. SSR? SPA? MPA? SSG? DPR? ISR? So many acronyms! So many choices! Which is the best?
The answer of course is “it depends”. But on what does it depend? How can we make the best decision for our projects, our users, and our developers? What are the questions we should ask to help us make the most appropriate and most informed choices?
In this talk, we’ll compare popular rendering models in order to understand their relative strengths and weaknesses. We’ll consider what project requirements might call for different types of rendering. And we’ll walk through a practical example which combines a number of rendering techniques to see how a web project can use a variety of complementary technologies with compelling results.