<?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>DEV Community: M4rrc0</title>
    <description>The latest articles on DEV Community by M4rrc0 (@m4rrc0).</description>
    <link>https://dev.to/m4rrc0</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%2F305812%2Fd9279f00-2fe3-4d55-9bbc-dff1635baef8.jpeg</url>
      <title>DEV Community: M4rrc0</title>
      <link>https://dev.to/m4rrc0</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/m4rrc0"/>
    <language>en</language>
    <item>
      <title>Lazy loading images, so easy you won’t believe it (a native strategy)</title>
      <dc:creator>M4rrc0</dc:creator>
      <pubDate>Fri, 10 Jun 2022 06:47:27 +0000</pubDate>
      <link>https://dev.to/m4rrc0/lazy-loading-images-so-easy-you-wont-believe-it-a-native-strategy-2pcp</link>
      <guid>https://dev.to/m4rrc0/lazy-loading-images-so-easy-you-wont-believe-it-a-native-strategy-2pcp</guid>
      <description>&lt;h2&gt;
  
  
  tl;dr
&lt;/h2&gt;

&lt;p&gt;The code is pretty easy to grasp. Read the comments and you should be good to go.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"img-lazy-wrapper"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; &lt;span class="c"&gt;&amp;lt;!-- We need a wrapper for the placeholder while image lazy loads --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt;
    &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/assets/lazy-bear@528w.96b21f25.png"&lt;/span&gt; &lt;span class="err"&gt;&amp;lt;!&lt;/span&gt;&lt;span class="na"&gt;--&lt;/span&gt; &lt;span class="na"&gt;Compress&lt;/span&gt; &lt;span class="na"&gt;your&lt;/span&gt; &lt;span class="na"&gt;image&lt;/span&gt; &lt;span class="na"&gt;--&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    alt="A lazy bear" &lt;span class="c"&gt;&amp;lt;!-- Don't forget the alt attribute --&amp;gt;&lt;/span&gt;
    loading="lazy" &lt;span class="c"&gt;&amp;lt;!-- Native browser lazy loading --&amp;gt;&lt;/span&gt;
    width="528" &lt;span class="c"&gt;&amp;lt;!-- Specify width and height so the browser can allocate the appropriate space for the image --&amp;gt;&lt;/span&gt;
    height="560"
    class="img-lazy"
    onload="this.parentNode.style.backgroundColor = 'transparent';this.style.opacity = 1;"&amp;gt;
        &lt;span class="c"&gt;&amp;lt;!-- Transition: Fade the image in and the background out --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
&lt;span class="nc"&gt;.img-lazy&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c"&gt;/* Start with image hidden */&lt;/span&gt;
  &lt;span class="nl"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;opacity&lt;/span&gt; &lt;span class="m"&gt;1s&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c"&gt;/* Transition to make the appearance smoother */&lt;/span&gt;
  &lt;span class="nl"&gt;max-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c"&gt;/* Avoid image punching out of the screen  */&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c"&gt;/* Necessary to keep aspect ratio if width is reduced by max-width */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.img-lazy-wrapper&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;-webkit-fit-content&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;-moz-fit-content&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;fit-content&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c"&gt;/* Make the wrapper borders stick to the image. Otherwise, would stretch horizontally */&lt;/span&gt;
  &lt;span class="nl"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;background-color&lt;/span&gt; &lt;span class="m"&gt;1s&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c"&gt;/* Make disappearance smoother */&lt;/span&gt;
  &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--color-img-placeholder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0.3&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="c"&gt;/* Basic placeholder */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;noscript&amp;gt;&amp;lt;style&amp;gt;&lt;/span&gt;
&lt;span class="c"&gt;/* Overrides opacity and background-color in case we have no JS to get them it back */&lt;/span&gt;
&lt;span class="nc"&gt;.img-lazy&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.img-lazy-wrapper&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;transparent&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&amp;lt;/noscript&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Intro
&lt;/h2&gt;

&lt;p&gt;Us developers really like to reinvent the wheel. Lazy loading images has been a topic of choice to do that.&lt;/p&gt;

&lt;p&gt;Nevertheless, we couldn't easily find an elegant, native, lightweight solution we like to implement by default on &lt;strong&gt;&lt;a href="https://poko.m4rr.co/"&gt;poko&lt;/a&gt;&lt;/strong&gt;. So here is our solution for now.&lt;/p&gt;

