Secret shortcuts of Loading

Web Performance

Start with why

1 seconds slowness > 10% less users

The BBC found they lost an additional  10% of users for every additional second  their site took to load.

100ms faster > 1% more conversions

For Mobify, every 100ms decrease in homepage load speed  worked out to a 1.11% increase in session-based conversion,  yielding an average annual revenue increase of nearly $380,000.

20% faster > 10% more conversions

Retailer Furniture Village audited their site speed and  developed a plan to address the problems they found, leading to a 20% reduction in page load time and a 10% increase in conversion rate.

40% faster > 15% more sign-ups

Pinterest reduced perceived wait times  by 40% and this increased search  engine traffic and sign-ups by 15%.

Core Web Vitals

Largest Contenful Paint (LCP)

First Input Delay (FID)

Cumulative Layout Shift (CLS)

Performance pipeline

Loading

performance

Performance of

performance

JavaScript

Rendering

Loading Performance

Getting files you need over the network

Fetching resources over the network can be slow and expensive

Depends on

your internet speed

size of resource files

location of the server

request roundtrips

user devices

Nikola Mitrović

Development Lead & Technical Architect

Vega IT

Novi Sad, Serbia

Nikola Mitrović

Development Lead & Technical Architect

Vega IT

Novi Sad, Serbia

The cost of JavaScript

Large bundle sizes affect almost all Web Vitals metrics

JavaScript is the biggest bottleneck behind slow apps

Large bundle size will likely cause delays during

load

render

user interaction

page scroll

Optimize Loading JS

Minifying the JavaScript

Compressing the JavaScript using gzip or brotli

Removing unused code via tree shaking

Analyze bundle size with Webpack / Vite

Check lib size with Bundlephobia

Import cost VSC plugin

Use            &           attributes for third-party scripts

async

defer

Code Splitting

import React, { Suspense } from 'react';
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import FirstPage from './components/FirstPage/FirstPage';
import RocketComponent from './components/Rocket/Rocket';

function App() {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/" element={<FirstPage />} />
        <Route
          path="/rocket"
          element={<RocketComponent />}
        />
      </Routes>
    </BrowserRouter>
  );
}

export default App;

Route-based Code Splitting

Route-based Code Splitting

import React, { Suspense, lazy } from 'react';
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import FirstPage from './components/FirstPage/FirstPage';
const RocketLoader = lazy(() => import('./components/RocketLoader/RocketLoader'));

function App() {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/" element={<FirstPage />} />
        <Route
          path="/rocket"
          element={
            <Suspense fallback={<div>Loading...</div>}>
              <RocketLoader />
            </Suspense>
          }
        />
      </Routes>
    </BrowserRouter>
  );
}

export default App;

Component-based Code Splitting

import React, { Suspense, lazy, useRef } from 'react';
import { useVisible } from './hooks/useVisible';
import logo from './assets/images/omega.png';
import astronaut from './assets/images/astronaut.jpg';
import RocketComponent from "./components/Rocket/Rocket";
const RocketLoader = lazy(() => import("./components/RocketLoader/RocketLoader"));

function App() {
  const sectionTwoRef = useRef<HTMLDivElement>();
  const isVisible = useVisible<boolean>(sectionTwoRef);

  return (
    <>
      <div className="section section-first">
        <img src={astronaut} className="astronaut" alt="astronaut" />
      </div>
      <div ref={sectionTwoRef} className="section section-second">
         <RocketComponent />
      </div>
      {isVisible && (
        <Suspense fallback={<div>Loading...</div>}>
          <RocketLoader />
        </Suspense>
      )}
    </>
  );
}

export default App;

Reduce usage of libs

import { useState, useEffect, RefObject } from 'react';

export const useVisible = (
  ref: RefObject<HTMLDivElement>,
  options = {
    root: null,
    rootMargin: '0px',
    threshold: 1.0
  }
) => {
  const [isVisible, setIsVisible] = useState<boolean>(false);

  useEffect(() => {
    const { current: elementRef } = ref;
    
    if (elementRef) {
      const observer = new IntersectionObserver(
        ([entry]) => setIsVisible(entry.isIntersecting), options
      );

      observer.observe(elementRef);

      return () => observer.unobserve(elementRef);
    }
  }, [ref, options]);

  return isVisible;
};

