



I'm Miguel Ángel Durán

@midudev


Enabler Frontend at

youtube.com/midudev


Why are we here?


february 2019



users trying to interact with your site


TTI:

february 2019
15 seconds


is SLOW, you definitely can't improve more the performance. It's IMPOSSIBLE.
You MUST move to Pug to FIX this.
Or even better RETURN TO .NET TO SEND HTML.
twitter developer




today


How have we achieved this?

Getting the most out of performance 🚀
while keeping bots happy 🤖

rendering strategies

the problem
Understanding

the problem
is...
rendering stuff on the client is expensive

and
quick recap
rendering on the web

Server Side Rendering (SSR)

👍 TTI & FCP
👎 Slow TTFB
👎 Interactive with different language
so... hard to mantain




Static Rendering




👍 TTI & FCP
👍 Fast TTFB!
👍 Static files
👎 Inflexible

👀 hydratation could be needed
export
Client Side Rendering





👍 Flexible
👍 Fast TTFB and FCP!
👍 Just one static index.html
👎 Slow TTFB
👎 TTI way after FCP
trying to get the best of two worlds
SSR 🌍 + CSR 🌎

SSR with (re)hydration


finally
that's (re)hydration
and it looks like this...



SSR with (re)hydration

👍 Flexible
👍 Universal code
👍 Fast TTFB and FCP
👎 Slow TTFB
👎 TTI way after FCP
👎 Infrastructure💰



someone in the audience that uses ...
but doesn't want to say it



SSR with (re)hydration

is getting the best...
or the worst of both?

👍 Flexible
👍 Universal code
👍 Fast TTFB and FCP
👎 Slow TTFB
👎 TTI way after FCP
👎 Infrastructure💰
SSR
with (re)hydration
DEMO
a solution

Finding
solution
and the
is...
render less things

on the client and/or the server
but... how?
🤖 SEO
I still want to...
⚛️ Flexibility
🤳 UX
👩💻 DX


rendering strategies

Dynamic Rendering
starting with...

Dynamic Rendering
wt* is...

🤖
👫

Does like it?
I hope it does.
It's their idea! 💡

Strategies with
Dynamic Rendering

by Route
by Component


Dynamic
Rendering
by route

// 🚇 middleware to activate Dynamic Rendering on Express
const BOTS_USER_AGENTS = [
'googlebot',
'google-structured-data-testing-tool',
'mediapartners-google'
]
const INDEX_HTML_PATH = path.join(__dirname, 'public', 'index.html')
const HTML_TEMPLATE = fs.readFileSync(INDEX_HTML_PATH, 'utf8')
export default function middleware(req, res, next) {
const rawUserAgent = req.get('user-agent')
const userAgent = rawUserAgent.toLowerCase()
// check if the request comes from a bot 🤖
if (BOTS_USER_AGENTS.find(ua => userAgent.includes(ua))) {
// if not an user, just use the normal behaviour (SSR?)
return next()
}
// return index.html directly if is an user 👩💻
return res.send(HTML_TEMPLATE)
}

// 🚆 Using the middleware in Express
const dynamicRendering = require('./dynamic-rendering')
// Use dynamic rendering for a specific path for testing
app.get(
'/es/:op/:type/barcelona-capital/*',
dynamicRenderingExperiment
)
// Isomorphic routes handler
app.get('*', SSR)

Dynamic
Rendering
by route
♻️ TTFB
🧹 Hydration data is gone

🧪 Useful technique for perf experiments
🆓 Free resources from your server
⏳ Help GoogleBot to index your content faster
☝️ More about this, later 👉
🤔 Just a CSR for the user
🚩 FP & FCP

Dynamic
Rendering
at component level


// ⚛️ Using <DynamicRendering /> component
// userAgent must be retrieved universally
// on server and client
<DynamicRendering userAgent={universalUserAgent}>
<a href='https://very-interesting-url.com'>
<VeryComplexToComputeComponent />
<img src='https://huge-image.com/panda.jpg'/>
</a>
</DynamicRendering>