&lt;h2&gt;
  
  
  Our prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Use the platform as much as possible&lt;/li&gt;
&lt;li&gt;Should provide a minimum viable experience on older browsers and without javascript&lt;/li&gt;
&lt;li&gt;Progressively enhanced (image placeholder while loading, transitions, ...)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A solution
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Compress images&lt;/li&gt;
&lt;li&gt;Determine the dimensions of each image&lt;/li&gt;
&lt;li&gt;Activate native lazy loading in browser&lt;/li&gt;
&lt;li&gt;Progressively enhance with some styles and two lines of javaScript.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  1. Compress images
&lt;/h3&gt;

&lt;p&gt;There are many ways to do it and your development pipeline might have a mature native solution already.&lt;/p&gt;

&lt;p&gt;If you are not a tech person and you are not sure your CMS or website builder handles it for you, there are many tools online to compress images.&lt;/p&gt;

&lt;p&gt;In our case, using &lt;a href="https://astro.build/"&gt;Astro&lt;/a&gt;, we simply used &lt;a href="https://github.com/RafidMuhymin/astro-imagetools"&gt;&lt;code&gt;astro-imagetools&lt;/code&gt;&lt;/a&gt; to compress images automatically while importing them. We might improve this later, for example generating multiple image sizes and formats, reducing the load even further for modern browsers. Currently, it checks our box though.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Determine the dimensions of each image
&lt;/h3&gt;

&lt;p&gt;This is necessary to make use of some native browser image handling. Providing &lt;code&gt;width&lt;/code&gt; and &lt;code&gt;height&lt;/code&gt; attributes to &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tags allows the browser to reserve space for your image before it loads. It is necessary to avoid having content jump around when images load. It used to not be enough to set these up but now it is.&lt;/p&gt;

&lt;p&gt;Again, there are multiple ways to get dimensions and your development pipeline might just provide this information by default. In case it doesn't, we have found a nice and small npm package called &lt;a href="https://github.com/nodeca/probe-image-size"&gt;&lt;code&gt;probe-image-size&lt;/code&gt;&lt;/a&gt;. It allows for 'probing' an image locally or remotely.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;probeImageSize&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;probe-image-size&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;imageData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;probeImageSize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;imageData&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// {&lt;/span&gt;
&lt;span class="c1"&gt;//   url: 'https:// ...',&lt;/span&gt;
&lt;span class="c1"&gt;//   width: 528,&lt;/span&gt;
&lt;span class="c1"&gt;//   height: 560,&lt;/span&gt;
&lt;span class="c1"&gt;//   type: 'png',&lt;/span&gt;
&lt;span class="c1"&gt;//   mime: 'image/png',&lt;/span&gt;
&lt;span class="c1"&gt;//   wUnits: 'px',&lt;/span&gt;
&lt;span class="c1"&gt;//   hUnits: 'px',&lt;/span&gt;
&lt;span class="c1"&gt;//   length: 47951&lt;/span&gt;
&lt;span class="c1"&gt;// }&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Activate native lazy loading in browser
&lt;/h3&gt;

&lt;p&gt;This is the easy part. Simply set the attribute on your HTML &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; element: &lt;code&gt;&amp;lt;img ... loading="lazy" ... &amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The HTML for the image becomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt;
  &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/assets/lazy-bear@528w.96b21f25.png"&lt;/span&gt;
  &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"A lazy bear"&lt;/span&gt;
  &lt;span class="na"&gt;loading=&lt;/span&gt;&lt;span class="s"&gt;"lazy"&lt;/span&gt;
  &lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;"528"&lt;/span&gt;
  &lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;"560"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Progressively enhance
&lt;/h3&gt;

&lt;p&gt;Now in modern browsers, we have got a compressed image that is lazy loaded and an allocated space on the page before that. That is great!&lt;/p&gt;

&lt;p&gt;Now we'd like to create a transition effect so that visitors understand that something is loading on the reserved space.&lt;/p&gt;

&lt;p&gt;On the image itself, we can use the &lt;code&gt;onload&lt;/code&gt; attribute to 'do something' when the image is loaded.&lt;/p&gt;

&lt;p&gt;The simplest placeholder we can think of is color. So let's color up the reserved space while the image is loading then fade out the color and fade in the image.&lt;/p&gt;

