<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Carles Núñez</title>
    <description>The latest articles on Forem by Carles Núñez (@carlesnunez).</description>
    <link>https://forem.com/carlesnunez</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F380301%2F0dc9d755-5a4f-4e91-917c-78769c34ee9e.png</url>
      <title>Forem: Carles Núñez</title>
      <link>https://forem.com/carlesnunez</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/carlesnunez"/>
    <language>en</language>
    <item>
      <title>Deep dive into lazy loading images 🖼</title>
      <dc:creator>Carles Núñez</dc:creator>
      <pubDate>Sat, 30 May 2020 16:16:27 +0000</pubDate>
      <link>https://forem.com/carlesnunez/deep-dive-into-lazy-loading-images-211f</link>
      <guid>https://forem.com/carlesnunez/deep-dive-into-lazy-loading-images-211f</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;🟠 This article was written 4 years ago and some things like the thresholds have been improved.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The first question is... why?
&lt;/h2&gt;

&lt;p&gt;In the current web app world saving time and network when a user enters into our webpage means a higher chance of increasing engagement and a big opportunity to have a better user experience. Trust me when I say that in the most of the cases we are wasting a bunch of resources when our user loads a webpage. Resources like, for example network bandwidth.&lt;/p&gt;

&lt;p&gt;There's no need to be an expert to realise that if one of the biggest problems in web development is wasting resources the solution could be stopping our user mobiles and computers from wasting them, right?&lt;/p&gt;

&lt;h2&gt;
  
  
  Don't load more than what you need
&lt;/h2&gt;

&lt;p&gt;This is a concept that not only come from web development but from game development and in this field they call it &lt;strong&gt;Viewing-frustum culling&lt;/strong&gt; which, according to wikipedia, is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;the process of removing objects that lie completely outside the viewing frustum from the rendering process. Rendering these objects would be a waste of time since they are not directly visible.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;if we translate this sentence to the web development environment we could see that our viewing frustum is the above-the-fold of our webpage.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk0pfn8si8ssjngfxj0b8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk0pfn8si8ssjngfxj0b8.png" alt="Imgur" width="800" height="1054"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why in my opinion native lazy-loading is not an option
&lt;/h2&gt;

&lt;p&gt;Starting from chrome 76 you are able to use the loading attribute to lazy-load resources without the need to write custom lazy-loading code or use a separate JavaScript library. This was my approach the first time that I implemented an image lazy load strategy on a site but after implementing the code.... nothing was happening. Why?&lt;/p&gt;

&lt;p&gt;In order to understand better what was happening I decided to deep dive into chromium code and understand better how chromium engineers where implementing their lazy load solution in order to understand what I was doing wrong.&lt;/p&gt;

&lt;h3&gt;
  
  
  How does native lazy load works?
&lt;/h3&gt;

&lt;p&gt;The browser will call the next function in order to initialise image monitoring for lazy load &lt;a href="https://github.com/chromium/chromium/blob/d1f1b29e2ea972f93a5007aefc7ad911931c8ab6/third_party/blink/renderer/core/loader/lazy_image_helper.cc#L77" rel="noopener noreferrer"&gt;check the code here&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;LazyImageHelper&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;StartMonitoring&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;blink&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Element&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;element&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;Document&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;document&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;GetRootDocumentOrNull&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;element&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;document&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Getting messages in order to perform console.log operations latter if an attribute is not ok.&lt;/span&gt;
  &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="n"&gt;DeferralMessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;LazyLoadImageObserver&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;DeferralMessage&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;deferral_message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DeferralMessage&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;kNone&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;auto&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;html_image&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ToHTMLImageElementOrNull&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;element&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Get loading att value, it can be eager, lazy auto or nothing.&lt;/span&gt;
    &lt;span class="n"&gt;LoadingAttrValue&lt;/span&gt; &lt;span class="n"&gt;loading_attr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;GetLoadingAttrValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;html_image&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;DCHECK_NE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;loading_attr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LoadingAttrValue&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;kEager&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;loading_attr&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;LoadingAttrValue&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;kAuto&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;deferral_message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DeferralMessage&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;kLoadEventsDeferred&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="nf"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;IsDimensionAbsoluteLarge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;html_image&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;DCHECK_EQ&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;loading_attr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LoadingAttrValue&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;kLazy&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="n"&gt;deferral_message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DeferralMessage&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;kMissingDimensionForLazy&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Here is where all start: Call the lazy load image observer and start monitoring&lt;/span&gt;
  &lt;span class="n"&gt;document&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;EnsureLazyLoadImageObserver&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;StartMonitoringNearViewport&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="n"&gt;document&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;element&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;deferral_message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://github.com/endlessm/chromium-browser/blob/7c4c6df2a8f8f76fb570d8f6a85b120c0c168c38/third_party/blink/renderer/core/html/lazy_load_image_observer.cc#L172" rel="noopener noreferrer"&gt;This code snippet&lt;/a&gt; lead to the &lt;code&gt;StartMonitoringNearViewport&lt;/code&gt; function which does the next:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;LazyLoadImageObserver&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;StartMonitoringNearViewport&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;Document&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;root_document&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Element&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;element&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;DeferralMessage&lt;/span&gt; &lt;span class="n"&gt;deferral_message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;DCHECK&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RuntimeEnabledFeatures&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;LazyImageLoadingEnabled&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;lazy_load_intersection_observer_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// 1&lt;/span&gt;
    &lt;span class="n"&gt;lazy_load_intersection_observer_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;IntersectionObserver&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Length&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Fixed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;GetLazyImageLoadingViewportDistanceThresholdPx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;root_document&lt;/span&gt;&lt;span class="p"&gt;))},&lt;/span&gt; &lt;span class="c1"&gt;// 2&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;numeric_limits&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;::&lt;/span&gt;&lt;span class="n"&gt;min&lt;/span&gt;&lt;span class="p"&gt;()},&lt;/span&gt; &lt;span class="n"&gt;root_document&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;WTF&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;BindRepeating&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;LazyLoadImageObserver&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;LoadIfNearViewport&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// 3&lt;/span&gt;
                           &lt;span class="n"&gt;WrapWeakPersistent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In order to follow the flow I've put numbers on some lines that I'll explain below.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What does this code exactly do?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;1 - They check if an intersection observer has been created before, otherwise they create it.&lt;/p&gt;