Optimize Loading Images

Use              tag over background-image

Take advantage of next-gen formats

Create responsive images with

Support resolution-switching with

Load key images fast with

fetchpriority

sizes

srcset

<img>

Lazy loading & async decoding

Compress images

.my-pic {
  background-image: url("my-pic.png");
}



<img src="my-pic.png" />
<img src="large.webp" />


<img srcset="small.webp 500w, \ 
             medium.webp 1000w, \
             large.webp 2000w" 
     src="large.webp"
/>
<img src="large.webp" />


<img srcset="small.webp 400w, \ 
             large.webp 900w" 
     sizes="(max-width: 400px) 95vw, \
            (max-width: 900px) 50vw"
     src="large.webp"
/>
<img src="img.webp" />
<img src="bellow-the-fold=img.webp" />



<img src="img.webp" />
<img src="bellow-the-fold=img.webp" 
     loading="lazy" 
     decoding="async" 
/>
<img src="image.png" />
<img src="image.jpg" />


<img src="image.webp" />
<img src="image.avif" />
<img src="above-the-fold-img.webp" />
<img src="img.webp" />



<img src="above-the-fold-img.webp" 
     fetchpriority="high" 
/>
<img src="img.webp" />
import React from "react";
import RocketLoader from "./components/RocketLoader/RocketLoader";
import space from "./assets/images/space-background.jpg";
import galaxy from "./assets/images/galaxy.jpeg";

function App() {
  return (
    <div className="App">
      <img src={galaxy} className="galaxy" />
      <div className="section section-first">
        <img src={space} alt="logo" />
      </div>
      <div className="section section-second">
        <RocketLoader />
      </div>
    </div>
  );
}

export default App;

Progressive image loading (LQIP pattern)

import React from 'react';
import ProImg from './components/ProImg/ProImg';
import RocketLoader from './components/RocketLoader/RocketLoader';
import space from "./assets/images/space-background.jpg";
import galaxy from './assets/images/galaxy.jpeg';
import galaxyLight from './assets/images/galaxy-light.jpeg';

function App() {
  return (
    <div className="App">
      <ProImg
        src={galaxy}
        fallback={galaxyLight}
      />
      <div className="section section-first">
        <img src={space} alt="logo" />
      </div>
      <div className="section section-second">
        <RocketLoader />
      </div>
    </div>
  );
}

export default App;

Progressive image loading (LQIP pattern)

import React from 'react';
import ProImg from './components/ProImg/ProImg';
import RocketLoader from './components/RocketLoader/RocketLoader';
import space from "./assets/images/space-background.jpg";
import galaxy from './assets/images/galaxy.jpeg';
import galaxyLight from './assets/images/galaxy-light.jpeg';

function App() {
  return (
    <div className="App">
      <ProImg
        src={galaxy}
        fallback={galaxyLight}
      />
      <div className="section section-first">
        <img src={space} alt="logo" />
      </div>
      <div className="section section-second">
        <RocketLoader />
      </div>
    </div>
  );
}

export default App;
import React, { useState, FC } from 'react';
import ProImgProps from './ProImgProps'
import './LazyImg.css';

const ProImg = ({ src, className, alt, fallback }: ProImgProps) => {
  const [imgLoaded, setLoaded] = useState<boolean>(false);

  const handleImgLoaded = () => setLoaded(true);

  return (
    <picture className="progressive-img">
      {!imgLoaded && (
        <img
          src={fallback}
          className={`progressive-img__small ${className}`}
          alt={alt}
        />
      )}
      <img 
        src={src} 
        className={`progressive-img__large ${className}`}
        alt={alt} 
        onLoad={handleImgLoaded} 
      />
    </picture>
  );
};

export default ProImg;

Progressive image loading (LQIP pattern)

Optimize Loading Data

Reduce data payload with GraphQL

Prefetch data

<link rel="preload" href="/api/data" as="fetch" crossorigin="anonymous">

Compress data payloads