&lt;p&gt;First some styles on the &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; and a wrapper &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; for the color.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
&lt;span class="nc"&gt;.img-lazy&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;opacity&lt;/span&gt; &lt;span class="m"&gt;1s&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;max-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.img-lazy-wrapper&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;-webkit-fit-content&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;-moz-fit-content&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;fit-content&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;background-color&lt;/span&gt; &lt;span class="m"&gt;1s&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--color-img-placeholder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0.3&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Some explanations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;max-width&lt;/code&gt; and &lt;code&gt;height&lt;/code&gt; is to allow scaling the image according to its context (the browser window or a parent element)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;width: fit-content;&lt;/code&gt; (and its prefixed versions) is to avoid having the wrapper div stretch wider than the image. Instead it will stick to its content's dimensions.&lt;/li&gt;
&lt;li&gt;We load the page with 0 &lt;code&gt;opacity&lt;/code&gt; for the image (it will fade in while it loads) and a &lt;code&gt;background-color&lt;/code&gt; on the wrapper div (it will fade out when the image loads).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;transition&lt;/code&gt; is to create the fading effect.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: we would very much like to avoid the wrapper &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; but could not find an elegant alternative. Please share if you have one.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Our HTML becomes&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"img-lazy-wrapper"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt;
    &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/assets/lazy-bear@528w.96b21f25.png"&lt;/span&gt;
    &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"A lazy bear"&lt;/span&gt;
    &lt;span class="na"&gt;loading=&lt;/span&gt;&lt;span class="s"&gt;"lazy"&lt;/span&gt;
    &lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;"528"&lt;/span&gt;
    &lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;"560"&lt;/span&gt;
    &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"img-lazy"&lt;/span&gt;
    &lt;span class="na"&gt;onload=&lt;/span&gt;&lt;span class="s"&gt;"this.parentNode.style.backgroundColor = 'transparent';this.style.opacity = 1;"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Note the &lt;code&gt;onload&lt;/code&gt; attribute on the &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tag. That is the only javaScript we need to make the transition from placeholder to image.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Of course, if we rely on javaScript to display the image, we should have a fail safe mechanism for visitors without JS. In this case, we simply remove the transition effect.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;noscript&amp;gt;&amp;lt;style&amp;gt;&lt;/span&gt;
&lt;span class="c"&gt;/* Overrides opacity and background-color in case we have no JS to get them it back */&lt;/span&gt;
&lt;span class="nc"&gt;.img-lazy&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.img-lazy-wrapper&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;transparent&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&amp;lt;/noscript&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Compatibility and final thoughts
&lt;/h2&gt;

&lt;p&gt;According to my limited research (and the &lt;a href="https://caniuse.com/"&gt;caniuse&lt;/a&gt; website), around 80% of users (in May 2022) should benefit from the whole technique. 20% of visitors will have a limited experience, either image is not lazy loaded, or content is jumping on image load, or the wrapping div will be wider than the image, or there will be no background transition effect... something like that. In any case, they won't have trouble seeing the content and that is the point.&lt;/p&gt;

&lt;p&gt;We love solutions like these which are lightweight and use the platform as much as possible. Technical debt is reduced to a minimum. It makes them future proof and compatibility ratio will only increase with time.&lt;/p&gt;

&lt;p&gt;Moreover, with &lt;strong&gt;&lt;a href="https://poko.m4rr.co/"&gt;poko&lt;/a&gt;&lt;/strong&gt; we are not forcing this on anyone since using this component is optional.&lt;/p&gt;

</description>
      <category>image</category>
      <category>performance</category>
      <category>html</category>
      <category>native</category>
    </item>
    <item>
      <title>Building my own static site generator for Svelte</title>
      <dc:creator>M4rrc0</dc:creator>
      <pubDate>Thu, 12 Mar 2020 12:52:01 +0000</pubDate>
      <link>https://dev.to/m4rrc0/building-my-own-static-site-generator-for-svelte-1474</link>
      <guid>https://dev.to/m4rrc0/building-my-own-static-site-generator-for-svelte-1474</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally posted on &lt;a href="https://www.m4rr.co/blog/building-my-own-static-site-generator-for-svelte/"&gt;https://www.m4rr.co/blog/building-my-own-static-site-generator-for-svelte/&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I have been using GatsbyJS since early V2 Alpha. At the time &lt;a href="https://jamstack.org/"&gt;Jamstack&lt;/a&gt; was barely a thing but for a newbie (like me at the time) coming into web development and interested in performance and developer experience it was a golden gate wide open and full of fun promises.&lt;/p&gt;

