Network Optimization

Techniques

  • Lazy loading
  • Loading javascript in async
  • Content visibility
  • Serving critical css
  • Resource hints
  • Caching using service worker
  • Caching using CDN
  • CSR, SSR
  • Compression techniques
  • Layout Shifts & repaints

Loading javascript techniques

<head>
    <!-- Critical UI framework -->
    <script src="vue.js"></script>
    
    <!-- Main app code can wait -->
    <script defer src="app.js"></script>
    
    <!-- Analytics don't block anything -->
    <script async src="analytics.js"></script>
    
    <!-- Modules are automatically deferred -->
    <script type="module" src="features.js"></script>
</head>
Normal script loading vs Async vs Defer

Lazy Loading

loading Attribute

<img src="example.jpg" loading="lazy" alt="An example image">
<img src="example.jpg" loading="eager" alt="An example image">

The loading attribute specifies whether a browser should load the image immediately or defer loading until it reaches a certain point in the viewport. This is particularly useful for improving page performance and loading times.

Values:

  • auto: The browser decides when to load the image based on its own heuristics (default behavior).
  • lazy: The image will only load when it is close to being in the viewport. This helps reduce the amount of data downloaded on initial page load, especially for pages with many images.
  • eager: The image will be loaded as soon as possible, regardless of whether it is currently in the viewport. This is useful for images that are critical to the user experience, such as hero images.

fetchpriority Attribute

<img src="important-image.jpg" fetchpriority="high" alt="An important image">

The fetchpriority attribute allows developers to indicate the importance of an image fetch request to the browser, helping to prioritize which resources to load first. This can be especially beneficial for optimizing critical resources and enhancing the perceived performance of a website.

Values:

  • auto: The browser will automatically determine the priority of the image load (default behavior).
  • high: The image should be prioritized and fetched earlier in the loading process. This is typically used for images that are critical for rendering the content or layout.
  • low: The image can be fetched later in the loading process. This is suitable for images that are not immediately necessary for user experience, such as decorative images.

Use Cases

<img src="hero-image.jpg" loading="eager" fetchpriority="high" alt="Hero image">
<img src="background-image.jpg" loading="lazy" fetchpriority="low" alt="Background image">
  1. Lazy Loading: For a gallery of images, using loading="lazy" allows images to load only when they are about to enter the viewport, improving the initial load time of the page.
  2. Eager Loading: For images above the fold (the portion of the webpage visible without scrolling), setting loading="eager" and fetchpriority="high" ensures they load quickly.
  3. Performance Optimization: By utilizing fetchpriority, developers can guide the browser to prioritize essential images, reducing perceived load time and improving the user experience.

Intersection Observer

The Intersection Observer API in JavaScript allows you to efficiently observe changes in the intersection of a target element with a root element (or the viewport by default). This is particularly useful for lazy-loading images, infinite scrolling, and triggering animations when elements come into view.

Basic Usage of Intersection Observer Create an Intersection Observer instance. Define the callback function to handle visibility changes. Observe the target element(s).

const options = {
  root: document.querySelector("#scrollArea"),
  rootMargin: "0px",
  threshold: 1.0,
};

const observer = new IntersectionObserver(callback, options);

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Intersection Observer Example</title>
<style>
  .box {
    width: 100%;
    height: 200px;
    margin: 50px 0;
    background-color: lightblue;
  }
  .observed {
    background-color: lightgreen;
  }
</style>
</head>
<body>
  <div class="box"></div>
  <div class="box"></div>
  <div class="box"></div>
  <div class="box observed">Observe me!</div>
  <div class="box"></div>
  <div class="box"></div>
  <script>
    // 1. Callback function when intersection changes
    const callback = (entries, observer) => {
      entries.forEach(entry => {
        if (entry.isIntersecting) {
          entry.target.style.backgroundColor = 'orange';
          console.log('Element is in view:', entry.target);
          observer.unobserve(entry.target); // Stop observing once it's in view
        }
      });
    };

    // 2. Create the observer instance with options
    const options = {
      root: null, // Use the viewport as the root
      rootMargin: '0px',
      threshold: 0.5 // 50% of the element must be in view
    };

    const observer = new IntersectionObserver(callback, options);

    // 3. Observe the target element
    const target = document.querySelector('.observed');
    observer.observe(target);
  </script>
</body>
</html>

Content Visibility