Paging and filtering

Prefetching data via useSWR

import React, { Suspense, useState } from 'react';
import { BrowserRouter, Routes, Route, Link } from 'react-router-dom';
import { mutate } from 'swr';
import FirstPage from './components/FirstPage/FirstPage';
import LaunchesPage from './components/LaunchesPage/LaunchesPage';
import RocketLoader from './components/RocketLoader/RocketLoader';


function App() {
  
  const handlePrefetch = async () => {
    const response = (await fetch('https://api.nasa.gov/planetary/apod')).json();
    await mutate('https://api.nasa.gov/planetary/apod', response);
  };

  return (
    <Suspense fallback={<RocketLoader />}>
      <BrowserRouter>
        <Routes>
          <Route
            path="/"
            element={
              <FirstPage>
                <Link to="rocket" onMouseEnter={handlePrefetch}>
                 Go to Rocket List Page
                </Link>
              </FirstPage>
            }
          />
          <Route path="/rocket" element={<LaunchesPage />} />
        </Routes>
      </BrowserRouter>
    </Suspense>
  );
}

export default App;
import React, { Suspense, useState } from 'react';
import { BrowserRouter, Routes, Route, Link } from 'react-router-dom';
import { mutate } from 'swr';
import FirstPage from './components/FirstPage/FirstPage';
import LaunchesPage from './components/LaunchesPage/LaunchesPage';
import RocketLoader from './components/RocketLoader/RocketLoader';


function App() {
  
  const handlePrefetch = async () => {
    const response = (await fetch('https://api.nasa.gov/planetary/apod')).json();
    await mutate('https://api.nasa.gov/planetary/apod', response);
  };

  return (
    <Suspense fallback={<RocketLoader />}>
      <BrowserRouter>
        <Routes>
          <Route
            path="/"
            element={
              <FirstPage>
                <Link to="rocket" onMouseEnter={handlePrefetch}>
                 Go to Rocket List Page
                </Link>
              </FirstPage>
            }
          />
          <Route path="/rocket" element={<LaunchesPage />} />
        </Routes>
      </BrowserRouter>
    </Suspense>
  );
}

export default App;
import React, { FC } from 'react';
import { useSWRConfig } from 'swr';
import { Container, Grid, Typography } from '@material-ui/core';
import FlightInfo from '../FlightInfo/FlightInfo';

const LaunchesPage: FC = () => {
  const { cache } = useSWRConfig();
  const flights = cache.get('https://api.nasa.gov/planetary/apod');

  return (
    <Container maxWidth="lg">
      <p>
        Welcome to <span>Svemirko Flights!</span>
      </p>
      <p>Explore Svemirko's recent flights</p>
      <Grid container justifyContent="flex-start" spacing={7}>
        {fligths?.map((flight) => (
          <FlightInfo key={flight.id} flight={flight} />
        ))}
      </Grid>
    </Container>
  );
};

export default LaunchesPage;

Prefetching data via useSWR

Caching

HTTP Cache eliminates network latency

Only affects "safe" HTTP methods

Cache-control headers

no-store

no-cache

max-age

Limited control

stale-while-revalidate

Content-Addressable Storage pattern

Service Workers

More control over cache

Proxy between browser and network

Different kind of scenarios

Offline capabilities + performance

Server Side Rendering

SSR Frameworks

Generate server side data at runtime

Generate server side data at runtime

Nested components with router outlet

Layouts

Stale While Revalidate cache control

Stale While Revalidate cache control

Prefetching via <Head> & <Link> components

Prefetching

Edge Computing

0kb JavaScript

Resumability

Code splitting + preloading

Lazy creation of event handlers

Serialized data

Islands architecture

Server-first

Zero JS, by default

UI agnostic

0kb JavaScript

Server side components

Serialized data

Component-specific data processing

Streams the result of data fetching

Best performance hack ever

setTimeout(() => {}, 5000);

With great power comes great responsibility

Green JavaScript

Internet is consuming 21% of the electricity

check out carbon footprint

website traffic

energy intensity

data transfers

Thank you!

n.mitrovic@vegait.rs

You can find me at

Link to the slides