&lt;p&gt;It has been so fulfilling to learn web development in the Jamstack communities we have today.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I have done since
&lt;/h2&gt;

&lt;p&gt;I have been freelancing and doing web projects with React and Gatsby mostly.&lt;/p&gt;

&lt;p&gt;A large portion of my time has been going in building &lt;a href="https://www.toile.io/"&gt;toile.io&lt;/a&gt; and the &lt;a href="https://github.com/toile-webstack/gatsby-site-builder"&gt;Gatsby site builder&lt;/a&gt; that it uses. To be honest I don't know if this project is good or bad. I have been told things like "it is not a 'real product' since you don't offer your own editing experience" (editing is done through Contentful). At the same time, clients are happy with the result and it allows me (and my graphic designer partner) to build customizable websites fairly quickly.&lt;/p&gt;

&lt;p&gt;While there is a lot of things to improve I think I am reasonably proud of it considering 90% of the code has been written when I was 2-3 years into programming.&lt;/p&gt;

&lt;p&gt;I have also done all the classic mistakes a beginner can do. That alone probably makes the experience worthy.&lt;/p&gt;

&lt;h2&gt;
  
  
  The frustrations
&lt;/h2&gt;

&lt;p&gt;Despite all the good stuff Gatsby was doing for me, frustrations arose. Gatsby is doing a lot of things under the hood. I would argue that it is mostly built for beginners. You can certainly build very big and complex projects with it but then you need a level of expertise that could make Gatsby useless.&lt;/p&gt;

&lt;p&gt;The GraphQL layer is a good example of hidden complexity. It is a great way for beginners to be able to explore their data. As a beginner, you feel good using this shiny newish tech and understanding what happens to your data and pulling it in your components. So great...&lt;/p&gt;

&lt;p&gt;Then you land on a "&lt;a href="https://github.com/gatsbyjs/gatsby/issues/1517"&gt;bug&lt;/a&gt;"... except you are told "that is how GraphQL works...". You know there are clear solutions but you have no idea how to implement this with GraphQL. So you try to find a workaround because you want to stay focus on your project. And here you are 3 years later, still stuck with your workaround...&lt;/p&gt;

&lt;p&gt;I am not going to expand on this point because I trust that you have either experienced the same kind of issue or are perfectly happy with your stack.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bottom line&lt;/strong&gt;: I have spent countless hours trying to figure out how to solve or work around problems that were very much Gatsby specific. I probably learned a few things from this but mostly it made me knowledgeable with Gatsby, not so much web development in general.&lt;/p&gt;

&lt;h2&gt;
  
  
  Decoupling your code from the tools you use
&lt;/h2&gt;

&lt;p&gt;The GraphQL layer has become a real burden in my Gatsby site builder. Every time a solution to one of my problems arise it brings new problems that I need to investigate. And every new Gatsby functionality makes the API and the ecosystem wider as well which probably makes it even harder for the Gatsby team to tackle the broad array of issues.&lt;/p&gt;

&lt;p&gt;As an experiment, I tried removing usage of the GraphQL layer from my Gatsby site builder. I knew this would solve a bunch of issues I have been dragging for a long time. I didn't really evaluate the consequences of handling data manually but that is why it is an experiment. I wanted to find out.&lt;/p&gt;

&lt;p&gt;The data fetching from Contentful took me an hour or so. I could just copy the interesting parts of the code from the &lt;code&gt;gatsby-source-contentful&lt;/code&gt; plugin and the Contentful documentation is clear so it is a piece of cake.&lt;br&gt;
Then I was doing a bunch of data transformations with Gatsby plugins like transforming markdown and creating some fields. Also a lot easier to do when you can just manipulate JSON instead of transforming a GraphQL schema...&lt;/p&gt;

