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?
👍 TTI & FCP
👎 Slow TTFB
👎 Interactive with different language
so... hard to mantain
👍 TTI & FCP
👍 Fast TTFB!
👍 Static files
👎 Inflexible
👀 hydratation could be needed
export
👍 Flexible
👍 Fast TTFB and FCP!
👍 Just one static index.html
👎 Slow TTFB
👎 TTI way after FCP
👍 Flexible
👍 Universal code
👍 Fast TTFB and FCP
👎 Slow TTFB
👎 TTI way after FCP
👎 Infrastructure💰
👍 Flexible
👍 Universal code
👍 Fast TTFB and FCP
👎 Slow TTFB
👎 TTI way after FCP
👎 Infrastructure💰
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)
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
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 👉
😱
@schibstedspain/react-perf-dynamic-rendering
Only 1️⃣ requirement:
It's open source!
you need to use React ⚛️
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
links detected
at a later time
💸 expensive!
https://youtu.be/LXF8bM4g-J4
WRS
https://youtu.be/LXF8bM4g-J4
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: ''}}
/>
)
}
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>)
a.k.a partial hydration
at component level
at component level
🏩
a.k.a partial hydration
👀 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>)
a.k.a partial hydration
How have we achieved this?
is could be slow.
Now you have strategies
to fix it.
rendering demos