




I'm Miguel Ángel Durán

@midudev

youtube.com/midudev

#performance⚡️ #react ⚛️
#sushi 🍣 #travelling ✈️
Enabler Frontend at

👨💻
@midudev

What frontend library
do you use?
Text
@midudev






📷 🏠



@midudev

february 2019



@midudev





global score
@midudev
🏋️♂️
🏋️♂️

1.
2.
3.
4.
@midudev
TTI

february 2019
15 seconds

📹
@midudev

@midudev

february 2019

Is slow?
We asked ourselves...

@midudev


today

february 2019
@midudev
@midudev


The problem is not React anymore

How do we have achieved this?

-2 seconds
-1 second
@midudev
⏳
Time to interactive


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

rendering strategies

@midudev
the problem
Understanding
@midudev

the problem
is...
render is expensive

and
@midudev
specially on the client 📱

Server Side Rendering (SSR)
Slow Time to First Byte
Maintain different codebases




✅
@midudev
Time To Interactive
First Contentful Paint
SEO Friendly
❌

Static Rendering



TTI & FCP
Fast TTFB!
Static files

👀 hydratation could be needed
export
@midudev
Inflexible
✅
❌

Client Side Rendering




Flexible
Fast TTFB and FCP!
Just static files
TTI way after FCP
No SEO friendly
@midudev
✅
❌

ReactDOM.render(
<App />,
document.getElementById('root')
)
Client Side Rendering
with
trying to get the best of two worlds...
SSR with (re)hydration 💦
@midudev
SSR 🌍 + CSR 🌎

SSR with (re)hydration 💦


hydration data
html from server


}
@midudev

server side

@midudev

const initialData = await getInitialData()
const html = ReactDOM.renderToString(
<App initialData={initialData} />
)
res.send(`
<div id='root'>${html}</div>
<script>
window.__INITIAL_DATA__ = ${JSON.stringify(initialData)}
</script>`
)
with
SSR with (re)hydration 💦
ReactDOM.hydrate(
<App initialData={window.__INITIAL_DATA__} />,
document.getElementById('root')
)
// SERVER SIDE simplified
// CLIENT SIDE simplified
🏁 Attach event listeners to the existing markup
💦 Hydration
🔎 Check mismatches between server-side and client-side
🌳 Recreate tree on the virtual dom
🚀 Set up life cycles order and fire them
a.k.a bootstraping or initialization
⚛️ constructor, componentDidMount, useEffect, ...

ReactDOM.hydrate

app.$mount('#root')
@midudev
SSR with (re)hydration 💦

}


⏳

ReactDOM.hydrate
from server
client after hydration
@midudev
on client side


frameworks
@midudev



SSR with (re)hydration 💦
Flexible
Universal code
Fast TTFB and FCP
SEO Friendly
@midudev
SSR with (re)hydration 💦
the best of the two worlds?
✅
Slow TTFB from SSR
TTI way after FCP from CSR
@midudev
SSR with (re)hydration 💦
... or the worst?
❌
SSR
with (re)hydration
DEMO

@midudev
@midudev
a solution

Finding
@midudev
but... how?
🤖 SEO
I still want to...
⚛️ Flexibility
🤳 User Experience (UX)
👩💻 Developer Experience (DX)
@midudev

rendering strategies

@midudev
Dynamic Rendering
starting with...

@midudev
Dynamic Rendering
what is...

🤖
👫
@midudev
Strategies with
Dynamic Rendering

by Route
by Component


@midudev
Dynamic
Rendering
by route

@midudev
// 🚇 middleware to activate Dynamic Rendering on Express
const BOTS_USER_AGENTS = [
'googlebot',
'mediapartners-google',
'yandexbot'
]
const INDEX_HTML_PATH = path.join(__dirname, 'public', 'index.html')
const HTML_TEMPLATE = fs.readFileSync(INDEX_HTML_PATH, 'utf8')
module.exports = 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('*', serverSideRenderingMiddleware)