&lt;p&gt;Don't you see? They use the &lt;strong&gt;same implementation&lt;/strong&gt; on lazy loading images &lt;strong&gt;natively&lt;/strong&gt; &lt;strong&gt;as&lt;/strong&gt; with a javascript &lt;strong&gt;library&lt;/strong&gt; but using low-level intersection observer API, isn't it amazing? 🙂&lt;/p&gt;

&lt;p&gt;2 - Calling &lt;code&gt;GetLazyLoadImageLoadingViewportDistanceThresholdPX&lt;/code&gt; : this function will get the needed threshold to load images based on the network that you are using.&lt;/p&gt;

&lt;p&gt;Here you have the code implementation but if you don't care about implementation you can jump directly to the table below for more info about thresholds:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;GetLazyImageLoadingViewportDistanceThresholdPx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;Document&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;document&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;Settings&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetSettings&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GetNetworkStateNotifier&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;EffectiveType&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;WebEffectiveConnectionType&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;kTypeUnknown&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;GetLazyImageLoadingDistanceThresholdPxUnknown&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;WebEffectiveConnectionType&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;kTypeOffline&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;GetLazyImageLoadingDistanceThresholdPxOffline&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;WebEffectiveConnectionType&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;kTypeSlow2G&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;GetLazyImageLoadingDistanceThresholdPxSlow2G&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;WebEffectiveConnectionType&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;kType2G&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;GetLazyImageLoadingDistanceThresholdPx2G&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;WebEffectiveConnectionType&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;kType3G&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;GetLazyImageLoadingDistanceThresholdPx3G&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;WebEffectiveConnectionType&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;kType4G&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;GetLazyImageLoadingDistanceThresholdPx4G&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;NOTREACHED&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So according to the &lt;a href="https://github.com/chromium/chromium/blob/ccd149af47315e4c6f2fc45d55be1b271f39062c/third_party/blink/renderer/core/frame/settings.json5#L963" rel="noopener noreferrer"&gt;native configuration json5&lt;/a&gt; code we can see that regarding on our internet connexion we will have one or another threshold but this threshold will be &lt;strong&gt;always &amp;gt;= 3000px&lt;/strong&gt; which honestly is &lt;strong&gt;alot&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Network&lt;/th&gt;
&lt;th&gt;Threshold&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Slow 2g&lt;/td&gt;
&lt;td&gt;8000px&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2g&lt;/td&gt;
&lt;td&gt;6000px&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3g&lt;/td&gt;
&lt;td&gt;4000px&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4g&lt;/td&gt;
&lt;td&gt;3000px&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Offline&lt;/td&gt;
&lt;td&gt;8000px&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Unknown&lt;/td&gt;
&lt;td&gt;5000px&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;3 - And finally, will call the 'callback' function which will do the next (&lt;a href="https://github.com/endlessm/chromium-browser/blob/7c4c6df2a8f8f76fb570d8f6a85b120c0c168c38/third_party/blink/renderer/core/html/lazy_load_image_observer.cc#L215" rel="noopener noreferrer"&gt;check full snippet&lt;/a&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;LazyLoadImageObserver&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;LoadIfNearViewport&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;HeapVector&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Member&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IntersectionObserverEntry&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;DCHECK&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsEmpty&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;entry&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Element&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;element&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;auto&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;image_element&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DynamicTo&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HTMLImageElement&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;element&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// If the loading_attr is 'lazy' explicitly, we'd better to wait for&lt;/span&gt;
    &lt;span class="c1"&gt;// intersection.&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;isIntersecting&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;image_element&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
        &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;EqualIgnoringASCIICase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;image_element&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;FastGetAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;html_names&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;kLoadingAttr&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s"&gt;"lazy"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Fully load the invisible image elements. The elements can be invisible&lt;/span&gt;
      &lt;span class="c1"&gt;// by style such as display:none, visibility: hidden, or hidden via&lt;/span&gt;
      &lt;span class="c1"&gt;// attribute, etc. Style might also not be calculated if the ancestors&lt;/span&gt;
      &lt;span class="c1"&gt;// were invisible.&lt;/span&gt;
      &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;ComputedStyle&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;style&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;GetComputedStyle&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;style&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;style&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Visibility&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;EVisibility&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;kVisible&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
          &lt;span class="n"&gt;style&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Display&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;EDisplay&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;kNone&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Check that style was null because it was not computed since the&lt;/span&gt;
        &lt;span class="c1"&gt;// element was in an invisible subtree.&lt;/span&gt;
        &lt;span class="n"&gt;DCHECK&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;style&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;IsElementInInvisibleSubTree&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;element&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="n"&gt;image_element&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;LoadDeferredImage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;lazy_load_intersection_observer_&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;unobserve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;element&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;isIntersecting&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
      &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;image_element&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;image_element&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;LoadDeferredImage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Load the background image if the element has one deferred.&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;ComputedStyle&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;style&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;element&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;GetComputedStyle&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
      &lt;span class="n"&gt;style&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;LoadDeferredImages&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;element&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;GetDocument&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

    &lt;span class="n"&gt;lazy_load_intersection_observer_&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;unobserve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;element&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can check others point of view regarding this topic &lt;a href="https://css-tricks.com/native-image-lazy-loading-in-chrome-is-way-too-eager/" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  So you say that I should use a JS library but... which one?
&lt;/h2&gt;

&lt;p&gt;Taking the web.dev article &lt;a href="https://developers.google.com/web/fundamentals/performance/lazy-loading-guidance/images-and-video" rel="noopener noreferrer"&gt;Lazy Loading Images and Video&lt;/a&gt; I invested a bit of time into analysing the different options that we have and the pros and cons of some of them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Analysing the state of the art
&lt;/h3&gt;

&lt;p&gt;First of all I checked which solutions we currently have on the market based on web.dev recommendations, how well maintained and how much popularity they have on the community. &lt;/p&gt;

&lt;p&gt;We have 4 recommendations and all of them rely on IntersectionObserver API to perform their work.&lt;/p&gt;

&lt;p&gt;I will analyse them using five metrics:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Stars&lt;/li&gt;
&lt;li&gt;Releases&lt;/li&gt;
&lt;li&gt;Public repos using it&lt;/li&gt;
&lt;li&gt;Contributors&lt;/li&gt;
&lt;li&gt;Library size&lt;/li&gt;
&lt;li&gt;NPM Download trend&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Github&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Library name&lt;/th&gt;
&lt;th&gt;⭐️ Stars&lt;/th&gt;
&lt;th&gt;🚀 Releases&lt;/th&gt;
&lt;th&gt;📦 Used by&lt;/th&gt;
&lt;th&gt;👥 Contributors&lt;/th&gt;
&lt;th&gt;🏋🏽‍♂️ Size&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/ApoorvSaxena/lozad.js" rel="noopener noreferrer"&gt;Lozad&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;6.2k&lt;/td&gt;
&lt;td&gt;17&lt;/td&gt;
&lt;td&gt;1.5k&lt;/td&gt;
&lt;td&gt;31&lt;/td&gt;
&lt;td&gt;&lt;a href="https://bundlephobia.com/result?p=lozad@1.14.0" rel="noopener noreferrer"&gt;1kb&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/dinbror/blazy" rel="noopener noreferrer"&gt;Blazy&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;2.6k&lt;/td&gt;
&lt;td&gt;19&lt;/td&gt;
&lt;td&gt;541&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;&lt;a href="https://bundlephobia.com/result?p=blazy@1.8.2" rel="noopener noreferrer"&gt;1.9kb&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/malchata/yall.js/" rel="noopener noreferrer"&gt;Yall&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;1k&lt;/td&gt;
&lt;td&gt;13&lt;/td&gt;
&lt;td&gt;69&lt;/td&gt;
&lt;td&gt;13&lt;/td&gt;
&lt;td&gt;&lt;a href="https://bundlephobia.com/result?p=blazy@1.8.2" rel="noopener noreferrer"&gt;1kb&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/aFarkas/lazysizes" rel="noopener noreferrer"&gt;Lazy Sizes&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;13.3k&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;11.2k&lt;/td&gt;
&lt;td&gt;38&lt;/td&gt;
&lt;td&gt;&lt;a href="https://bundlephobia.com/result?p=lazysizes@5.2.0" rel="noopener noreferrer"&gt;3.3kb&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;NPM Trends&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm4gqywj6wrp2g90qyy1z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm4gqywj6wrp2g90qyy1z.png" alt="https://i.imgur.com/9dglETK.png" width="800" height="293"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It seems that lazysizes is the most community supported library, but is also the heaviest so I'm going to select for my tests and benchmarks TWO of the libraries.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Lazysizes&lt;/li&gt;
&lt;li&gt;Lozad&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Field test
&lt;/h3&gt;

&lt;p&gt;In order to check which library have a better API I decided to perform a small test on a codesandbox site and check how each implementation behave.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lozad:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useEffect&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;lozad&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lozad&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;other&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;observe&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;lozad&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;observe&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;img&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"lozad"&lt;/span&gt; &lt;span class="na"&gt;data-src&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;other&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Lozad&lt;/strong&gt; uses a className as an identifier for the library in order to replace the data-src by a real src attribute in order to load the image.&lt;/p&gt;

&lt;p&gt;It also uses an observe function in order to observe the element. The observe function is a function that will mark elements as loaded, so multiple calls to that function shouldn't affect performance at all. You can check the code implementation of that function on load.js source code - &lt;a href="https://github.com/ApoorvSaxena/lozad.js/blob/master/src/lozad.js#L121" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;LazySizes:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lazysizes&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lazysizes/plugins/attrchange/ls.attrchange&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;other&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;img&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"lazyload"&lt;/span&gt; &lt;span class="na"&gt;data-src&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;other&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;LazySizes have a similar api to lozad but you don't need to call observe function, it will be called automatically on import. On the other side if you perform data-src changes dynamically you have to add a plugin that will watch the data-src value so if it changes it will re-trigger the image load function.&lt;/p&gt;

&lt;p&gt;More info about ls.attrchange &lt;a href="https://github.com/aFarkas/lazysizes/tree/gh-pages/plugins/attrchange" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Summary: The good and the bad&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Lozad &lt;strong&gt;PROS&lt;/strong&gt; 👍&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Lozad is a really tiny library (only 1kb!)&lt;/li&gt;
&lt;li&gt;Lozad is really easy to use and give us autonomy on calling observe and unobserve methods&lt;/li&gt;
&lt;li&gt;It loads only what needs to load with the default threshold (2 images on mobile)&lt;/li&gt;
&lt;li&gt;It is configurable&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Lozad &lt;strong&gt;CONS&lt;/strong&gt; 👎&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Running the observable on each component is not something that I like and even is not a problem on performance, I wouldn't like to have a lozad.observe outside the lazy image component definition, the solution must be provided as it is, with no extra work.&lt;/li&gt;
&lt;li&gt;They are not clear on if the library is SEO compliant and this is a problem if you care about SEO - &lt;a href="https://github.com/aFarkas/lazysizes/issues/436" rel="noopener noreferrer"&gt;more info here&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;LazySizes &lt;strong&gt;PROS&lt;/strong&gt; 👍&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The api is really easy to use&lt;/li&gt;
&lt;li&gt;The community behind is huge&lt;/li&gt;
&lt;li&gt;It is the library recommended by google&lt;/li&gt;
&lt;li&gt;It is fully SEO compliant&lt;/li&gt;
&lt;li&gt;It has the possibility of extending its capacity with plugins &lt;a href="https://github.com/aFarkas/lazysizes/tree/gh-pages/plugins" rel="noopener noreferrer"&gt;check here&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;It is also configurable&lt;/li&gt;
&lt;li&gt;It works right out of the box you only need to import the library&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;LazySizes &lt;strong&gt;CONS&lt;/strong&gt; 👎&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Library size is the triple of lozad&lt;/li&gt;
&lt;li&gt;If you want to configure it you have to put a config object on the window which is not so elegant.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;General tradeoff to consider if you care about SSR&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We are lazy loading images using a library that is imported and consumed on our bundles, this means that we loose the SSR power on images as this JS code must be loaded in order to show images on first render. But it shouldn't be a problem at least that you have a big amount of JS to load on your bundle.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusions
&lt;/h2&gt;

&lt;p&gt;In my opinion in this case the community and google have picked the correct library to trust on, lazy sizes have slight differences that give us the balance between size, usability and maintainability.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;My recomendation is give lazysizes an opportunity and test its viability.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Head Photo by Kate Stone Matheson on Unsplash&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>lazyload</category>
      <category>webdev</category>
      <category>webperf</category>
    </item>
  </channel>
</rss>