The content-visibility CSS property is a powerful tool for optimizing the rendering performance of a webpage. It allows the browser to skip rendering elements that are not visible to the user, improving performance for large, complex pages.

  1. content-visibility: visible Default Value.
    The element and its content are fully rendered. No performance optimization is applied.
    .my-element {
        content-visibility: visible;
    }
    
  2. content-visibility: hidden
    The content of the element is not rendered or painted. The element still occupies space in the layout (it acts similar to visibility: hidden).
    .my-element {
        content-visibility: hidden;
    }
    
  3. content-visibility: auto
    The most powerful and commonly used value. The browser skips rendering the element's content until it is near the viewport (scrollable area). Great for improving performance with long pages or elements below the fold.
    .my-element {
        content-visibility: auto;
    }
    

Optional Enhancements with content-visibility

  1. contain-intrinsic-size (Used with content-visibility: auto)
    When content-visibility: auto is used, the browser does not know the size of the element until it is visible. To prevent layout shifts, use contain-intrinsic-size to specify an estimated size.
    .my-element {
        content-visibility: auto;
        contain-intrinsic-size: 500px; /* Set an estimated height or width */
    }
    
  2. content-visibility: hidden-matchable (Experimental)
    This value is still experimental and is used for search and accessibility purposes. It hides the content from rendering but makes it available for search indexing and accessibility tools like screen readers. Practical Use Cases Lazy Rendering for Long Content
    .article-section {
        content-visibility: auto;
        contain-intrinsic-size: 800px; /* Estimated height for each section */
    }
    

    This improves the initial load time, as the browser will only render sections of the article when they are near the viewport.
    Enhancing Performance for Large Lists
    .item {
        content-visibility: auto;
        contain-intrinsic-size: 100px;
    }
    

    Use content-visibility: auto for list items that may not be visible initially, reducing the initial render cost. Browser Support Supported in most modern browsers like Chrome, Edge, and Firefox. Not supported in Internet Explorer. Check compatibility before using it extensively, as it is a relatively newer addition to CSS.

Summary

The content-visibility property, especially with the auto value, is a great way to boost performance by deferring rendering of off-screen content. It complements other performance optimizations like lazy loading of images and infinite scrolling.

Serving critical css

To load CSS based on media type or media conditions (like screen size, orientation, or device characteristics), you can use media queries in two primary ways:

  1. Using the Element with media Attribute You can specify the CSS file to be loaded based on the media type or condition using the media attribute in the element.
<!-- Load styles only for screen devices (not print) -->
<link rel="stylesheet" href="screen.css" media="screen">

<!-- Load styles for devices with a minimum width of 600px -->
<link rel="stylesheet" href="desktop.css" media="screen and (min-width: 600px)">

<!-- Load styles only for printing -->
<link rel="stylesheet" href="print.css" media="print">

Explanation:

  • media="screen": Loads the CSS file only on screen devices like computers and mobile phones.
  • media="print": Loads the CSS file only when the document is being printed.
  • media="screen and (min-width: 600px)": Loads the CSS file only if the screen width is at least 600px.
  1. Using Media Queries Inside CSS Files You can include media queries directly inside a CSS file to apply specific styles based on media conditions.

Example

/* General styles */
body {
  font-family: Arial, sans-serif;
}

/* Styles for screen devices with a maximum width of 768px (tablets, mobile) */
@media screen and (max-width: 768px) {
  body {
    background-color: lightblue;
  }
}

/* Styles for print */
@media print {
  body {
    background-color: white;
    color: black;
  }
}
  1. Dynamically Loading CSS via JavaScript If you want to load a CSS file based on media conditions dynamically using JavaScript, you can do this by checking the window.matchMedia API.
<script>
  if (window.matchMedia("(min-width: 768px)").matches) {
    const link = document.createElement("link");
    link.rel = "stylesheet";
    link.href = "desktop.css";
    document.head.appendChild(link);
  } else {
    const link = document.createElement("link");
    link.rel = "stylesheet";
    link.href = "mobile.css";
    document.head.appendChild(link);
  }
</script>

Using Media Types and Features Together You can also combine media types and features:

<!-- Load a stylesheet for print when the color scheme is dark -->
<link rel="stylesheet" href="dark-print.css" media="print and (prefers-color-scheme: dark)">

Common Media Types:

  • all: Suitable for all devices.
  • screen: Intended for computer screens, tablets, and smartphones.
  • print: Intended for paged material and documents viewed on a screen in print preview mode.