next()
response html


👩💻
🤖
Dynamic
Rendering
at route level
DEMO
@midudev

@midudev
Dynamic
Rendering
by route
♻️ Fast TTFB
🧹 Hydration data is gone
1️⃣ Only changes on the server side

🧪 Useful technique for perf experiments
🆓 Free resources from your server
⏳ SEO Friendly
🤔 Client Side Rendering for user
🚩 Bad first paint

@midudev
Is Dynamic Rendering worth it?
🤔🚩
so...
@midudev
Dynamic
Rendering
at component level

@midudev


ServerSide Rendering
with (re)hydration
⚛️
@midudev
Dynamic Rendering
at component level
with
...
@midudev

⚛️
🤖 🌍
💁♂️ 🌍




👀
👀
@midudev
viewport
How to use & implementation

@midudev

<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 /> ~"kinda" implementation
export default function DynamicRendering({ children, userAgent }) {
const isBot = checkUserAgentIsBot({userAgent})
if (isBot) return children
if (typeof window !== 'undefined') { // isBrowser
return <LazyContent>{children}</LazyContent>
} else { // isServer
return <div style={{border: '1px solid red'}} />
}
}

A real use case scenario
@midudev
in production


📏 ~700px visible
>20000px rendered 👉
😱
👀 only 1 card
34 cards
@midudev


without Dynamic Rendering

~525ms
with Dynamic Rendering
~107ms
-80% reduction ✂️

@midudev
Only 1️⃣ requirement:
It's open source!
you need to use React ⚛️
# npm
npm install @midudev/react-dynamic-rendering
# yarn
yarn add @midudev/react-dynamic-rendering
Fully compatible with:

@midudev


📦

@midudev
Dynamic
Rendering
at component level
DEMO
@midudev
@midudev
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

🚩 Keep Hydration data
👩🔬 Need universal UA calculation
🤖 Bot still gets full cost
@midudev

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

@midudev
The new bots of Google are superb!
🧩 JS Modules
📐 Intersection Observer
🧱 Custom Elements
🌚 Shadow DOM
🏫 Classes
🏷 Tagged Template Literals


Why do we need this?

@midudev


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

@midudev
want/need to be
crawled fast and indexed faster
use server-side rendering. period.
tl;dl 👂
@midudev
also SSR is usually faster than CSR
⚡
@midudev


@midudev
Static
Rendering
at component level

@midudev
🏁 Attach event listeners to the existing markup
💦 Hydration
🔎 Check mismatches between server-side and client-side
🌳 Recreate tree on the virtual dom
🚀 Set up life cycles order and fire them
a.k.a bootstraping or initialization
constructor, componentDidMount, useEffect, ...
avoid the cost of this 👆
@midudev
How to use & implementation

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

// ⚛️ <StaticContent /> ~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
dangerouslySetInnerHTML={{__html: ''}}
suppressHydrationWarning
/>
)
}





⚛️ core contributor

@midudev

@midudev

Static
Rendering
DEMO
@midudev
@midudev
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>)
@midudev
Only 1️⃣ requirement:
It's open source!
you need to use React ⚛️
# npm
npm install @midudev/react-static-content
# yarn
yarn add @midudev/react-static-content
Fully compatible with:

@midudev

Can we do it better?
Can we do it progressive?
@midudev



@midudev


Static
Rendering
Dynamic
Rendering
at component level
➕
@midudev
Progressive
Hydration

@midudev



👀

👀

👀
👈 Static HTML
👈 Static HTML
👈 Static HTML
@midudev
@schibstedspain/react-perf-dynamic-rendering
http://bit.ly/sui-dynamic-rendering
// ⚛️ How to use <ProgressiveRendering />
function ProgressiveHydrationUsage({articles}) {
return (
<Grid>
<h1>Articles</h1>
{articles.map(article => (
<ProgressiveHydration key={article.id}>
<Card {...article} />
</ProgressiveHydration>
))}
</Grid>
)
}