@schibstedspain/react-perf-dynamic-rendering
http://bit.ly/sui-dynamic-rendering
// ⚛️ <DynamicRendering /> kind of implementation
export default function DynamicRendering(props) {
const { children, disabled, forceRender, height, userAgent } = props
// check if the userAgent is a bot and if we're in the browser
const isBot = checkUserAgentIsBot({userAgent})
const isOnBrowser = typeof window !== 'undefined'
// Force render in server and client
if (forceRender) return children
// if isBot or disabled, we return in server and client the content
if (isBot) return children
// now, we're sure the user isNotBot
// so check if we're on the browser side and
// if is not disabled the component
if (isOnBrowser && !disabled) {
return <LazyContent height={height}>{children}</LazyContent>
} else {
// so, we're on the server side or the component is disabled
return <div style={{height: `${height}px`}} />
}
}



👈 ~700px visible
~20000px rendered 👉
😱


without Dynamic Rendering

~500ms
with Dynamic Rendering
~100ms
5x times less


@schibstedspain/react-perf-dynamic-rendering
Only 1️⃣ requirement:
It's open source!
you need to use React ⚛️
Dynamic
Rendering
at component level
DEMO
Dynamic
Rendering
at component level

✅ Improve TTI
😴 Lazy Load for the user
🏋️♀️ Perfect for stuff below the fold
🆓 Free resources from your server
⏳ Help GoogleBot to index your content faster
☝️ More about this, now 👉

🚩 Keep Hydration data
👩🔬 Need universal UA calculation
🤖 Bot still gets full cost
The new GoogleBot is superb!
🧩 JS Modules
📐 Intersection Observer
🧱 Custom Elements
🌚 Shadow DOM
🏫 Classes
🏷 Tagged Template Literals


Why do we need this?



links detected
at a later time
💸 expensive!
https://youtu.be/LXF8bM4g-J4

130 trillion web pages to crawl
rendering all those pages, takes time

WRS

Google Bot 101

https://youtu.be/LXF8bM4g-J4

If you want/need your site
to be crawled fast and indexed faster
you better bet on SSR
tl;dl 👂
for now
also SSR is usually faster than CSR


Static
Rendering
at component level

// ⚛️ <StaticContent /> usage
import StaticContent from './StaticContent'
function Footer () {
return (
<StaticContent>
<HugeListOfLinks data={listOfLinks} />
</StaticContent>
)
}

// ⚛️ <StaticContent /> easy-peasy implementation
import React from 'react'
export default function StaticContent({children}) {
// we're in the server, just render the content
if (typeof window === 'undefined') {
return <div>{children}</div>
}
// avoid re-render on the client
return (
<div
suppressHydrationWarning
dangerouslySetInnerHTML={{__html: ''}}
/>
)
}






Static
Rendering
DEMO
Static
Rendering
at component level

⚡ Avoid re-hydrate for static components
🤳 Thus could greatly improve TTI
📸 For expensive rendering lists or static content (SEO Footers)
🤖 GoogleBot is definitely going to detect it

⚠️ Lose interactivity
🏋️♀️ Hydration data still there
🥪 Element wrapper (ex. <div>)


Progressive
Rendering
a.k.a partial hydration



Static
Rendering
at component level
Dynamic
Rendering
at component level
🏩

Progressive
Rendering
a.k.a partial hydration
DEMO



👀 Only re-hydrate what's visible
🤳 Thus could greatly improve TTI
🔛 Activate interactivity on demand
📸 Kind of lazy loading experience
🤖 GoogleBot will get the rendered static html (not hydrated)
🏋️♀️ Hydration data still there
🥪 Element wrapper (ex. <div>)
Progressive
Rendering
a.k.a partial hydration

so, what's next?



How have we achieved this?


Static
Rendering
Dynamic
Rendering


is could be slow.
Now you have strategies
to fix it.


rendering demos


Thanks!
gifs borrowed from @eh_cat!


@midudev
youtube.com/midudev


React Rendering Strategies
By Miguel Angel Durán García
React Rendering Strategies
Version: Barcelona JS 2019
- 1,327