Common Media Features:

  • (min-width), (max-width): Based on viewport width.
  • (orientation: portrait) or (orientation: landscape): Based on the orientation of the device.
  • (prefers-color-scheme: dark): Based on user preference for dark mode.
  • (hover: none): For touch devices that don't support hover.

Summary

Loading CSS based on media type or media queries can help optimize the user experience by applying specific styles only when necessary, reducing unnecessary CSS loads, and improving performance.

Resource Hinting

  • preload
    • The preload attribute is used in HTML to indicate that certain resources (like CSS, JavaScript, fonts, images, etc.) should be loaded early in the loading process, even before they are needed. This can help improve performance by reducing the time to first paint or first contentful paint. Common Use Cases for preload
      • Preloading CSS Files
      • Preloading Fonts
      • Preloading JavaScript Files
      • Preloading Images
      • Preloading Videos

      Important: as Attribute
      The as attribute specifies the type of content being preloaded. This is crucial because it allows the browser to handle the resource correctly. Here are common values for the as attribute:
      • as="style": For CSS files.
      • as="script": For JavaScript files.
      • as="font": For fonts.
      • as="image": For images.
      • as="video": For videos.
      • as="audio": For audio files.
      <link rel="preload" href="styles.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
      

      onload="this.onload=null;this.rel='stylesheet'": This snippet ensures that the preload link is converted to a regular stylesheet link after loading, allowing it to apply the styles.
  • prefetch

The prefetch attribute is used to anticipate future navigation and fetch resources that might be needed later. Unlike preload, which is meant for critical resources needed during the initial page load, prefetch is intended for resources that are not immediately needed but might be used in the near future.

This can help improve performance and reduce waiting times when users navigate to a new page or perform an action that requires additional assets.

<link rel="prefetch" href="next-page-styles.css" as="style">

Key Differences Between preload and prefetch

Preload vs Prefetch
  • preconnect / dns prefetch (supported for old browsers same as preconnect)
    • The preconnect attribute is used in HTML to establish early connections to external origins (domains). It helps reduce latency by performing DNS lookups, TLS handshakes, and TCP handshakes in advance, even before any actual resource is requested from that origin.

Using preconnect can be particularly beneficial for optimizing performance when your webpage relies on external resources like:

  • CDNs (Content Delivery Networks) for assets (images, scripts, fonts)
  • Third-party APIs
  • Analytics and tracking services
  • Fonts from services like Google Fonts
  • dns-prefetch
  • prerender
    The prerender attribute is used to preload an entire webpage in the background, anticipating that the user might navigate to it next. When the user decides to navigate to the pre-rendered page, it appears instantly because the page has already been fully loaded and rendered in the background.
    Use Cases:
    • Anticipating user navigation (e.g., when the user is likely to click a specific link).
    • Speeding up navigation in single-page applications (SPAs).
    • Enhancing user experience by making pages appear faster.
    <link rel="prerender" href="https://example.com/next-page">
    
  • module preload
    The modulepreload attribute is a way to preload JavaScript modules in HTML. It helps the browser fetch and cache module files before they are needed, reducing the loading time and improving performance, especially for JavaScript applications that use the ES module system (
    When you use ES modules, the browser must fetch and evaluate each module file and its dependencies. modulepreload allows the browser to fetch these files in parallel, even before the module script is executed, optimizing the loading process.

Why Use modulepreload?

  • Performance: Reduces the delay caused by module fetching and parsing, especially for large applications with deep dependency trees.
  • Better Caching: Enables the browser to cache module files early.
  • Improves Initial Load: By preloading dependencies, it reduces the blocking time when the main module script is executed.
    <link rel="modulepreload" href="path/to/module.js">
    
Modulepreload vs Preload

Client Side Rendering, Server Side Rendering, Static Side Rendering, Prerendering

Metric

Framework Support

  • Next.js
    • Supports all strategies
    • Built-in ISR
    • Automatic static optimization
    • API routes
    • Middleware support
  • Nuxt.js
    • Similar capabilities to Next.js
    • Vue.js based
    • Universal rendering
    • Static site generation
    • Dynamic imports
  • Gatsby
    • Static-first approach
    • GraphQL layer
    • Rich plugin ecosystem
    • Image optimization
    • Progressive enhancement
  • Remix
    • SSR-focused
    • Nested routing
    • Built-in loading states
    • Error boundary handling
    • Form abstractions

Compression