@schibstedspain/react-perf-dynamic-rendering
http://bit.ly/sui-dynamic-rendering
// ⚛️ <ProgressiveRendering /> ~"kinda" implementation
function ProgressiveHydration({children}) {
const ref = useRef(null)
const isNearScreen = useNearScreen({ref})
useEffect(() => {
// CLIENT: If the element is near screen, then hydrate it
if (isNearScreen) {
ReactDOM.hydrate(children, ref.current)
}
}, [children, isNearScreen])
// SERVER: Just render the content as usual
if (typeof window === 'undefined')
return <div ref={ref}>{children}</div>
// CLIENT: Avoid hydration until we say so
return (
<div ref={ref}
suppressHydrationWarning
dangerouslySetInnerHTML={{__html: ''}}
/>
)
}

vue-lazy-hydration by maoberlehner
Lazy hydration of server-side rendered Vue.js components
about

lazy-hydration by znck
Lazy Hydration for Vue SSR
Simple API. Plug and play.
Configurable interaction to fire hydration.
@midudev
<template>
<div class="ArticlePage">
<LazyHydrate when-idle>
<ImageSlider/>
</LazyHydrate>
<LazyHydrate ssr-only>
<ArticleContent :content="article.content"/>
</LazyHydrate>
<LazyHydrate when-visible>
<AdSlider/>
</LazyHydrate>
<!-- `on-interaction` listens for a `focus` by default ... -->
<LazyHydrate on-interaction>
<CommentForm :article-id="article.id"/>
</LazyHydrate>
<!-- ... but you can listen for any event you want ... -->
<LazyHydrate on-interaction="click">
<CommentForm :article-id="article.id"/>
</LazyHydrate>
<!-- ... or even multiple events. -->
<LazyHydrate :on-interaction="['click', 'touchstart']">
<CommentForm :article-id="article.id"/>
</LazyHydrate>
</div>
</template>
vue-lazy-hydration

about




@midudev
and
react-prerendered-component by theKashey
Partial Hydration and Component Level Caching
Powerful 💪 but complicated 🤹♂️
react-progressive-hydration by
Progressive Hydration
Experimental 🧪 but interesting 🤯
@midudev/react-progressive-hydration
Progressive Hydration
Breaks connectivity between React trees ⚛️ 🌳


@midudev
Progressive
Hydration
DEMO

@midudev
@midudev


👀 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>)
⚠️ No Context passed down
Progressive
Hydration
@midudev
Only 1️⃣ requirement:
It's open source!
you need to use React ⚛️
# npm
npm install @midudev/react-progressive-hydration
# yarn
yarn add @midudev/react-progressive-hydration
Fully compatible with:

@midudev


so, what's next?
@midudev
SSR in React could be much better
from Andrew Clarke presentation: https://youtu.be/z-6JC0_cOns?t=950
Can't start rendering anything until everything is loaded
Encapsulation is difficult (no state or lifecycles)
Need to hoist all async data
@midudev
First class support
on React?


@midudev

Progressive Hydration
Selective Hydration


Yuzhi Zheng at React Conf 2019
@midudev


Actual SSR with hydration
@midudev
hydrate on the client 💦
while streaming from the server 📡
Future SSR with hydration
📹
@midudev
Selective Hydration
@midudev

so...
@midudev

How have we achieved this?


Static
Rendering
Dynamic
Rendering

@midudev
⏳
Time to interactive



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

@midudev

rendering strategies

@midudev
🔗 https://midu.dev/devfest-links
¡Gracias Alicante 🙇♂️!
all links &
resources


@midudev


DevFest Alicante - React Rendering Strategies
By Miguel Angel Durán García
DevFest Alicante - React Rendering Strategies
Version: DevFest Alicante
- 968