1 of 74

Building a Fast Website for every single visitor

Medhat Dawoud

2 of 74

Web is Full of Challenges

2G

3G

4G

5G

@med7atdawoud

3 of 74

Everyone loves to use latest technology

4 of 74

Developers are privileged

5 of 74

6 of 74

Give MacBooks away to users to be guaranteed

7 of 74

Building a Fast Website for every single visitor

Medhat Dawoud

8 of 74

Medhat Dawoud

Senior Software Engineer at Miro

Google Developer Expert in #WebPerf

๐• @med7atdawoud

medhat.dev

9 of 74

It is all about making the Web more Accessible

Performance is one factor

10 of 74

Performance is relative

11 of 74

So, How to measure Performance ?

12 of 74

#1 Lab Testing

aka synthetic testing

13 of 74

14 of 74

IT IS NOT REAL

15 of 74

You Canโ€™t Predict the User Experience

16 of 74

#2 Field Testing

aka RUM testing

17 of 74

Core Web Vitals

18 of 74

Device Distribution

19 of 74

Connection Distribution

20 of 74

What if we build for the worst user condition? ๐Ÿค”

@med7atdawoud

21 of 74

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

22 of 74

Well, letโ€™s adapt to

User Variables

@med7atdawoud

23 of 74

Device Screen Sizes

Browsers support

User Preferences

Device (CPU + RAM)

Internet connection

Five user variables when shipping a web app

@med7atdawoud

24 of 74

โœ… Detect user conditions

โš™๏ธ Mitigate perf issue

Steps to mitigate issues

@med7atdawoud

25 of 74

Device Screen Sizes

Browsers support

User Preferences

Device (CPU + RAM)

Internet connection

Five user variables when shipping a web app

Device Screen Sizes

@med7atdawoud

26 of 74

Components should be responsive like water

@med7atdawoud

27 of 74

Components should be responsive like water cats

@med7atdawoud

28 of 74

@med7atdawoud

29 of 74

Desktop

Tablet

Phone

@med7atdawoud

30 of 74

<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

31 of 74

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

๐Ÿ’ก

32 of 74

โœ… Device Screen Sizes

Browsers Support

User Preferences

Device (CPU + RAM)

Internet connection

Five user variables when shipping a web app

Browsers Support

@med7atdawoud

33 of 74

Browsers Engines

Gecko

Blink

Webkit

Blink

Blink

34 of 74

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

35 of 74

* Even Edge added support for AVIF lately

36 of 74

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

37 of 74

Always check for available features and polyfill

if ('requestIdleCallback' in window) {

// Use requestIdleCallback to schedule work.

} else {

// Do what youโ€™d do today.

}

@med7atdawoud

38 of 74

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

39 of 74

Skip Unnecessary shipped polyfills

Recommendation

๐Ÿ’ก

40 of 74

41 of 74

42 of 74

Device (CPU + RAM)

โœ… Device Screen Sizes

โœ… Browsers support

User Preferences

Internet connection

Five user variables when shipping a web app

User Preferences

@med7atdawoud

43 of 74

User can change their preference and you need to adapt to that

44 of 74

@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

45 of 74

46 of 74

Animations might trigger discomfort for people suffering from Vestibular Motion Disorders

47 of 74

48 of 74

Device (CPU + RAM)

โœ… Device Screen Sizes

โœ… Browsers support

Internet connection

Five user variables when shipping a web app

โœ… User Preferences

Device (CPU + RAM)

@med7atdawoud

49 of 74

@med7atdawoud

50 of 74

51 of 74

navigator.deviceMemory ๐Ÿงช

8

> window.navigator.deviceMemory

  • It returns the approximate device memory in GB
  • Client can send these info to server as memory hints to send memory heavy items according to available memory like 3D models.

@med7atdawoud

52 of 74

@med7atdawoud

53 of 74

navigator.hardwareConcurrency

10

> window.navigator.hardwareConcurrency

  • returns number of logical processors available to run threads on the user's computer.
  • You can use it to know the max number of web workers that can run

@med7atdawoud

54 of 74

@med7atdawoud

55 of 74

Zooming in/out and animations in slow devices can be disabled

Recommendation #1

๐Ÿ’ก

@med7atdawoud

56 of 74

Image carousel can load other images on intent

Recommendation #2

๐Ÿ’ก

@med7atdawoud

57 of 74

Yielding to main thread is NOT always the best for slow devices

Recommendation #3

๐Ÿ’ก

@med7atdawoud

58 of 74

โœ… Device (CPU + RAM)

โœ… Device Screen Sizes

โœ… Browsers support

Internet connection

Five user variables when shipping a web app

โœ… User Preferences

Internet connection

@med7atdawoud

59 of 74

2G

3G

4G

5G

1G

Internet Connection

@med7atdawoud

60 of 74

@med7atdawoud

61 of 74

@med7atdawoud

62 of 74

@med7atdawoud

63 of 74

Network Information APIs ๐Ÿงช

{

downlink: 3.85

effectiveType: "4g"

onchange: null

rtt: 50

saveData: false

}

> window.navigator.connection

@med7atdawoud

64 of 74

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

65 of 74

66 of 74

@med7atdawoud

67 of 74

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

. . .

}

68 of 74

Fetch smaller number of items per page in slow connection

Recommendation #1

๐Ÿ’ก

@med7atdawoud

69 of 74

Change a heavy custom font to a system font

Recommendation #2

๐Ÿ’ก

@med7atdawoud

70 of 74

Network Information APIs ๐Ÿงช

{

downlink: 3.85

effectiveType: "4g"

onchange: null

rtt: 50

saveData: false

}

> window.navigator.connection

@med7atdawoud

71 of 74

saveData (Gifs and videos should not autoplay)

@med7atdawoud

72 of 74

โœ… Device Screen Sizes

โœ… Browsers support

โœ… User Preferences

โœ… Device (CPU + RAM)

โœ… Internet connection

Recap

@med7atdawoud

73 of 74

Resources

74 of 74

Thanks

Medhat Dawoud โ€ข medhat.dev โ€ข @med7atdawoud