&lt;p&gt;In the end, I put my experiment on pause because I want to refactor core pieces of my app and some of these pieces were tightly coupled with the way I was handling data with GraphQL.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bottom line&lt;/strong&gt;: I cannot fully evaluate yet the benefits and costs of using specific tools but I know I want to pay more attention to spending time learning web development good practices and spend less time debugging specific tooling I have little control on.&lt;br&gt;
We know we need to modularize but we don't really know how to do it until we are faced with our first deep refactoring. I guess that is one of those things that come with experience.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Alway pay attention to the coupling you are introducing in your code base when using specific tools. Ask yourself: "if I want to change this piece, how easy will it be?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The trigger
&lt;/h2&gt;

&lt;p&gt;Development has been slow on my Gatsby site builder. One reason is bad maintainability. Like I said before, I have probably done all the mistakes a rookie can do (at least I hope I have done most).&lt;/p&gt;

&lt;p&gt;Between handling clients' requests, rewriting almost the whole app, implementing new features and improving scalability to be able to make it a business, setting priorities has been challenging to say the least.&lt;/p&gt;

&lt;p&gt;One day, as I was disappointed by the loading time of the JS bundle on some clients' sites I re-discovered Rich Harris talk about Svelte &lt;em&gt;&lt;a href="https://youtu.be/AdNJ3fydeao"&gt;Rethinking Reactivity&lt;/a&gt;&lt;/em&gt;. That lead me to some explorations on (among others) &lt;a href="https://svelte.dev/"&gt;Svelte&lt;/a&gt;, &lt;a href="https://sapper.svelte.dev/"&gt;Sapper&lt;/a&gt;, &lt;a href="https://github.com/sw-yx/ssg"&gt;SSG&lt;/a&gt;, &lt;a href="https://github.com/jakedeichert/svelvet"&gt;Svelvet&lt;/a&gt;, ...&lt;/p&gt;

&lt;p&gt;I also tried &lt;a href="https://www.11ty.dev/"&gt;Eleventy&lt;/a&gt;. It is super tempting to dig into and must be a delight to use for a personal site for example... I am just not sure I want to drop the 'framework feel' from my daily dev practice&lt;/p&gt;

