Building a Fast Website for every single visitor
Medhat Dawoud
Web is Full of Challenges
2G
3G
4G
5G
@med7atdawoud
Everyone loves to use latest technology
Developers are privileged
Give MacBooks away to users to be guaranteed
Building a Fast Website for every single visitor
Medhat Dawoud
Medhat Dawoud
Senior Software Engineer at Miro
Google Developer Expert in #WebPerf
๐ @med7atdawoud
medhat.dev
It is all about making the Web more Accessible
Performance is one factor
Performance is relative
So, How to measure Performance ?
#1 Lab Testing
aka synthetic testing
IT IS NOT REAL
You Canโt Predict the User Experience
#2 Field Testing
aka RUM testing
Core Web Vitals
Device Distribution
Connection Distribution
What if we build for the worst user condition? ๐ค
@med7atdawoud
Pick Your Direction
๐ Lowest UX
๐ Best UX
To build a web application for the user with guaranteed conditions
To build a web application that can adapt to user conditions
@med7atdawoud
Well, letโs adapt to
User Variables
@med7atdawoud
Device Screen Sizes
Browsers support
User Preferences
Device (CPU + RAM)
Internet connection
Five user variables when shipping a web app
@med7atdawoud
โ Detect user conditions
โ๏ธ Mitigate perf issue
Steps to mitigate issues
@med7atdawoud
Device Screen Sizes
Browsers support
User Preferences
Device (CPU + RAM)
Internet connection
Five user variables when shipping a web app
Device Screen Sizes
@med7atdawoud
Components should be responsive like water
@med7atdawoud
Components should be responsive like water cats
@med7atdawoud
@med7atdawoud
Desktop
Tablet
Phone
@med7atdawoud
<img
srcset="elva-fairy-480w.jpg 480w, elva-fairy-800w.jpg 800w"
sizes="(max-width: 600px) 480px, 800px"
src="elva-fairy-800w.jpg"
alt="Elva dressed as a fairy" />
Responsive Images
@med7atdawoud
๐ก
https://developer.chrome.com/docs/aurora
const sharp = require('sharp');
const fs = require('fs');
const directory = './images';
fs.readdirSync(directory).forEach(file => {
sharp(`${directory}/${file}`)
.resize(200, 100) // width, height
.toFile(`${directory}/${file}-small.jpg`);
});
Server Different Images with different sizes using Sharp or ImageMagick
Or use an image service like Cloudinary or Thumbor
@med7atdawoud
๐ก
โ Device Screen Sizes
Browsers Support
User Preferences
Device (CPU + RAM)
Internet connection
Five user variables when shipping a web app
Browsers Support
@med7atdawoud
Browsers Engines
Gecko
Blink
Webkit
Blink
Blink
Fallback sources (media)
<video class="theatre" autoplay muted playsinline control>
<source src="/static/img/doodle-theatre.webm" type="video/webm">
<source src="/static/img/doodle-theatre.mp4" type="video/mp4">
</video>
<picture>
<source srcset="photo.avif" type="image/avif" />
<source srcset="photo.webp" type="image/webp" />
<img src="photo.jpg" alt="photo" />
</picture>
@med7atdawoud
* Even Edge added support for AVIF lately
Always check for available features and add a polyfill
if ("geolocation" in navigator) {
navigator.geolocation.getCurrentPosition(function (position) {
// show the location on a map, such as the Google Maps API
});
} else {
// Give the user a choice of static maps
// or a polyfill to this missing feature
}
@med7atdawoud
Always check for available features and polyfill
if ('requestIdleCallback' in window) {
// Use requestIdleCallback to schedule work.
} else {
// Do what youโd do today.
}
@med7atdawoud
CSS @supports
/* This rule won't be applied in browsers that don't support :has() */
ul:has(> li li) {
/* CSS is applied when the :has(โฆ) pseudo-class is supported */
}
@supports not selector(:has(a, b)) {
/* Fallback for when :has() is unsupported */
ul > li,
ol > li {
/* The above expanded for browsers that don't support :has(โฆ) */
}
}
@med7atdawoud
Skip Unnecessary shipped polyfills
Recommendation
๐ก
Device (CPU + RAM)
โ Device Screen Sizes
โ Browsers support
User Preferences
Internet connection
Five user variables when shipping a web app
User Preferences
@med7atdawoud
User can change their preference and you need to adapt to that
@media (prefers-color-scheme: dark) { โฆ }
@media (prefers-reduced-motion: reduced) { โฆ }
@media (prefers-reduced-data: reduced) { โฆ }
.translucent { opacity: 0.4; }
@media (prefers-reduced-transparency) {
.translucent { opacity: 0.8; }
}
@med7atdawoud
Animations might trigger discomfort for people suffering from Vestibular Motion Disorders
Device (CPU + RAM)
โ Device Screen Sizes
โ Browsers support
Internet connection
Five user variables when shipping a web app
โ User Preferences
Device (CPU + RAM)
@med7atdawoud
@med7atdawoud
navigator.deviceMemory ๐งช
8
> window.navigator.deviceMemory
@med7atdawoud
@med7atdawoud
navigator.hardwareConcurrency
10
> window.navigator.hardwareConcurrency
@med7atdawoud
@med7atdawoud
Zooming in/out and animations in slow devices can be disabled
Recommendation #1
๐ก
@med7atdawoud
Image carousel can load other images on intent
Recommendation #2
๐ก
@med7atdawoud
Yielding to main thread is NOT always the best for slow devices
Recommendation #3
๐ก
@med7atdawoud
โ Device (CPU + RAM)
โ Device Screen Sizes
โ Browsers support
Internet connection
Five user variables when shipping a web app
โ User Preferences
Internet connection
@med7atdawoud
2G
3G
4G
5G
1G
Internet Connection
@med7atdawoud
@med7atdawoud
@med7atdawoud
@med7atdawoud
Network Information APIs ๐งช
{
downlink: 3.85
effectiveType: "4g"
onchange: null
rtt: 50
saveData: false
}
> window.navigator.connection
@med7atdawoud
const connectionType = navigator.connection.effectiveType;
switch(connectionType) {
case '4g':
return <Video src={videoSrc} />
case '3g':
return <Image src={imageSrc.mid} alt={alt} />
default:
return <Image src={imageSrc.low} alt={alt} />
}
4G
3G
2G
@med7atdawoud
@med7atdawoud
React Adaptive Hooks โ๏ธ
import { useNetworkStatus } from 'react-adaptive-hooks/network';
import { useSaveData } from 'react-adaptive-hooks/save-data';
import { useHardwareConcurrency } from 'react-adaptive-hooks/hardware-concurrency';
export const Func = () => {
const { effectiveConnectionType } = useNetworkStatus();
// effectiveConnectionType can be slow-2G, 3G, 4G
. . .
}
Fetch smaller number of items per page in slow connection
Recommendation #1
๐ก
@med7atdawoud
Change a heavy custom font to a system font
Recommendation #2
๐ก
@med7atdawoud
Network Information APIs ๐งช
{
downlink: 3.85
effectiveType: "4g"
onchange: null
rtt: 50
saveData: false
}
> window.navigator.connection
@med7atdawoud
saveData (Gifs and videos should not autoplay)
@med7atdawoud
โ Device Screen Sizes
โ Browsers support
โ User Preferences
โ Device (CPU + RAM)
โ Internet connection
Recap
@med7atdawoud
Resources
https://www.youtube.com/watch?v=puUPpVrIRkc
https://medium.com/@roderickhsiao/sophisticated-adaptive-loading-strategies-7118341fcf91
https://medium.com/@sushant7/pwa-progressive-web-apps-in-2023-86b04be17508
https://web.dev/adaptive-loading-cds-2019/
https://www.youtube.com/watch?v=BoaORHnJYAE&ab_channel=BeyondFireship
https://addyosmani.com/blog/adaptive-serving/
https://deanhume.com/dynamic-resources-using-the-network-information-api-and-service-workers/
https://justmarkup.com/articles/2017-11-02-network-based-image-loading/
https://developer.mozilla.org/en-US/docs/Glossary/Graceful_degradation
https://www.smashingmagazine.com/2009/04/progressive-enhancement-what-it-is-and-how-to-use-it/
https://developer.mozilla.org/en-US/docs/Glossary/Progressive_Enhancement
Thanks
Medhat Dawoud โข medhat.dev โข @med7atdawoud