&lt;p&gt;One thought was haunting me:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;That would be great to have a super lightweight and fast static site generator that provides the developer experience of a framework while being able to generate an SPA, a PWA or a plain and simple HTML site.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;These wanderings and reflexions lead to &lt;a href="https://github.com/jakedeichert/svelvet/issues/31"&gt;this discussion&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And finally to launching &lt;a href="https://www.m4rr.co/"&gt;this site&lt;/a&gt; (&lt;a href="https://github.com/MarcCoet/m4rrco-from-scratch"&gt;repo&lt;/a&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  Building an SSG
&lt;/h2&gt;

&lt;p&gt;I am finally building my personal site.&lt;/p&gt;

&lt;p&gt;I decided to try and build my own static site generator along the way and more generally, create the editing experience I would like to provide to my clients and everyone creating its own &lt;strong&gt;&lt;em&gt;personal web space&lt;/em&gt;&lt;/strong&gt;. I am fairly happy with this decision so far. I have been making steady progress and haven't encountered a major road block so far. To be honest I didn't think development would be so smooth especially since I am navigating in uncharted water. JS is my language of choice but all the tools I am using were unknown to me a few weeks back.&lt;/p&gt;

&lt;p&gt;The principles I want to follow:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;sites must be fast to navigate and have a fast build time&lt;/li&gt;
&lt;li&gt;boilerplate is ok if it removes the "magic black box" effect&lt;/li&gt;
&lt;li&gt;use the simplest convenient tech possible / stay close to the platform&lt;/li&gt;
&lt;li&gt;development experience of a framework&lt;/li&gt;
&lt;li&gt;progressively enhance the sites on the principle of &lt;a href="https://hankchizljaw.com/wrote/the-p-in-progressive-enhancement-stands-for-pragmatism/"&gt;Andy Bell's "minimum viable experience"&lt;/a&gt;, meaning we can use all the shiny new tech available as long as we provide a viable experience for older browsers.&lt;/li&gt;
&lt;li&gt;no magic around Progressive Web App creation but sensible defaults&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I should point out that the end goal is not simply a SSG but a &lt;strong&gt;&lt;em&gt;portable website&lt;/em&gt; that is editable without code and customizable with code.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What does this SSG looks like so far
&lt;/h2&gt;

&lt;p&gt;It is very much based on &lt;a href="https://github.com/jakedeichert/svelvet"&gt;svelvet&lt;/a&gt; and &lt;a href="https://www.snowpack.dev/"&gt;snowpack&lt;/a&gt;. It is using Svelte only to compile JS and HTML files (no headless chrome or other prerendering engine).&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It copies files with random extensions from the &lt;code&gt;src&lt;/code&gt; folder to the &lt;code&gt;dist&lt;/code&gt; folder&lt;/li&gt;
&lt;li&gt;It compiles &lt;code&gt;.svelte&lt;/code&gt; and &lt;code&gt;.js&lt;/code&gt; files to the &lt;code&gt;dist&lt;/code&gt; folder to be used as &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules"&gt;web modules&lt;/a&gt; directly in your application.&lt;/li&gt;
&lt;li&gt;It compiles &lt;code&gt;.svelte&lt;/code&gt; and &lt;code&gt;.js&lt;/code&gt; files to the &lt;code&gt;build&lt;/code&gt; folder as needed for server side rendering.&lt;/li&gt;
&lt;li&gt;It generates hydratable HTML files for pages

&lt;ul&gt;
&lt;li&gt;based on folder structure (every svelte component in &lt;code&gt;src/_pages&lt;/code&gt; gets its HTML equivalent)&lt;/li&gt;
&lt;li&gt;programmatically with the &lt;code&gt;src/routes.js&lt;/code&gt; file&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;It puts external dependencies in &lt;code&gt;dist/web_modules&lt;/code&gt; as web modules thanks to snowpack&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The result is a &lt;code&gt;dist&lt;/code&gt; folder with HTML, JS and other assets files you can host on any static host. Files are not bundled so we get some kind of automatic code splitting.&lt;/p&gt;

&lt;h3&gt;
  
  
  Caveats
&lt;/h3&gt;

&lt;p&gt;Web modules are not supported in IE. It means IE clients will only see our HTML so we need to provide them with a minimum viable experience.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fun and interesting features
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;dev server with hot reloading (a bit clumsy at the moment)&lt;/li&gt;
&lt;li&gt;CSS can be defined either globally in a CSS file or embedded in the HTML &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; (or inline on html elements if you need dynamic behavior)&lt;/li&gt;
&lt;li&gt;can build 2 types of nav experience: "HTML to single page application (SPA)" (like Gatsby) or "each page its JS" (like Eleventy). You can even mix in the same project some parts as individual pages and some groups of pages as SPAs.&lt;/li&gt;
&lt;li&gt;can write svelte components in markdown thanks to &lt;a href="https://github.com/pngwn/MDsveX"&gt;MDsveX&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  WIP roadmap
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;RSS feed&lt;/li&gt;
&lt;li&gt;be able to not load JS at all if we only want HTML and/or our page does not generate any side effect anyway&lt;/li&gt;
&lt;li&gt;look into adding more preprocessors like sass, postCSS, ...&lt;/li&gt;
&lt;li&gt;design the site with &lt;em&gt;nuds&lt;/em&gt; (website design tooling that will be the subject of other articles)&lt;/li&gt;
&lt;li&gt;better handle external libraries&lt;/li&gt;
&lt;li&gt;make hot reloading work properly&lt;/li&gt;
&lt;li&gt;check how to safely remove CSS from JS components created with svelte (should be safe when an HTML page is created but not if it is a client-only route in SPA mode)&lt;/li&gt;
&lt;li&gt;looking at Progressive Web App creation&lt;/li&gt;
&lt;li&gt;looking at integrating IndieWeb functionalities&lt;/li&gt;
&lt;li&gt;incremental builds would be nice&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you are curious, please test it out but don't expect any kind of support until I experiment with it more and decide to release it in some fashion. Nonetheless I would greatly appreciate any constructive feedback or general opinion.&lt;/p&gt;

&lt;p&gt;Get in touch on &lt;a href="https://twitter.com/M4rrc0"&gt;Twitter&lt;/a&gt;&lt;br&gt;
Subscribe to the &lt;a href="https://www.m4rr.co/subscribe/"&gt;newsletter&lt;/a&gt;&lt;br&gt;
Support my work on &lt;a href="https://www.patreon.com/m4rrco/"&gt;Patreon&lt;/a&gt;&lt;/p&gt;

</description>
      <category>svelte</category>
      <category>jamstack</category>
      <category>ssg</category>
    </item>
  </channel>
</rss>
