<?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: PROTOTYP</title>
    <description>The latest articles on Forem by PROTOTYP (@prototyp).</description>
    <link>https://forem.com/prototyp</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%2Forganization%2Fprofile_image%2F1014%2F22219225-134b-498e-a18b-42f9ae9cd91d.jpg</url>
      <title>Forem: PROTOTYP</title>
      <link>https://forem.com/prototyp</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/prototyp"/>
    <language>en</language>
    <item>
      <title>Load fonts asynchronously &amp; handle FOUT with my Gatsby plugin</title>
      <dc:creator>Adrian Bece</dc:creator>
      <pubDate>Tue, 03 Nov 2020 06:42:35 +0000</pubDate>
      <link>https://forem.com/prototyp/load-fonts-asynchronously-handle-fout-with-my-gatsby-plugin-19e</link>
      <guid>https://forem.com/prototyp/load-fonts-asynchronously-handle-fout-with-my-gatsby-plugin-19e</guid>
      <description>&lt;p&gt;In the past few weeks I was familiarizing myself with Gatsby and I was making a list of plugins that are good-to-have while working on a project. I went through the SEO plugins, SVG plugins, data source plugins, and finally, I wanted to check out some font loader plugins when I noticed something strange.&lt;/p&gt;

&lt;p&gt;Here are the top two Gatsby plugins for fonts.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fbrx26n7mtszd9ds2w3em.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fbrx26n7mtszd9ds2w3em.PNG" alt="Alt Text" width="430" height="587"&gt;&lt;/a&gt;&lt;/p&gt;

Top two font plugins for Gatsby




&lt;p&gt;And if we open any of those two, we are greeted by the following message and README .&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fkwa9qfvynohcsulgya71.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fkwa9qfvynohcsulgya71.PNG" alt="Alt Text" width="800" height="587"&gt;&lt;/a&gt;&lt;/p&gt;

Oh no... this isn't good




&lt;p&gt;&lt;strong&gt;Most-used plugins are abandoned, deprecated and not actively maintained&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Don't get me wrong, it's totally okay for authors and companies to back down from developing and maintaining an open-source repo. I think it would be better to transfer the ownership to someone who wants to continue the development beforehand rather than leaving it as is and archiving it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Asynchronous font loading
&lt;/h2&gt;

&lt;p&gt;That missing support for the most-used plugins was my primary motivation to develop the font loader plugin, but what do font loaders even do?&lt;/p&gt;

&lt;p&gt;In short, &lt;strong&gt;font loaders help eliminate render-blocking resources&lt;/strong&gt;, in this case, fonts and their respective CSS files. I've gone into more detail about render-blocking resources in one of my previous articles.&lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/prototyp" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__org__pic"&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nRxnx9N5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://media.dev.to/cdn-cgi/image/width%3D150%2Cheight%3D150%2Cfit%3Dcover%2Cgravity%3Dauto%2Cformat%3Dauto/https%253A%252F%252Fdev-to-uploads.s3.amazonaws.com%252Fuploads%252Forganization%252Fprofile_image%252F1014%252F22219225-134b-498e-a18b-42f9ae9cd91d.jpg" alt="PROTOTYP" width="150" height="150"&gt;
      &lt;div class="ltag__link__user__pic"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--V9WiJ0zi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://media.dev.to/cdn-cgi/image/width%3D150%2Cheight%3D150%2Cfit%3Dcover%2Cgravity%3Dauto%2Cformat%3Dauto/https%253A%252F%252Fdev-to-uploads.s3.amazonaws.com%252Fuploads%252Fuser%252Fprofile_image%252F91468%252Fcdda7312-9dc4-4b73-98ed-fb0c3d55a624.jpg" alt="" width="150" height="150"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/prototyp/improving-website-performance-by-eliminating-render-blocking-css-and-javascript-28ei" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Improving website performance by eliminating render-blocking CSS and JavaScript&lt;/h2&gt;
      &lt;h3&gt;Adrian Bece for PROTOTYP ・ Aug 31 '20&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#javascript&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#webdev&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#css&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#tutorial&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;p&gt;When a website document is loaded, it automatically starts downloading high-priority resources that are needed before displaying the page - CSS, JavaScript, images, iframe, videos, fonts... Depending on how we instruct the browser, we can load some of those resources after the page has been displayed (rendered). By doing this, we are displaying the content as fast as possible to the user and loading all non-critical resources afterward to speed up the loading process.&lt;/p&gt;

&lt;p&gt;This is especially true for web fonts, like Google fonts for example. During our page load, we are requesting a font CSS file from Google servers which also requests additional font files from Google CDN. Our page is not displayed until this chain of request resolves which can take some time depending on the CDN performance and user's internet connection.&lt;/p&gt;

&lt;p&gt;With asynchronous loading, we can give a low priority to the font file and CSS and load it after the page is displayed. Although this has improved site performance, we have created a minor visual issue - Flash of Unstyled Text (FOUT). &lt;/p&gt;

&lt;h2&gt;
  
  
  Flash Of Unstyled Text (FOUT)
&lt;/h2&gt;

&lt;p&gt;If the font is loaded after page content is displayed, we can see the moment the font changes between the fallback (default) font and the main web font that has been loaded asynchronously. This event is called &lt;strong&gt;Flash Of Unstyled Text&lt;/strong&gt; or &lt;strong&gt;FOUT&lt;/strong&gt;, for short. This change might even affect the page layout, size of some elements and even cause some visual bugs because the page is styled with web font in mind.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fcblxyti7vjq5ms3t5lk9.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fcblxyti7vjq5ms3t5lk9.gif" alt="Alt Text" width="800" height="243"&gt;&lt;/a&gt;&lt;/p&gt;

This isn't all that bad considering the performance gain, but it is very noticeable




&lt;p&gt;What we can do to make this effect much less noticeable is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Choose the fallback font that looks as closely as possible to the web font that is being loaded asynchronously &lt;/li&gt;
&lt;li&gt;Adjust font size, line height, letter spacing and word spacing to match the web font as closely as possible&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After adjusting the fallback font CSS, we get the following result.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fkwjrk9kwln3ybqjsd352.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fkwjrk9kwln3ybqjsd352.gif" alt="Alt Text" width="800" height="274"&gt;&lt;/a&gt;&lt;/p&gt;

We've adjusted the style (line-height, letter spacing, word spacing) of fallback font and the change looks nicer




&lt;p&gt;You might be asking: &lt;strong&gt;how can we detect when the font has been download and applied to the document?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We'll have to use JavaScript to detect that event. In case of Gatsby, I've written &lt;a href="https://www.gatsbyjs.com/plugins/gatsby-omni-font-loader/"&gt;a plugin&lt;/a&gt; that both loads the web font asynchronously and it listens to font load event and applies a CSS class to HTML body element to handle FOUT.&lt;/p&gt;

&lt;h2&gt;
  
  
  Gatsby omni font loader plugin
&lt;/h2&gt;

&lt;p&gt;During the past week, I've been working on creating a Gatsby plugin that will use the &lt;a href="https://csswizardry.com/2020/05/the-fastest-google-fonts/"&gt;recommended way of loading fonts&lt;/a&gt; and enable devs to handle FOUT easily.&lt;/p&gt;

&lt;p&gt;And few days ago, I've published &lt;a href="https://www.gatsbyjs.com/plugins/gatsby-omni-font-loader/"&gt;Gatsby Omni Font Loader&lt;/a&gt; that can work with both web fonts and self-hosted fonts, add preload and preconnect on SSR, add font asynchronously, and handle FOUT. All in one small, neat package.&lt;/p&gt;

&lt;p&gt;You can check out the &lt;a href="https://github.com/codeAdrian/gatsby-omni-font-loader"&gt;source code on Github&lt;/a&gt;. Feel free to submit issues, feature requests and pull requests. Support and contribution are very much appreciated!&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--A9-wwsHG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/codeAdrian"&gt;
        codeAdrian
      &lt;/a&gt; / &lt;a href="https://github.com/codeAdrian/gatsby-omni-font-loader"&gt;
        gatsby-omni-font-loader
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Font loader optimized for maximum performance. Removes render-blocking font resources and loads them asynchronusly. Handle FOUT &amp;amp; FOUC with font loading status watcher. Supports both local-hosted fonts and web fonts.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div&gt;
&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/cd560c1984341cad2e82289ce08218e9c36d5cf9f0127f409fa9142fce7920bf/68747470733a2f2f7265732e636c6f7564696e6172792e636f6d2f64617a6474393764332f696d6167652f75706c6f61642f635f7363616c652c715f6175746f3a626573742c775f3230302f76313630363535383232332f6f6d6e692d6c6f676f2e6a7067"&gt;&lt;img src="https://camo.githubusercontent.com/cd560c1984341cad2e82289ce08218e9c36d5cf9f0127f409fa9142fce7920bf/68747470733a2f2f7265732e636c6f7564696e6172792e636f6d2f64617a6474393764332f696d6167652f75706c6f61642f635f7363616c652c715f6175746f3a626573742c775f3230302f76313630363535383232332f6f6d6e692d6c6f676f2e6a7067" alt="Omni font loader logo"&gt;&lt;/a&gt;
&lt;br&gt;&lt;br&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Gatsby Omni Font Loader v2&lt;/h1&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Simple way to add webfonts or custom fonts to Gatsby project&lt;/li&gt;
&lt;li&gt;Performant asynchronous font loading can be enabled&lt;/li&gt;
&lt;li&gt;Font loading listener can be enabled&lt;/li&gt;
&lt;li&gt;Flash Of Unstyled Text (FOUT) handling support&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;
&lt;br&gt;
&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/3f47f9da6641f1680e24169f1a5484d332b915b8ede71a5f79f97597aeefce5c/68747470733a2f2f62616467656e2e6e65742f6769746875622f7461672f636f646541647269616e2f6761747362792d6f6d6e692d666f6e742d6c6f61646572"&gt;&lt;img src="https://camo.githubusercontent.com/3f47f9da6641f1680e24169f1a5484d332b915b8ede71a5f79f97597aeefce5c/68747470733a2f2f62616467656e2e6e65742f6769746875622f7461672f636f646541647269616e2f6761747362792d6f6d6e692d666f6e742d6c6f61646572"&gt;&lt;/a&gt; &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/5242823bd7a94f36b0e2ee5d3fa61dc037c8bd0ed0e4b94f20b78695f4bb32e7/68747470733a2f2f62616467656e2e6e65742f6e706d2f64742f6761747362792d6f6d6e692d666f6e742d6c6f61646572"&gt;&lt;img src="https://camo.githubusercontent.com/5242823bd7a94f36b0e2ee5d3fa61dc037c8bd0ed0e4b94f20b78695f4bb32e7/68747470733a2f2f62616467656e2e6e65742f6e706d2f64742f6761747362792d6f6d6e692d666f6e742d6c6f61646572"&gt;&lt;/a&gt; &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/7f745fb7dd2a22f68fe03adcdb977963ada4c8265675e572c629b29b9b34af2b/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5052732d77656c636f6d652d627269676874677265656e2e737667"&gt;&lt;img src="https://camo.githubusercontent.com/7f745fb7dd2a22f68fe03adcdb977963ada4c8265675e572c629b29b9b34af2b/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5052732d77656c636f6d652d627269676874677265656e2e737667"&gt;&lt;/a&gt;
&lt;br&gt;&lt;br&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/dea03df67bccd33429108a136644d683f833776a3da7a21979b7e39aa20a634a/68747470733a2f2f62616467656e2e6e65742f6769746875622f73746172732f636f646541647269616e2f6761747362792d6f6d6e692d666f6e742d6c6f61646572"&gt;&lt;img src="https://camo.githubusercontent.com/dea03df67bccd33429108a136644d683f833776a3da7a21979b7e39aa20a634a/68747470733a2f2f62616467656e2e6e65742f6769746875622f73746172732f636f646541647269616e2f6761747362792d6f6d6e692d666f6e742d6c6f61646572"&gt;&lt;/a&gt; &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/b79492c5a51ca3cfc3645f15e5986984462db27519b56bd51cfa726c58c1d77e/68747470733a2f2f62616467656e2e6e65742f6769746875622f6f70656e2d6973737565732f636f646541647269616e2f6761747362792d6f6d6e692d666f6e742d6c6f61646572"&gt;&lt;img src="https://camo.githubusercontent.com/b79492c5a51ca3cfc3645f15e5986984462db27519b56bd51cfa726c58c1d77e/68747470733a2f2f62616467656e2e6e65742f6769746875622f6f70656e2d6973737565732f636f646541647269616e2f6761747362792d6f6d6e692d666f6e742d6c6f61646572"&gt;&lt;/a&gt; &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/a943bc78363b197ece6cef6876f21df7a9c78f1780a8ba0d0d9e90b915a53263/68747470733a2f2f62616467656e2e6e65742f6769746875622f636c6f7365642d6973737565732f636f646541647269616e2f6761747362792d6f6d6e692d666f6e742d6c6f61646572"&gt;&lt;img src="https://camo.githubusercontent.com/a943bc78363b197ece6cef6876f21df7a9c78f1780a8ba0d0d9e90b915a53263/68747470733a2f2f62616467656e2e6e65742f6769746875622f636c6f7365642d6973737565732f636f646541647269616e2f6761747362792d6f6d6e692d666f6e742d6c6f61646572"&gt;&lt;/a&gt; &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/64b1142a66bdcd3dc48c7c7674b4bdb29e48164f5bc8d8fc077a8b61b7bec84a/68747470733a2f2f62616467656e2e6e65742f6769746875622f6c6173742d636f6d6d69742f636f646541647269616e2f6761747362792d6f6d6e692d666f6e742d6c6f616465722f6d61696e"&gt;&lt;img src="https://camo.githubusercontent.com/64b1142a66bdcd3dc48c7c7674b4bdb29e48164f5bc8d8fc077a8b61b7bec84a/68747470733a2f2f62616467656e2e6e65742f6769746875622f6c6173742d636f6d6d69742f636f646541647269616e2f6761747362792d6f6d6e692d666f6e742d6c6f616465722f6d61696e"&gt;&lt;/a&gt; &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/b5fe51dd638ca13f35cad82179cf6ae2df0b9d708b47effb3f66a30f097966bd/68747470733a2f2f62616467656e2e6e65742f6769746875622f6c6963656e73652f636f646541647269616e2f6761747362792d6f6d6e692d666f6e742d6c6f61646572"&gt;&lt;img src="https://camo.githubusercontent.com/b5fe51dd638ca13f35cad82179cf6ae2df0b9d708b47effb3f66a30f097966bd/68747470733a2f2f62616467656e2e6e65742f6769746875622f6c6963656e73652f636f646541647269616e2f6761747362792d6f6d6e692d666f6e742d6c6f61646572"&gt;&lt;/a&gt; &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/e5db49857ebf94cb45fb5037414c2f756759576a0991bea64c6b052a3cd9a684/68747470733a2f2f62616467656e2e6e65742f7061636b61676570686f6269612f696e7374616c6c2f6761747362792d6f6d6e692d666f6e742d6c6f61646572"&gt;&lt;img src="https://camo.githubusercontent.com/e5db49857ebf94cb45fb5037414c2f756759576a0991bea64c6b052a3cd9a684/68747470733a2f2f62616467656e2e6e65742f7061636b61676570686f6269612f696e7374616c6c2f6761747362792d6f6d6e692d666f6e742d6c6f61646572"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;br&gt;&lt;br&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Features&lt;/h2&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Supports web fonts &amp;amp; self-hosted fonts&lt;/li&gt;
&lt;li&gt;Preloads the files &amp;amp; preconnects to the URL&lt;/li&gt;
&lt;li&gt;Loads fonts asynchronously to avoid render blocking&lt;/li&gt;
&lt;li&gt;Implemented with &lt;a href="https://csswizardry.com/2020/05/the-fastest-google-fonts/" rel="nofollow"&gt;fast loading snippets&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Loading status listener for avoiding FOUT&lt;/li&gt;
&lt;li&gt;Small size &amp;amp; minimal footprint&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Install&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;&lt;code&gt;npm install gatsby-omni-font-loader react-helmet&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;or&lt;/p&gt;
&lt;p&gt;&lt;code&gt;yarn add gatsby-omni-font-loader react-helmet&lt;/code&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Configuration&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;Add the following snippet to &lt;code&gt;gatsby-config.js&lt;/code&gt; plugins array.&lt;/p&gt;
&lt;div class="highlight highlight-source-js notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-kos"&gt;{&lt;/span&gt;
  &lt;span class="pl-c"&gt;/* Include plugin */&lt;/span&gt;
  &lt;span class="pl-c1"&gt;resolve&lt;/span&gt;: &lt;span class="pl-s"&gt;"gatsby-omni-font-loader"&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt;
  &lt;span class="pl-c"&gt;/* Plugin options */&lt;/span&gt;
  &lt;span class="pl-c1"&gt;options&lt;/span&gt;: &lt;span class="pl-kos"&gt;{&lt;/span&gt;

    &lt;span class="pl-c"&gt;/* Font loading mode */&lt;/span&gt;
    &lt;span class="pl-c1"&gt;mode&lt;/span&gt;: &lt;span class="pl-s"&gt;"async"&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt;

    &lt;span class="pl-c"&gt;/* Enable font loading listener to handle FOUT */&lt;/span&gt;
    &lt;span class="pl-c1"&gt;enableListener&lt;/span&gt;: &lt;span class="pl-c1"&gt;true&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt;

    &lt;span class="pl-c"&gt;/* Preconnect URL-s. This example is for Google Fonts */&lt;/span&gt;
    &lt;span class="pl-c1"&gt;preconnect&lt;/span&gt;: &lt;span class="pl-kos"&gt;[&lt;/span&gt;&lt;span class="pl-s"&gt;"https://fonts.gstatic.com"&lt;/span&gt;&lt;span class="pl-kos"&gt;]&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt;

    &lt;span class="pl-c"&gt;/* Self-hosted fonts&lt;/span&gt;&lt;/pre&gt;…
&lt;/div&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/codeAdrian/gatsby-omni-font-loader"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;h3&gt;
  
  
  Installation
&lt;/h3&gt;

&lt;p&gt;Start by installing the plugin with NPM or Yarn.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--save-dev&lt;/span&gt; gatsby-omni-font-loader
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn add &lt;span class="nt"&gt;--dev&lt;/span&gt; gatsby-omni-font-loader
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Configure the plugin
&lt;/h3&gt;

&lt;p&gt;In &lt;code&gt;gatsby-config.js&lt;/code&gt; file, reference the &lt;code&gt;gatsby-omni-font-loader&lt;/code&gt; plugin in the &lt;code&gt;plugins&lt;/code&gt; array and configure it.&lt;/p&gt;

&lt;p&gt;Below is the sample config and explanation for each of the options available.&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="p"&gt;{&lt;/span&gt;
  &lt;span class="cm"&gt;/* Include plugin */&lt;/span&gt;
  &lt;span class="nl"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;gatsby-omni-font-loader&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

  &lt;span class="cm"&gt;/* Plugin options */&lt;/span&gt;
  &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="cm"&gt;/* Enable font loading listener to handle FOUC */&lt;/span&gt;
    &lt;span class="nl"&gt;enableListener&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="cm"&gt;/* Preconnect URL-s. This example is for Google Fonts */&lt;/span&gt;
    &lt;span class="nx"&gt;preconnect&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://fonts.gstatic.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;

    &lt;span class="cm"&gt;/* Self-hosted fonts config. Add font files and font CSS files to "static" folder */&lt;/span&gt;
    &lt;span class="nx"&gt;custom&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="cm"&gt;/* Exact name of the font as defied in @font-face CSS rule */&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Font Awesome 5 Brands&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Font Awesome 5 Free&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="cm"&gt;/* Path to the font CSS file inside the "static" folder with @font-face definition */&lt;/span&gt;
        &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/fonts/fontAwesome/css/all.min.css&lt;/span&gt;&lt;span class="dl"&gt;"&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="cm"&gt;/* Web fonts. File link should point to font CSS file. */&lt;/span&gt;
    &lt;span class="nx"&gt;web&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
        &lt;span class="cm"&gt;/* Exact name of the font as defied in @font-face CSS rule */&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Staatliches&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="cm"&gt;/* URL to the font CSS file with @font-face definition */&lt;/span&gt;
        &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://fonts.googleapis.com/css2?family=Staatliches&lt;/span&gt;&lt;span class="dl"&gt;"&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="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Handling FOUT
&lt;/h3&gt;

&lt;p&gt;When &lt;code&gt;enableListener: true&lt;/code&gt; is set in plugin config in &lt;code&gt;gatsby-config.js&lt;/code&gt;, HTML classes are being added to &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt; element as the fonts are being loaded.&lt;/p&gt;

&lt;p&gt;HTML class name format will be in the following format&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;wf-[font-family-name]--loaded
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can use the &lt;a href="https://meowni.ca/font-style-matcher/"&gt;Font Style Matcher&lt;/a&gt; to adjust the perfect fallback font and fallback CSS config and use the styles from there.&lt;/p&gt;

&lt;p&gt;Here is the example of how body element will look like after all fonts are being loaded (depending on the config).&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;body&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"wf-font-awesome-5-brands--loaded wf-font-awesome-5-free--loaded wf-staatliches--loaded"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So the CSS will look something like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;"Merriweather"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Georgia&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;sans-serif&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="c"&gt;/* default styles */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="nd"&gt;:not&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;.wf-merriweather--loaded&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="c"&gt;/* fallback font (Georgia) CSS config */&lt;/span&gt;
 &lt;span class="c"&gt;/* line-height, letter spacing, font-size... */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="nd"&gt;:not&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;.wf-merriweather--loaded&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;h1&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="c"&gt;/* fallback font (Georgia) CSS config */&lt;/span&gt; 
 &lt;span class="c"&gt;/* line-height, letter spacing, font-size... */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.wf-merriweather--loaded&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="c"&gt;/* web font CSS config */&lt;/span&gt; 
 &lt;span class="c"&gt;/* line-height, letter spacing, font-size... */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.wf-merriweather--loaded&lt;/span&gt; &lt;span class="nt"&gt;h1&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="c"&gt;/* web font CSS config */&lt;/span&gt; 
 &lt;span class="c"&gt;/* line-height, letter spacing, font-size... */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;These articles are fueled by coffee. So if you enjoy my work and found it useful, consider buying me a coffee! I would really appreciate it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.buymeacoffee.com/ubnZ8GgDJ"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Igf3SR33--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.buymeacoffee.com/buttons/default-red.png" alt="Buy Me A Coffee" width="434" height="100"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thank you for taking the time to read this post. If you've found this useful, please give it a ❤️ or 🦄, share and comment.&lt;/p&gt;

</description>
      <category>react</category>
      <category>gatsby</category>
      <category>webdev</category>
      <category>javascript</category>
    </item>
    <item>
      <title>The thing about service workers...</title>
      <dc:creator>Sebastijan Dumancic</dc:creator>
      <pubDate>Fri, 25 Sep 2020 08:10:43 +0000</pubDate>
      <link>https://forem.com/prototyp/the-thing-about-service-workers-2k97</link>
      <guid>https://forem.com/prototyp/the-thing-about-service-workers-2k97</guid>
      <description>&lt;p&gt;For years, service workers have promised us a feeling of a native app straight in the browser. While specific parts of it are true, such as access to device hardware (gyroscope, orientation sensor, etc.) or background sync, we're still far cry from the native feel in areas such as push notifications, which don't work at all in iOS, and especially offline support. While it's fun to have options and experiment on private, small projects, we can't really consider technologies to be production-ready when they don't support half the devices out there.&lt;/p&gt;

&lt;p&gt;With that in mind, service workers also come with a lot of baggage. &lt;em&gt;And we've found that out in a hard way.&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Problem with service workers
&lt;/h1&gt;

&lt;p&gt;First, you start developing your app, include service worker because it's a cool new thing to do, you want offline support or definitely want to have all the stuff they bring as an option to use. Months go by, you release v1 of the app and users start rolling in. As it's the first version, things are bound to change, so you implement the first couple of changes. You maybe change some copy around, some links, but soon you get a report that you have a huge bug discovered by one of your users. Maybe something potentially catastrophic for your database, it happens. &lt;/p&gt;

&lt;p&gt;Fear not, you patch it up and go about your day. Months go by, and you release v2 of the app, market it like crazy and it reaches some top lists of this and that, and same users that entered your app a couple of months ago, and never again, land on your page and load a completely cached version from the last time they visited, together with any hardcoded values, bugs, funnel changes... essentially a snapshot from the past.&lt;/p&gt;

&lt;p&gt;By the time you realize what you have done, you've already saved a snapshot of the app in the arbitrary time to thousands of devices, unable to wipe the cache on their devices, and just sit and wait for them to potentially open your older, less secure version of the app from months ago. &lt;/p&gt;

&lt;h1&gt;
  
  
  Potential solutions
&lt;/h1&gt;

&lt;p&gt;The way service worker works is when users land on your website for the 2nd time, it loads all assets and files from SW and THEN checks if you have pushed a new SW update in the meantime. If it finds one, it schedules it to be loaded next time you open the website. To make matters worse, normal refresh won't load the new SW content, but you have to close the session (i.e. close all website tabs currently open, or whole browser) and reopen the website to load the new version. &lt;/p&gt;

&lt;p&gt;By following this article: &lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag__link"&gt;
  &lt;a href="/glukmann" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F288075%2Fd8e2dcd6-c87f-428e-aa4b-330fc8d88f61.jpg" alt="glukmann"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/glukmann/let-users-know-when-you-have-updated-your-service-worker-in-create-react-app-3il9" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Let Users Know When You Have Updated Your Service Workers in Create React App&lt;/h2&gt;
      &lt;h3&gt;Gert Glükmann ・ Jan 4 '20&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#react&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#javascript&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#serviceworkers&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#pwa&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;p&gt;I found out that you can insert an action into this process of discovering the new version of SW. The problem is that you can either notify users that a new version is available and leave it up to them to click &lt;strong&gt;Load new version&lt;/strong&gt; which will reload the website properly, or you can force a refresh when the browser figures out that a new SW version is available and installed, which can be after a couple of seconds and well into the time when the app was already interactive, meaning that user started to do something. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F1d8p5yhkyec2xahxcppa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F1d8p5yhkyec2xahxcppa.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Usually, early in the live of an app, updates could be released several times a day, which means users could get a prompt or hard refresh multiple times a day when they land on a website, which is also not a good option.&lt;/p&gt;

&lt;p&gt;Other way around, we leave ourselves open to the ghosts of the past by giving users the option to load a new version of the site or not. This way malicious users could take advantage of the app in its pre-patched state.&lt;/p&gt;

&lt;p&gt;The worst thing is, when you decide to change your approach, you still have all the old users cached on your previous decision and they don't get the memo that they should reload as soon as they open the site. &lt;/p&gt;

&lt;p&gt;Now, when you think about it, it becomes obvious that this is how native apps work from the start. You have a version that is the latest and a lot of previous versions that people have installed, and are still using. &lt;em&gt;But the web isn't native&lt;/em&gt;. &lt;/p&gt;

&lt;p&gt;Addon: &lt;br&gt;
As &lt;strong&gt;leob&lt;/strong&gt; mentioned in the comments (thanks!) some apps force users to update to new versions even in the native environment. Banking apps first come to mind, which just confirms that the issue exists and if security is an important priority for us, that we have to address it in a creative way.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;Many years ago, Web solved the issue where users had to install a new version of software on their devices, where we had to incentivize them to update or upgrade, and support legacy versions with both features and bugfixes. We approach web development with a different mindset than native development. We KNOW we can push updates very quickly to 100% percent of our users, and we are used to pushing experimental features, knowing very well that we can patch them up as we go or remove them fairly quickly if they turn out to be bad ideas. Including service workers turn the tables around once again, by introducing fragmentation, legacy support, and fear of the unknown on the internet, which is something that should be avoided at all costs. &lt;/p&gt;

&lt;p&gt;Sure, if we create just a couple of versions all of which are perfect, this is a non-issue, but let's be real, those apps don't exist. So how do you deal with these issues if you still want to keep offline support? Do you use service workers at all? Let me know! &lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Improving website performance by eliminating render-blocking CSS and JavaScript</title>
      <dc:creator>Adrian Bece</dc:creator>
      <pubDate>Mon, 31 Aug 2020 07:20:20 +0000</pubDate>
      <link>https://forem.com/prototyp/improving-website-performance-by-eliminating-render-blocking-css-and-javascript-28ei</link>
      <guid>https://forem.com/prototyp/improving-website-performance-by-eliminating-render-blocking-css-and-javascript-28ei</guid>
      <description>&lt;p&gt;In my previous post, I've talked about how I boosted Lighthouse scores for &lt;a href="https://codeadrian.github.io/" rel="noopener noreferrer"&gt;my personal website&lt;/a&gt; by implementing native lazy loading with fallback.&lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/prototyp" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__org__pic"&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F1014%2F22219225-134b-498e-a18b-42f9ae9cd91d.jpg" alt="PROTOTYP"&gt;
      &lt;div class="ltag__link__user__pic"&gt;
        &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F91468%2Fcdda7312-9dc4-4b73-98ed-fb0c3d55a624.jpg" alt=""&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/prototyp/best-way-to-lazy-load-images-for-maximum-performance-27o1" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Best way to lazy load images for maximum performance&lt;/h2&gt;
      &lt;h3&gt;Adrian Bece for PROTOTYP ・ Aug 24 '20&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#webdev&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#tutorial&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#html&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#javascript&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;p&gt;Another important improvement that boosted my performance and Lighthouse score was eliminating render-blocking resources.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fw4n411pbs6oiwk6uoc4b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fw4n411pbs6oiwk6uoc4b.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Critical and non-critical resources
&lt;/h2&gt;

&lt;p&gt;When we usually build a project, we like to include everything we need right out of the box - all styles, JavaScript plugins, JavaScript code, fonts, images, etc. We usually do this to make sure that no async loading errors happen while we develop the project.&lt;/p&gt;

&lt;p&gt;The reality is that browser needs to load, parse and run everything we include when website loads, &lt;strong&gt;which can make the first paint (with no cached resources) unnecessarily slow&lt;/strong&gt;. It's called render-blocking because &lt;strong&gt;browser is wasting time and resources parsing the code that is unnecessary for the initial page load&lt;/strong&gt; and not displaying the page content.&lt;/p&gt;

&lt;p&gt;When we take a look at our resources (CSS, JavaScript, fonts, etc.) we can sort them into two categories:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Critical resources&lt;/strong&gt; - code that's critical to the page's core functionality.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Non-critical resources&lt;/strong&gt; - code not being used in page's core functionality and a code that runs after the page is loaded or on user interaction.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So let's take a look how we to handle critical and non-critical CSS and JavaScript resources.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handling critical CSS
&lt;/h2&gt;

&lt;p&gt;Critical CSS refers to the styles that are &lt;strong&gt;necessary for styling above the fold content&lt;/strong&gt;. Above the fold &lt;strong&gt;content that is visible to users when they first load the page&lt;/strong&gt; (top section of the page).&lt;/p&gt;

&lt;p&gt;In order to add critical CSS to the page, we need to remove those styles from the CSS stylesheet and add them directly to HTML in a &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; tag inside a &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; element.&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;head&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
    &lt;span class="c"&gt;/* Add critical styles here */&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;

  &lt;span class="c"&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F840f35f4nwzg5piiypf2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F840f35f4nwzg5piiypf2.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;
Critical CSS on my personal site



&lt;p&gt;This approach may increase HTML document size a bit, but those changes are insignificant if you use compression algorithm like GZIP or Brotli for HTML delivery. &lt;/p&gt;

&lt;p&gt;Adding critical CSS directly to HTML document ensures that those styles and parsed and applied on the first paint (initial load).&lt;/p&gt;

&lt;h2&gt;
  
  
  Handling non-critical CSS
&lt;/h2&gt;

&lt;p&gt;In order to make the critical CSS effective, we need to tell the browser how to handle non-critical CSS and display the page. It also allows us to use the website while the additional non-critical CSS loads. Depending on Internet connection speed, you might not even notice the additional styles being loaded.&lt;/p&gt;

&lt;p&gt;In order to handle non-critical CSS, we need to change how the CSS file which contains those styles loads. &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;head&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;crossorigin&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"preload"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/path/to/styles.css"&lt;/span&gt; &lt;span class="na"&gt;as=&lt;/span&gt;&lt;span class="s"&gt;"style"&lt;/span&gt; &lt;span class="na"&gt;onload=&lt;/span&gt;&lt;span class="s"&gt;"this.onload=null;this.rel='stylesheet'"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;noscript&amp;gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/path/to/styles.css"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/noscript&amp;gt;&lt;/span&gt;

  &lt;span class="c"&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;This may look like a hack at first, but this is a really smart and efficient way of loading CSS in an efficient way with proper fallback:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;link rel="preload" as="style"&lt;/code&gt; loads the CSS file in a non-render-blocking way.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;onload="this.onload=null;this.rel='stylesheet'"&lt;/code&gt; makes sure that CSS file is parsed and loaded after the site loads and the &lt;code&gt;onload&lt;/code&gt; function is deleted.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;noscript&lt;/code&gt; fallback makes sure that the CSS loads the standard way if JavaScript is not available.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fh0jixg77rlm1ols7etqt.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fh0jixg77rlm1ols7etqt.gif" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;
Non-critical styles being loaded on a below-the-fold content. Notice how some (critical) styles are already applied (grey background) before the rest is loaded (non-critical)



&lt;p&gt;It's also important to note that &lt;strong&gt;we can load Google Fonts stylesheets in the same efficient way!&lt;/strong&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;link&lt;/span&gt; &lt;span class="na"&gt;crossorigin&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"preload"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://fonts.googleapis.com/css2?family=Montserrat:wght@400;600&amp;amp;display=swap"&lt;/span&gt; &lt;span class="na"&gt;as=&lt;/span&gt;&lt;span class="s"&gt;"style"&lt;/span&gt; &lt;span class="na"&gt;onload=&lt;/span&gt;&lt;span class="s"&gt;"this.onload=null;this.rel='stylesheet'"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;noscript&amp;gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://fonts.googleapis.com/css2?family=Montserrat:wght@400;600&amp;amp;display=swap"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/noscript&amp;gt;&lt;/span&gt;


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

&lt;/div&gt;
&lt;h3&gt;
  
  
  Firefox issue &amp;amp; handling IE
&lt;/h3&gt;

&lt;p&gt;At the time of writing this article, &lt;a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1405761" rel="noopener noreferrer"&gt;Firefox has a bug&lt;/a&gt; related to preloading CSS. This efficient way of loading non-critical CSS currently isn't working on Firefox, but it should be fixed soon.&lt;/p&gt;

&lt;p&gt;You might want to provide a fallback for browsers that do not support preloading or have issue with it (like Firefox). Luckily, this is really easy to do with inline JavaScript.&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;script&amp;gt;&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;isIE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;!!&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MSInputMethodContext&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="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;documentMode&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;isFirefox&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userAgent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;indexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;firefox&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&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="nx"&gt;isIE&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;isFirefox&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;pageStylesheet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;link&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;pageStylesheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;stylesheet&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;pageStylesheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text/css&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;pageStylesheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/path/to/styles.css&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;head&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pageStylesheet&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;/script&amp;gt;&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;We just need to add this code before &lt;code&gt;body&lt;/code&gt; closing tag to insert the regular &lt;code&gt;link&lt;/code&gt; element into &lt;code&gt;head&lt;/code&gt; element for Firefox and IE browsers which do not support preloading.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handling critical JavaScript
&lt;/h2&gt;

&lt;p&gt;We handle critical JavaScript the similar way we handle critical CSS, by inlining it within HTML code. It's important to note that we need to insert critical JavaScript code using &lt;code&gt;script&lt;/code&gt; tag before the &lt;code&gt;body&lt;/code&gt; closing tag. That way we make sure that JavaScript doesn't block content render and all available DOM nodes are created and available to JavaScript code.&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;

&lt;p&gt;&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;&lt;br&gt;
  &lt;span class="c"&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;&lt;br&gt;
    &lt;span class="cm"&gt;/* Inlined JavaScript code */&lt;/span&gt;&lt;br&gt;
  &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;&lt;br&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;&lt;/p&gt;

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

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  Handling non-critical JavaScript&lt;br&gt;
&lt;/h2&gt;

&lt;p&gt;We can handle non-critical JavaScript just by adding &lt;code&gt;defer&lt;/code&gt; or &lt;code&gt;async&lt;/code&gt; tags to &lt;code&gt;script&lt;/code&gt; tag (inline JavaScript or JavaScript loaded from &lt;code&gt;src&lt;/code&gt;).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We use &lt;code&gt;defer&lt;/code&gt; for scripts that need the whole DOM and/or their relative execution order is important. It tells the browser to load the page first and then load the script in the background.&lt;/li&gt;
&lt;li&gt;We use &lt;code&gt;async&lt;/code&gt; for independent scripts that can be executed in any order. This script doesn't wait for any other scripts and can loaded in parallel with other scripts with &lt;code&gt;async&lt;/code&gt; scripts.&lt;/li&gt;
&lt;/ul&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;

&lt;p&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;defer&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/path/to/script.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;&lt;br&gt;
&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;async&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/path/to/script.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;&lt;/p&gt;

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

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  Boosted performance &amp;amp; Lighthouse score&lt;br&gt;
&lt;/h2&gt;

&lt;p&gt;On my personal website I've handled critical and non-critical CSS and JavaScript as I've described in the article. After implementing this modern approach, I've eliminated render-blocking, non-critical CSS and JavaScript which in turn boosted my Lighthouse score and overall performance!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fllf9skh1rppbykj31bxt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fllf9skh1rppbykj31bxt.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;These articles are fueled by coffee. So if you enjoy my work and found it useful, consider buying me a coffee! I would really appreciate it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.buymeacoffee.com/ubnZ8GgDJ" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.buymeacoffee.com%2Fbuttons%2Fdefault-red.png" alt="Buy Me A Coffee"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thank you for taking the time to read this post. If you've found this useful, please give it a ❤️ or 🦄, share and comment.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>css</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Best way to lazy load images for maximum performance</title>
      <dc:creator>Adrian Bece</dc:creator>
      <pubDate>Mon, 24 Aug 2020 05:50:20 +0000</pubDate>
      <link>https://forem.com/prototyp/best-way-to-lazy-load-images-for-maximum-performance-27o1</link>
      <guid>https://forem.com/prototyp/best-way-to-lazy-load-images-for-maximum-performance-27o1</guid>
      <description>&lt;p&gt;Image lazy loading is one of the more popular approaches of optimizing websites due to the relatively easy implementation and large performance gain. With &lt;strong&gt;lazy loading&lt;/strong&gt; we load images asynchronously, meaning that we load images only when they appear in the browser's viewport.&lt;/p&gt;

&lt;p&gt;Almost a year ago, native lazy loading for images and iframes was released for Chrome and other major browsers. The point of the feature is to give browsers control when to request an image or iframe resource, which makes dev jobs a bit easier. Up to that point, only option was to use various JavaScript plugins which monitored the viewport changes and loaded resources dynamically. Now browsers can do that natively!&lt;/p&gt;

&lt;p&gt;At the time of writing this article, around &lt;a href="https://caniuse.com/#search=loading"&gt;73% of currently used browsers&lt;/a&gt; support this feature which is not bad, but we don't want to make the website image content inaccessible and unusable to 27% of potential users.&lt;/p&gt;

&lt;p&gt;So this puts us in an interesting situation: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We want to use the benefits of native lazy loading for browsers that support it&lt;/li&gt;
&lt;li&gt;We want to use a JS plugin as fallback for lazy loading for browsers that don't support it&lt;/li&gt;
&lt;li&gt;We don't want to load the lazy loading JS plugin if the browser supports native lazy loading.&lt;/li&gt;
&lt;li&gt;Support both &lt;code&gt;img&lt;/code&gt; and &lt;code&gt;source&lt;/code&gt; elements is mandatory&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The "loading" attribute
&lt;/h2&gt;

&lt;p&gt;We have three possible values that we can use for &lt;code&gt;loading&lt;/code&gt; attribute.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;auto&lt;/code&gt; - default value. Same as not setting the attribute.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;eager&lt;/code&gt; - load the resource immediately.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;lazy&lt;/code&gt; - load the resource once it's in the viewport.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Although it depends on the use-case, generally we want to use &lt;code&gt;eager&lt;/code&gt; value for resources above the fold and &lt;code&gt;lazy&lt;/code&gt; value for resources below the fold.&lt;/p&gt;

&lt;h2&gt;
  
  
  Modern approach
&lt;/h2&gt;

&lt;p&gt;We need to write a script that will run after the HTML document is loaded. I've used Jekyll and added the script as an include that was appended to the end of the HTML &lt;code&gt;body&lt;/code&gt; element. This is the most effective way of running JavaScript functions to avoid render blocking.&lt;/p&gt;

&lt;h3&gt;
  
  
  Image markup
&lt;/h3&gt;

&lt;p&gt;We want our JavaScript function to start the image loading process based on the native lazy loading feature support. To achieve that we'll add the path to our images to &lt;code&gt;data-src&lt;/code&gt; instead of &lt;code&gt;src&lt;/code&gt;. But we shouldn't leave &lt;code&gt;src&lt;/code&gt; empty, so we'll use 1x1px transparent image placeholder. Our markup for &lt;code&gt;img&lt;/code&gt; elements will look something like this&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;"/path/to/placeholder/image.png"&lt;/span&gt;
    &lt;span class="na"&gt;data-src=&lt;/span&gt;&lt;span class="s"&gt;"/path/to/full/image.jpg"&lt;/span&gt;
    &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"Image description"&lt;/span&gt;
    &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"lazyload"&lt;/span&gt;
    &lt;span class="na"&gt;loading=&lt;/span&gt;&lt;span class="s"&gt;"lazy"&lt;/span&gt;
&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Please note&lt;/strong&gt; that &lt;code&gt;class="lazyload"&lt;/code&gt; is used by the lazyload fallback plugin. I've used &lt;a href="https://github.com/aFarkas/lazysizes"&gt;lazysizes&lt;/a&gt; that uses this particular class name.&lt;/p&gt;

&lt;p&gt;Additionally, we want to support &lt;code&gt;picture&lt;/code&gt; element that contains multiple &lt;code&gt;source&lt;/code&gt; element and fallback &lt;code&gt;img&lt;/code&gt; element.&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;picture&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;source&lt;/span&gt; &lt;span class="na"&gt;data-srcset=&lt;/span&gt;&lt;span class="s"&gt;"path/to/image.webp"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"image/webp"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;source&lt;/span&gt; &lt;span class="na"&gt;data-srcset=&lt;/span&gt;&lt;span class="s"&gt;"path/to/image.jpg"&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;loading=&lt;/span&gt;&lt;span class="s"&gt;"lazy"&lt;/span&gt; 
        &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"lazyload"&lt;/span&gt;
        &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"path/to/placeholder/image.png"&lt;/span&gt;
        &lt;span class="na"&gt;data-src=&lt;/span&gt;&lt;span class="s"&gt;"path/to/image.jpg"&lt;/span&gt;
        &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"Image description"&lt;/span&gt;
    &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/picture&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Feature detection
&lt;/h3&gt;

&lt;p&gt;We need to detect if user's browser supports native lazy loading. Luckily, we can do that using JavaScript directly.&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;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;loading&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;HTMLImageElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="cm"&gt;/* Native lazy loading is supported */&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="cm"&gt;/*  Native lazy loading is not supported */&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Final JavaScript code
&lt;/h3&gt;

&lt;p&gt;For &lt;strong&gt;native lazy loading&lt;/strong&gt;, we only need to assign &lt;code&gt;data-src&lt;/code&gt; value to &lt;code&gt;src&lt;/code&gt; value for &lt;code&gt;img&lt;/code&gt; and &lt;code&gt;source&lt;/code&gt; elements and let the browser handle the rest.&lt;/p&gt;

&lt;p&gt;For &lt;strong&gt;unsupported browsers&lt;/strong&gt;, we only need to load the JavaScript plugin and, optionally, run it (if not done automatically). I've used &lt;a href="https://github.com/aFarkas/lazysizes"&gt;lazysizes&lt;/a&gt; but any plugin will work, just make sure that the markup is correct (class names, data elements, etc.).&lt;/p&gt;

&lt;p&gt;So the final JavaScript code will look something like this:&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;script&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;loading&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;HTMLImageElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;images&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;img[loading="lazy"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;sources&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;source[data-srcset]&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;sources&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;srcset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;srcset&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="nx"&gt;images&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dataset&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="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;script&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;script&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;script&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/link/to/lazyload.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;script&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;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Boosted performance &amp;amp; Lighthouse score
&lt;/h2&gt;

&lt;p&gt;On my &lt;a href="https://codeadrian.github.io/"&gt;personal website&lt;/a&gt; I've used a JavaScript plugin for image lazy loading for all browsers. After implementing this modern approach, I've eliminated one JavaScript file that is being loaded and parsed on website load which in turn boosted my Lighthouse score and overall performance!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fllf9skh1rppbykj31bxt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fllf9skh1rppbykj31bxt.png" alt="Alt Text" width="800" height="239"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  More image optimization techniques for maximum performance
&lt;/h2&gt;

&lt;p&gt;Lazy loading is one of many ways to optimize image performance on the web. I've wrote this in-depth posts that covers other important techniques and aspects of image optimization for the web like web-specific image formats, using CDN, progressive images, etc.&lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/prototyp" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__org__pic"&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nRxnx9N5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://media.dev.to/cdn-cgi/image/width%3D150%2Cheight%3D150%2Cfit%3Dcover%2Cgravity%3Dauto%2Cformat%3Dauto/https%253A%252F%252Fdev-to-uploads.s3.amazonaws.com%252Fuploads%252Forganization%252Fprofile_image%252F1014%252F22219225-134b-498e-a18b-42f9ae9cd91d.jpg" alt="PROTOTYP" width="150" height="150"&gt;
      &lt;div class="ltag__link__user__pic"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--V9WiJ0zi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://media.dev.to/cdn-cgi/image/width%3D150%2Cheight%3D150%2Cfit%3Dcover%2Cgravity%3Dauto%2Cformat%3Dauto/https%253A%252F%252Fdev-to-uploads.s3.amazonaws.com%252Fuploads%252Fuser%252Fprofile_image%252F91468%252Fcdda7312-9dc4-4b73-98ed-fb0c3d55a624.jpg" alt="" width="150" height="150"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/prototyp/optimizing-images-for-the-web-an-in-depth-guide-4j7d" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Optimizing images for the web - an in-depth guide&lt;/h2&gt;
      &lt;h3&gt;Adrian Bece for PROTOTYP ・ Oct 1 '19&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#webdev&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#beginners&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#productivity&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#html&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;




&lt;div class="ltag__link"&gt;
  &lt;a href="/prototyp" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__org__pic"&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nRxnx9N5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://media.dev.to/cdn-cgi/image/width%3D150%2Cheight%3D150%2Cfit%3Dcover%2Cgravity%3Dauto%2Cformat%3Dauto/https%253A%252F%252Fdev-to-uploads.s3.amazonaws.com%252Fuploads%252Forganization%252Fprofile_image%252F1014%252F22219225-134b-498e-a18b-42f9ae9cd91d.jpg" alt="PROTOTYP" width="150" height="150"&gt;
      &lt;div class="ltag__link__user__pic"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--V9WiJ0zi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://media.dev.to/cdn-cgi/image/width%3D150%2Cheight%3D150%2Cfit%3Dcover%2Cgravity%3Dauto%2Cformat%3Dauto/https%253A%252F%252Fdev-to-uploads.s3.amazonaws.com%252Fuploads%252Fuser%252Fprofile_image%252F91468%252Fcdda7312-9dc4-4b73-98ed-fb0c3d55a624.jpg" alt="" width="150" height="150"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/prototyp/improving-website-performance-by-eliminating-render-blocking-css-and-javascript-28ei" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Improving website performance by eliminating render-blocking CSS and JavaScript&lt;/h2&gt;
      &lt;h3&gt;Adrian Bece for PROTOTYP ・ Aug 31 '20&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#javascript&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#webdev&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#css&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#tutorial&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;





&lt;p&gt;These articles are fueled by coffee. So if you enjoy my work and found it useful, consider buying me a coffee! I would really appreciate it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.buymeacoffee.com/ubnZ8GgDJ"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Igf3SR33--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.buymeacoffee.com/buttons/default-red.png" alt="Buy Me A Coffee" width="434" height="100"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thank you for taking the time to read this post. If you've found this useful, please give it a ❤️ or 🦄, share and comment.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>tutorial</category>
      <category>html</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Multi-language routing in React</title>
      <dc:creator>Vlatko Vlahek</dc:creator>
      <pubDate>Wed, 30 Oct 2019 12:55:46 +0000</pubDate>
      <link>https://forem.com/prototyp/multi-language-routing-in-react-k9l</link>
      <guid>https://forem.com/prototyp/multi-language-routing-in-react-k9l</guid>
      <description>&lt;h1&gt;
  
  
  Multi-language routing in React
&lt;/h1&gt;

&lt;p&gt;One of the great things around routing in React is that its ecosystem has allowed for great and very declarative routing syntax. You can define your routing as a set of components, write an object structure from which you will render the routing logic, etc.&lt;/p&gt;

&lt;p&gt;And it’s constantly improving and getting better and better:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://reacttraining.com/blog/react-router-v5-1/"&gt;react-router v5.1 is out&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://reach.tech/router"&gt;reach&lt;/a&gt; router is also getting big traction, check it out.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But, what if you want to support a true multi-language routing, which will support route names in multiple languages and redirect your users to correct routes when you change languages?&lt;/p&gt;

&lt;h3&gt;
  
  
  Is that really a big deal?
&lt;/h3&gt;

&lt;p&gt;Well, it is definitively possible to go on without such a feature and have a fully usable website. There are a lot of websites that have English-only routing, but multi-language content.&lt;/p&gt;

&lt;p&gt;From a &lt;em&gt;development&lt;/em&gt; perspective, the reasons for this vary:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A framework that doesn’t support it is used.&lt;/li&gt;
&lt;li&gt;It’s a big effort to implement.&lt;/li&gt;
&lt;li&gt;It’s not always easy to maintain.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However, having a multi-language route localization can give you and your &lt;em&gt;end-users&lt;/em&gt; the following benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;multi-language SEO&lt;/li&gt;
&lt;li&gt;users get the additional context of the page hierarchy in their own language&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A solution written in React is relatively simple to implement and maintain, and this article will outline the packages and methods that will guide you to a solution.&lt;/p&gt;

&lt;p&gt;The example is written with &lt;em&gt;TypeScript&lt;/em&gt;, latest &lt;em&gt;react-router-dom&lt;/em&gt;, and &lt;em&gt;react-hooks&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Add a Router package
&lt;/h2&gt;

&lt;p&gt;In case you aren’t using a router package, try &lt;a href="https://www.npmjs.com/package/react-router-dom"&gt;react-router-dom&lt;/a&gt; out.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn add react-router-dom
yarn add @types/react-router-dom &lt;span class="nt"&gt;--dev&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;After adding a router, we should define a few routes and components that will be used on our website.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FC&lt;/span&gt; &lt;span class="o"&gt;=&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;BrowserRouter&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;AppLayout&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Switch&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Route&lt;/span&gt; &lt;span class="na"&gt;exact&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;AppRoute&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Home&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;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;views&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Home&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Route&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Route&lt;/span&gt; &lt;span class="na"&gt;exact&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;AppRoute&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Summary&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;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;views&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Summary&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Route&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Route&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"*"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;views&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;GeneralError&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Route&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Switch&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;AppLayout&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;BrowserRouter&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;In the latest react-router-dom version, component and render props were scrapped for children prop which is much more flexible. The only downside is that the v4 version was more concise and readable in most scenarios. Please note that the old way of doing things through component/render props is still available at this moment, but it will become deprecated soon.&lt;/p&gt;

&lt;p&gt;We also added an &lt;strong&gt;&lt;em&gt;AppLayout&lt;/em&gt;&lt;/strong&gt; component which allows us to have a global header, navigation, and footer, and renders the routes inside the main tag as partial views.&lt;/p&gt;

&lt;p&gt;There is also a fallback route here that renders the error component so our users know that they ended up on the error page in case they try to access a route that doesn’t exist.&lt;/p&gt;

&lt;h2&gt;
  
  
  Add an i18n package
&lt;/h2&gt;

&lt;p&gt;First, we need to add a package that will allow us to internationalize things in our app. There are a lot of good examples, but one of the best packages around is &lt;a href="https://github.com/formatjs/react-intl"&gt;react-intl&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It is a project by &lt;a href="https://formatjs.io/"&gt;FormatJS&lt;/a&gt; (Yahoo! Inc) which has impressive support for localizing almost everything, including currencies, dates, etc.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;    yarn add react-intl
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This package was written in Typescript so it has its own types included.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding a base locale
&lt;/h3&gt;

&lt;p&gt;It’s always easiest to start with a language that will be the primary language for the website as a baseline. You can always add more languages easily later on.&lt;/p&gt;

&lt;p&gt;Let’s first add an enum which will be a collection of all languages used inside our app. For start, we will only add the base language.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;enum&lt;/span&gt; &lt;span class="nx"&gt;AppLanguage&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;English&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;en&lt;/span&gt;&lt;span class="dl"&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;The value of each enum property should match a two-letter country code ISO locale.&lt;/p&gt;

&lt;p&gt;After adding a language, we should also add some language strings for that language, which we will use to localize our routes and other content.&lt;/p&gt;

&lt;p&gt;Create an intl folder somewhere in the app, and a file for your base language.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;baseStrings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="cm"&gt;/** Routes */&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;routes.home&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;routes.summary&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/summary&lt;/span&gt;&lt;span class="dl"&gt;'&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;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;LanguageStrings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;baseStrings&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;en&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;baseStrings&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The exported type will be used as an equality enforcer which all other languages need to support, meaning that any localization added to &lt;em&gt;baseStrings&lt;/em&gt; will need to be added to other files in order to enforce some safety. It also works vice-versa.&lt;/p&gt;

&lt;p&gt;If you try to add a string to a specific language which doesn’t exist in the base strings file, you will get a compilation error. This will enforce that all of the used languages have all strings at least set, if not translated, and save you from runtime errors.&lt;/p&gt;

&lt;p&gt;We are also exporting the &lt;em&gt;baseStrings&lt;/em&gt; as a matching iso variable for the language at hand.&lt;/p&gt;

&lt;p&gt;Now let’s add a matching enum (or frozen object in plain JS) which we can use to reference the routes to avoid any typos.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;enum&lt;/span&gt; &lt;span class="nx"&gt;AppRoute&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;Home&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;routes.home&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;Summary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;routes.summary&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Localized Switch component
&lt;/h3&gt;

&lt;p&gt;In order to simplify the process of translating the route paths, we will create a custom &lt;strong&gt;LocalizedSwitch&lt;/strong&gt; component that handles this logic.&lt;/p&gt;

&lt;p&gt;It is also possible to do this on the route component level, however, swapping out the Switch component allows you to support this with the least amount of changes, as it is easier to update the parent then every route to a LocalizedRoute variant. Changing the route component is probably a more flexible solution though.&lt;/p&gt;

&lt;p&gt;The intended suggestion for the LocalisedSwitch component is imagined as a drop-in replacement for the normal Switch one, and it is designed to work with Route components from the &lt;strong&gt;react-router-dom&lt;/strong&gt; package.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;LocalizedSwitch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FC&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&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="cm"&gt;/**
   * inject params and formatMessage through hooks, so we can localize the route
   */&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;formatMessage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;locale&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useIntl&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="cm"&gt;/**
   * Apply localization to all routes
   * Also checks if all children elements are &amp;lt;Route /&amp;gt; components
   */&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Switch&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Children&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;child&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isValidElement&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;RouteProps&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;child&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cloneElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;child&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="nx"&gt;child&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;localizeRoutePath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;child&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;path&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="nx"&gt;child&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Switch&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="cm"&gt;/**
   *
   * @param path can be string, undefined or string array
   * @returns Localized string path or path array
   */&lt;/span&gt;
  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;localizeRoutePath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[])&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="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;path&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;undefined&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;object&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;`/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;locale&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;formatMessage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="p"&gt;}));&lt;/span&gt;
      &lt;span class="nl"&gt;default&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;isFallbackRoute&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;*&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;isFallbackRoute&lt;/span&gt;
          &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;
          &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;locale&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;formatMessage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;path&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="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Wiring it all up
&lt;/h3&gt;

&lt;p&gt;To wire it all together, we need to add the &lt;strong&gt;IntlProvider&lt;/strong&gt; component from the &lt;a href="https://github.com/formatjs/react-intl"&gt;react-intl&lt;/a&gt; package, connect it to the data we defined, and add our own LocalizedSwitch component.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FC&lt;/span&gt; &lt;span class="o"&gt;=&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;LocalizedRouter&lt;/span&gt;
    &lt;span class="na"&gt;RouterComponent&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;BrowserRouter&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="na"&gt;languages&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;AppLanguage&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="na"&gt;appStrings&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;appStrings&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;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;AppLayout&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;LocalizedSwitch&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Route&lt;/span&gt; &lt;span class="na"&gt;exact&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;AppRoute&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Home&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;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;views&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Home&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Route&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Route&lt;/span&gt; &lt;span class="na"&gt;exact&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;AppRoute&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Summary&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;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;views&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Summary&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Route&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Route&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"*"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;views&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;GeneralError&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Route&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;LocalizedSwitch&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;AppLayout&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;LocalizedRouter&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;h2&gt;
  
  
  Supporting multiple languages
&lt;/h2&gt;

&lt;p&gt;Now that we have covered the basics of setting up the logic that allows us to internationalize our application and localize the application routes, we need to add support for other languages and add their route definitions.&lt;/p&gt;

&lt;p&gt;For the purpose of this example, let’s add support for &lt;em&gt;Deutch&lt;/em&gt;, &lt;em&gt;French&lt;/em&gt; and &lt;em&gt;Croatian&lt;/em&gt; languages, all inside the intl folder that we already have.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Disclaimer:&lt;/strong&gt; I don’t personally speak Deutch or French, so if the translations are not perfect, I do apologize for mangling your language, but please blame Google Translate :)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Adding translations for a new language
&lt;/h3&gt;

&lt;p&gt;Just add a new language file inside the intl folder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;de&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;LanguageStrings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="cm"&gt;/** Routes */&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;routes.home&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;routes.summary&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/zusammenfassung&lt;/span&gt;&lt;span class="dl"&gt;'&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;If you are wondering why this was done in &lt;strong&gt;.ts&lt;/strong&gt; file in this scenario, and not another format like JSON, the sole purpose is to enforce safety that comes with using TypeScript.&lt;/p&gt;

&lt;p&gt;You can, of course, write these in JSON, JS or another preferred format in case you don’t want or need the type-safety.&lt;/p&gt;

&lt;p&gt;For every language file you add, extend the &lt;strong&gt;AppLanguage&lt;/strong&gt; enum.&lt;/p&gt;

&lt;h3&gt;
  
  
  Updating the router
&lt;/h3&gt;

&lt;p&gt;We first need to update the router to support redirecting to other languages, reading the current language from the pathname, and setting the locale accordingly.&lt;/p&gt;

&lt;p&gt;Expected behaviour:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/summary -&amp;gt; Redirect to base language
/en/summary -&amp;gt; English language summary page
/de/zusammenfassung -&amp;gt; German language summary page
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We will swap out the default router component with the one that supports pathname detection and returns react-intl provider.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Props&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;RouterComponent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ComponentClass&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;any&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;languages&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="na"&gt;k&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="nl"&gt;appStrings&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="na"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="nx"&gt;LanguageStrings&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="nx"&gt;defaultLanguage&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;AppLanguage&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;LocalizedRouter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FC&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Props&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;RouterComponent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;appStrings&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;defaultLanguage&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;RouterComponent&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Route&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"/:lang([a-z]{2})"&lt;/span&gt;&lt;span class="p"&gt;&amp;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;match&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;location&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="cm"&gt;/**
         * Get current language
         * Set default locale to en if base path is used without a language
         */&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;match&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;match&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt; &lt;span class="p"&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;lang&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;defaultLanguage&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;AppLanguage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;English&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="cm"&gt;/**
         * If language is not in route path, redirect to language root
         */&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;pathname&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;location&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="nx"&gt;pathname&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;lang&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&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="nc"&gt;Redirect&lt;/span&gt; &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;`/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;lang&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/`&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;span class="cm"&gt;/**
         * Return Intl provider with default language set
         */&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;IntlProvider&lt;/span&gt; &lt;span class="na"&gt;locale&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;lang&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;appStrings&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;lang&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;IntlProvider&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Route&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;RouterComponent&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;Wrapping everything in a route, allows us to use regex to determine the language from the pathname, and use that match to inject the current language into the provider.&lt;/p&gt;

&lt;p&gt;Also, our new router component will enforce that a language is always a part of the pathname.&lt;/p&gt;

&lt;p&gt;The regex used in this example will only support lowercase language, but you can modify it to &lt;strong&gt;[a-zA-z]{2}&lt;/strong&gt; and use &lt;strong&gt;String.toLowercase()&lt;/strong&gt; method when pathname matching if you want to support uppercase routes as well.&lt;/p&gt;

&lt;h3&gt;
  
  
  Language switcher
&lt;/h3&gt;

&lt;p&gt;We also need to add a language switcher component that will allow us to change the active language and show the currently activated language based on the pathname.&lt;/p&gt;

&lt;p&gt;Apart from the styling, we need a helper function that checks for matching route inside the strings object for other languages if we want to support navigating to the same page in another language directly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;LanguageSwitcher&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FC&lt;/span&gt; &lt;span class="o"&gt;=&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;pathname&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useLocation&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;locale&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;messages&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useIntl&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;ul&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;css&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;AppLanguage&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lang&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;lang&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;css&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&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;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;NavLink&lt;/span&gt;
            &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;css&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;link&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="na"&gt;activeClassName&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;css&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;link&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;active&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;getMatchingRoute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;AppLanguage&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;lang&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;AppLanguage&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;lang&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;NavLink&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;ul&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;getMatchingRoute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;language&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cm"&gt;/**
     * Get the key of the route the user is currently on
     */&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;route&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;pathname&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;locale&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;routeKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;route&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="cm"&gt;/**
     * Find the matching route for the new language
     */&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;matchingRoute&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;appStrings&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;language&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="nx"&gt;routeKey&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

    &lt;span class="cm"&gt;/**
     * Return localized route
     */&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;language&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;matchingRoute&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;&lt;strong&gt;Navigation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The last thing to do is updating the &lt;strong&gt;Navigation&lt;/strong&gt; component itself, to also support switching to other routes in all languages.&lt;/p&gt;

&lt;p&gt;We simply use the &lt;strong&gt;formatMessage&lt;/strong&gt; function from the react-intl hook for this purpose.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Navigation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FC&lt;/span&gt; &lt;span class="o"&gt;=&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;formatMessage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;locale&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useIntl&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;ul&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;css&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;AppRoute&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;elem&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;elem&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;css&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&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;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;NavLink&lt;/span&gt;
            &lt;span class="na"&gt;exact&lt;/span&gt;
            &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;css&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;link&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="na"&gt;activeClassName&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;css&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;link&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;active&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;localizeRouteKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;AppRoute&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;elem&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;formatMessage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AppRouteTitles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;AppRoute&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;elem&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;NavLink&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;ul&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;localizeRouteKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&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="s2"&gt;`/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;locale&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;formatMessage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;path&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;In order to allow for easier route name resolution, since TS enums don’t allow for reverse mapping on string enums, you can create an ES6 map.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;AppRouteTitles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;AppRoute&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Home&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;home.title&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;AppRoute&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Summary&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;summary.title&lt;/span&gt;&lt;span class="dl"&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;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;As you can see, localizing the routes of the website is not a hard task in React. It requires a few components and little thinking on the side of the project architecture, so you don’t overcomplicate things. The result is easy to understand the solution that will easily scale regardless of the language count that you might add later on.&lt;/p&gt;

&lt;p&gt;A fully working example can be found on:&lt;br&gt;
&lt;a href="https://github.com/vlaja/multilanguage-routing-react"&gt;&lt;strong&gt;vlaja/multilanguage-routing-react&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>react</category>
      <category>routing</category>
      <category>webdev</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>UX lessons learned from eCommerce projects</title>
      <dc:creator>Adrian Bece</dc:creator>
      <pubDate>Mon, 28 Oct 2019 07:10:57 +0000</pubDate>
      <link>https://forem.com/prototyp/ux-lessons-learned-from-ecommerce-projects-18p4</link>
      <guid>https://forem.com/prototyp/ux-lessons-learned-from-ecommerce-projects-18p4</guid>
      <description>&lt;p&gt;I have worked as a certified Magento 1 and Magento 2 frontend developer for an eCommerce agency for 4 years. I've developed and maintained several webshops ranging from small scale to enterprise shops and I've audited numerous websites for frontend performance and UX.&lt;/p&gt;

&lt;p&gt;After moving onto non-eCommerce projects, I've noticed that I could apply the lessons I've learned while working on eCommerce to create very user-friendly and performant frontends with great UX features.&lt;/p&gt;

&lt;p&gt;In this article, I will present a general overview of some of the lessons and UX best practices that I've learned throughout my 4-year career working on eCommerce projects. &lt;/p&gt;

&lt;h2&gt;
  
  
  Reduce user's effort where possible
&lt;/h2&gt;

&lt;p&gt;An eCommerce website can make the shopping experience as clear and simple as possible by providing various helpful features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Saved billing &amp;amp; shipping addresses&lt;/li&gt;
&lt;li&gt;Saved payment methods&lt;/li&gt;
&lt;li&gt;Easy login (social media login)&lt;/li&gt;
&lt;li&gt;Fast checkout with saved customer details&lt;/li&gt;
&lt;li&gt;Customer wishlist&lt;/li&gt;
&lt;li&gt;Clear shopping flow (uniform CTA elements)&lt;/li&gt;
&lt;li&gt;etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By reducing the effort that the user has to invest in shopping and focusing the effort only on areas that require it, we can increase the conversion and overall revenue.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_ei0OeeA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/0yplzytoyy13y4crlqe9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_ei0OeeA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/0yplzytoyy13y4crlqe9.png" alt="Alt Text" width="775" height="330"&gt;&lt;/a&gt;&lt;/p&gt;
Example of stored payment method on Magento 2 demo



&lt;p&gt;With that in mind, we can work on reducing the user effort by offering the user various ways of speeding up and simplifying the flow. It's important to take note of repeating processes in the flow and processes that could be automated or sped up.&lt;/p&gt;

&lt;h2&gt;
  
  
  Header &amp;amp; first impression of a website
&lt;/h2&gt;

&lt;p&gt;Header and its navigation represent the webshop brand, informs the customer about types of products and services that it offers and provides helpful information.&lt;/p&gt;

&lt;p&gt;It has several functions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Represents the brand (with logo)&lt;/li&gt;
&lt;li&gt;Navigation gives a general overview of the product catalog (top-level categories)&lt;/li&gt;
&lt;li&gt;Displays account state (guest or logged in)&lt;/li&gt;
&lt;li&gt;Displays cart state (amount of products in the cart)&lt;/li&gt;
&lt;li&gt;Displays a search element (input or a toggle)&lt;/li&gt;
&lt;li&gt;(optional) Provide only the most helpful links (FAQ, billing, shipping, returns, etc.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Z-ztrRcO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/7jew5bot8ugej5abs1j0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Z-ztrRcO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/7jew5bot8ugej5abs1j0.png" alt="Alt Text" width="800" height="76"&gt;&lt;/a&gt;&lt;/p&gt;
Standard webshop navigation with branding, categories, cart, account and helpful link elements



&lt;p&gt;Looking at the header, the customer needs to become familiar with the branding and instantly know what kind of content and general topics that can be found on the website. Alongside that, the header needs to provide helpful tools and links that can make the site navigation easier.&lt;/p&gt;

&lt;h2&gt;
  
  
  Call To Action elements
&lt;/h2&gt;

&lt;p&gt;In eCommerce, Call To Action (CTA elements) need to have the most visual weight on the page, be uniform and lead the user down the path from browsing the product catalog, adding individual product(s) to the cart, and successfully finishing the checkout.&lt;/p&gt;

&lt;p&gt;For example, the "Subscribe" button on a Newsletter element should have less visual weight than the "Add to cart" button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wRvIu_do--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/3wkouspp7xe9u3c35ovl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wRvIu_do--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/3wkouspp7xe9u3c35ovl.png" alt="Alt Text" width="800" height="538"&gt;&lt;/a&gt;&lt;/p&gt;
Prominent CTA button



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Vo1D0UcE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/ta0hcve3vuxuco2pvf0c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Vo1D0UcE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/ta0hcve3vuxuco2pvf0c.png" alt="Alt Text" width="800" height="536"&gt;&lt;/a&gt;&lt;/p&gt;
Prominent CTA button



&lt;p&gt;CTA elements that have the most visual weight on the site need to guide a user in the main website flow or the flow they choose to follow. Having many CTA elements on the page can be confusing to the user, so it's important to establish an element hierarchy and use the appropriate element for a context (primary flow, secondary flow, optional flow, etc.).&lt;/p&gt;

&lt;h2&gt;
  
  
  Importance of responsive design
&lt;/h2&gt;

&lt;p&gt;There are more users accessing the web from a mobile device than a desktop device, so more and more customers are using mobile devices to shop online. More and more websites focus on having a great, if not superior, UX on mobile and tablet devices. Revenue and number of purchases on mobile and tablet devices continue to grow year after year.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;79% of smartphone users have made a purchase online using their mobile device in the last 6 months&lt;/p&gt;

&lt;p&gt;Almost 40% of all eCommerce purchases during the 2018 holiday season were made on a smartphone.&lt;/p&gt;
&lt;/blockquote&gt;
Source: Mobile eCommerce Stats in 2018 and the Future Online Shopping Trends of mCommerce



&lt;p&gt;By using analytics tools like Google Analytics, we can determine the ratio of users using mobile devices to users using desktop devices and track various metrics (like conversion rates) for each group. Website owners and management can make various decisions when improving the site or planning new features.&lt;/p&gt;

&lt;h2&gt;
  
  
  Every second of loading time matters
&lt;/h2&gt;

&lt;p&gt;How much does &lt;strong&gt;each second&lt;/strong&gt; of loading cost?&lt;/p&gt;

&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Pages that loaded in 2.4 seconds had a 1.9% conversion rate&lt;/li&gt;
&lt;li&gt;At 3.3 seconds, conversion rate was 1.5%&lt;/li&gt;
&lt;li&gt;At 4.2 seconds, conversion rate was less than 1%&lt;/li&gt;
&lt;li&gt;At 5.7+ seconds, conversion rate was 0.6%&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
Source: Cloudflare - How Website Performance Affects Conversion Rates



&lt;p&gt;It's not all that surprising that users will abandon the site if it loads too slow. In the context of an eCommerce site, if a user abandons the site due to the large loading times right before checking out, it results in a revenue loss.&lt;/p&gt;

&lt;p&gt;Many webshops found that improving the loading times and optimizing the frontend and backend resulted in a significant revenue increase.&lt;/p&gt;

&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Walmart found that for every 1 second improvement in page load time, conversions increased by 2%&lt;/li&gt;
&lt;li&gt;COOK increased conversions by 7% by reducing page load time by 0.85 seconds&lt;/li&gt;
&lt;li&gt;Mobify found that each 100ms improvement in their homepage's load time resulted in a 1.11% increase in conversion&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
Source: Cloudflare - How Website Performance Affects Conversion Rates



&lt;p&gt;After working on eCommerce projects, I've started optimizing code and asset file size right from the start, eliminating any potential performance bottlenecks right from the start. These bottlenecks and issues are common for all websites. I've covered the subject of improving frontend performance in some of my previous articles.&lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/prototyp" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__org__pic"&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nRxnx9N5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://media.dev.to/cdn-cgi/image/width%3D150%2Cheight%3D150%2Cfit%3Dcover%2Cgravity%3Dauto%2Cformat%3Dauto/https%253A%252F%252Fdev-to-uploads.s3.amazonaws.com%252Fuploads%252Forganization%252Fprofile_image%252F1014%252F22219225-134b-498e-a18b-42f9ae9cd91d.jpg" alt="PROTOTYP" width="150" height="150"&gt;
      &lt;div class="ltag__link__user__pic"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--V9WiJ0zi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://media.dev.to/cdn-cgi/image/width%3D150%2Cheight%3D150%2Cfit%3Dcover%2Cgravity%3Dauto%2Cformat%3Dauto/https%253A%252F%252Fdev-to-uploads.s3.amazonaws.com%252Fuploads%252Fuser%252Fprofile_image%252F91468%252Fcdda7312-9dc4-4b73-98ed-fb0c3d55a624.jpg" alt="" width="150" height="150"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/prototyp/optimizing-images-for-the-web-an-in-depth-guide-4j7d" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Optimizing images for the web - an in-depth guide&lt;/h2&gt;
      &lt;h3&gt;Adrian Bece for PROTOTYP ・ Oct 1 '19&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#webdev&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#beginners&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#productivity&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#html&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;



&lt;div class="ltag__link"&gt;
  &lt;a href="/prototyp" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__org__pic"&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nRxnx9N5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://media.dev.to/cdn-cgi/image/width%3D150%2Cheight%3D150%2Cfit%3Dcover%2Cgravity%3Dauto%2Cformat%3Dauto/https%253A%252F%252Fdev-to-uploads.s3.amazonaws.com%252Fuploads%252Forganization%252Fprofile_image%252F1014%252F22219225-134b-498e-a18b-42f9ae9cd91d.jpg" alt="PROTOTYP" width="150" height="150"&gt;
      &lt;div class="ltag__link__user__pic"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--V9WiJ0zi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://media.dev.to/cdn-cgi/image/width%3D150%2Cheight%3D150%2Cfit%3Dcover%2Cgravity%3Dauto%2Cformat%3Dauto/https%253A%252F%252Fdev-to-uploads.s3.amazonaws.com%252Fuploads%252Fuser%252Fprofile_image%252F91468%252Fcdda7312-9dc4-4b73-98ed-fb0c3d55a624.jpg" alt="" width="150" height="150"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/prototyp/improving-css-performance-and-file-size-an-in-depth-guide-4mb5" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Improving CSS performance and file size - an in-depth guide&lt;/h2&gt;
      &lt;h3&gt;Adrian Bece for PROTOTYP ・ Oct 16 '19&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#css&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#webdev&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#productivity&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;



&lt;div class="ltag__link"&gt;
  &lt;a href="/prototyp" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__org__pic"&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nRxnx9N5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://media.dev.to/cdn-cgi/image/width%3D150%2Cheight%3D150%2Cfit%3Dcover%2Cgravity%3Dauto%2Cformat%3Dauto/https%253A%252F%252Fdev-to-uploads.s3.amazonaws.com%252Fuploads%252Forganization%252Fprofile_image%252F1014%252F22219225-134b-498e-a18b-42f9ae9cd91d.jpg" alt="PROTOTYP" width="150" height="150"&gt;
      &lt;div class="ltag__link__user__pic"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--V9WiJ0zi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://media.dev.to/cdn-cgi/image/width%3D150%2Cheight%3D150%2Cfit%3Dcover%2Cgravity%3Dauto%2Cformat%3Dauto/https%253A%252F%252Fdev-to-uploads.s3.amazonaws.com%252Fuploads%252Fuser%252Fprofile_image%252F91468%252Fcdda7312-9dc4-4b73-98ed-fb0c3d55a624.jpg" alt="" width="150" height="150"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/prototyp/implementing-skeleton-loading-in-react-kia" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Implementing Skeleton Loading in React&lt;/h2&gt;
      &lt;h3&gt;Adrian Bece for PROTOTYP ・ Sep 4 '19&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#react&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#webdev&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#tutorial&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  Using schema.org to describe the content
&lt;/h2&gt;

&lt;p&gt;By adding &lt;code&gt;schema.org&lt;/code&gt; tags to a website, Google and other search engines can better understand the site they crawl and display. &lt;code&gt;schema.org&lt;/code&gt; is just a collection of simple HTML attributes that can be easily added to websites. There are a lot of use-cases and tags that can describe your website in great detail and can be a topic of a future article due to the number of possible use-cases and options.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Hzr8xFl8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/3iz4qgmb5dn0450whyme.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Hzr8xFl8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/3iz4qgmb5dn0450whyme.png" alt="Alt Text" width="800" height="970"&gt;&lt;/a&gt;&lt;/p&gt;
eCommerce website using schema.org tags



&lt;p&gt;Here are some useful links on how to implement &lt;code&gt;schema.org&lt;/code&gt; tags&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://schema.org/docs/schemas.html"&gt;Schema.org schemas&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.osiaffiliate.com/blog/how-to-use-schema-org-for-ecommerce-websites/"&gt;How to use schema.org for ecommerce websites&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developers.google.com/search/docs/guides/intro-structured-data"&gt;Understand how structured data works&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Analyzing user flow to determine performance bottlenecks - category page example
&lt;/h2&gt;

&lt;p&gt;The standard category page consists of a product catalog (displayed in a grid or a list), a toolbar with sorting and display options, layered navigation (filters) and pagination.&lt;/p&gt;

&lt;p&gt;This layout usually also applies to the search results page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qOYq7L94--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/y1kacg0yubqopnybo9ie.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qOYq7L94--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/y1kacg0yubqopnybo9ie.png" alt="Alt Text" width="800" height="409"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;From what we know, we can notice the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This page is reloaded each time user interacts with filters, sorting options, display options, pagination or the search page&lt;/li&gt;
&lt;li&gt;Images play the biggest part in page size and number of requests&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, If there are 24 products displayed on a category page with only 8 items in the user's viewport on load, we are downloading 16 images that the user might not even see, but it slows down a page load considerably (depending on image size and optimization).&lt;/p&gt;

&lt;p&gt;With that in mind, the best improvement that we can do on a category page is to optimize image sizes and add lazy loading. I've gone into more detail about image optimization in the following article.&lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/prototyp" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__org__pic"&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nRxnx9N5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://media.dev.to/cdn-cgi/image/width%3D150%2Cheight%3D150%2Cfit%3Dcover%2Cgravity%3Dauto%2Cformat%3Dauto/https%253A%252F%252Fdev-to-uploads.s3.amazonaws.com%252Fuploads%252Forganization%252Fprofile_image%252F1014%252F22219225-134b-498e-a18b-42f9ae9cd91d.jpg" alt="PROTOTYP" width="150" height="150"&gt;
      &lt;div class="ltag__link__user__pic"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--V9WiJ0zi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://media.dev.to/cdn-cgi/image/width%3D150%2Cheight%3D150%2Cfit%3Dcover%2Cgravity%3Dauto%2Cformat%3Dauto/https%253A%252F%252Fdev-to-uploads.s3.amazonaws.com%252Fuploads%252Fuser%252Fprofile_image%252F91468%252Fcdda7312-9dc4-4b73-98ed-fb0c3d55a624.jpg" alt="" width="150" height="150"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/prototyp/optimizing-images-for-the-web-an-in-depth-guide-4j7d" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Optimizing images for the web - an in-depth guide&lt;/h2&gt;
      &lt;h3&gt;Adrian Bece for PROTOTYP ・ Oct 1 '19&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#webdev&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#beginners&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#productivity&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#html&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;p&gt;By following user flow, we can determine potential bottlenecks on the page and optimize them from the start. It's also helpful to be aware of the type of content that is going to be on the page (images, text, videos) and apply optimization techniques accordingly.&lt;/p&gt;

&lt;h2&gt;
  
  
  User focus and forms - checkout page example
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Removing distracting page elements
&lt;/h3&gt;

&lt;p&gt;When the user decides to check out and buy the items in their cart, we need to ensure that the user doesn't get distracted and leaves the checkout page. The best way of doing that is to simplify the header and footer (or remove footer entirely). Ideal checkout page consists of the following elements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Header with branding only - the user is reassured that they are on the same website, but they are not distracted by navigation and links&lt;/li&gt;
&lt;li&gt;Checkout form - divided into sections or steps. &lt;/li&gt;
&lt;li&gt;Security section (short info, lock icon, etc.) - the user is reassured that the data they provide in checkout (address, payment data, etc.) is safe and that they can trust the website.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's take a look at the example of a simplified page layout on checkout.&lt;/p&gt;

&lt;p&gt;First, let's take a look at how the header looks like on the homepage.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--BKB6oLw6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/8u8bgs6s829qfrnika0b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BKB6oLw6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/8u8bgs6s829qfrnika0b.png" alt="Alt Text" width="800" height="471"&gt;&lt;/a&gt;&lt;/p&gt;
Harry's landing page - notice the header



&lt;p&gt;And this is how it looks like on checkout. It keeps the user focused on the checkout form, while reassuring users that they've stayed on the same website.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---3o6G07N--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/oygm5ozrgaj1x9fcgfug.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---3o6G07N--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/oygm5ozrgaj1x9fcgfug.png" alt="Alt Text" width="800" height="652"&gt;&lt;/a&gt;&lt;/p&gt;
Harry's checkout - notice how header is simplified



&lt;p&gt;We have the ability to guide the user's focus while the user browses the website. It's important to know when we need to reduce the number of distracting elements on the page to focus the user on the important flow or give the user many focus points on the page.&lt;/p&gt;

&lt;h3&gt;
  
  
  Form layout
&lt;/h3&gt;

&lt;p&gt;Often times, it's tempting to create multi-column forms, but this can cause some issues. Users can take different paths when filling out those forms.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2NgHKA0n--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/loxkl1qzzb9q01jqyiq8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2NgHKA0n--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/loxkl1qzzb9q01jqyiq8.png" alt="Alt Text" width="800" height="657"&gt;&lt;/a&gt;&lt;/p&gt;
Users can fill out multi-column forms in different ways



&lt;p&gt;This can become very confusing to the user and they could lose the sense of progression, and might not check out successfully.&lt;/p&gt;

&lt;p&gt;By &lt;strong&gt;keeping the form inputs in a single column&lt;/strong&gt; and &lt;strong&gt;dividing them into sections and steps&lt;/strong&gt; customer can scan the inputs more clearly, have a better sense of progression and have a better chance of successfully submitting the form and following the user flow.&lt;/p&gt;

&lt;h3&gt;
  
  
  Form validation
&lt;/h3&gt;

&lt;p&gt;On checkout, form validation needs to happen as the user is typing data. &lt;/p&gt;

&lt;p&gt;If we run validation when the user clicks the "Submit" button and there is an error somewhere in among a handful of inputs, the user needs to go back and find the error and fix it before proceeding. This ruins the user's sense of progression and doesn't reassure them that their data is valid in time.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hjF32gev--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/eo4zl6lcmoz53dq5use3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hjF32gev--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/eo4zl6lcmoz53dq5use3.png" alt="Alt Text" width="800" height="566"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Additionally, &lt;strong&gt;required&lt;/strong&gt; fields need to be clearly marked and their labels need to stand out more prominently in the form.&lt;/p&gt;

&lt;p&gt;If the data like address or telephone needs to follow a certain pattern or format, this needs to be clearly communicated to the user via tooltip next to the form.&lt;/p&gt;

&lt;p&gt;Additionally, you can also use those tooltips to reassure users that their email and/or telephone number will only be used for purposes important to the website flow and not for promotion (unless the user doesn't explicitly gives permission).&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;After 4 years of working in the eCommerce industry, I've learned how properly follow the user flow on the page and pinpoint potential performance and UX bottlenecks, various UX best practices, how every second of load time matters, etc. I've successfully managed to apply these lessons when I moved onto projects beyond eCommerce and it changed the way I've looked at and analyzed the websites.&lt;/p&gt;

&lt;p&gt;I hope that you've found this article helpful and that you'll be able to apply these UX improvements in your projects. I've only covered a handful of UX lessons I've learned, but feel free to share your own UX tips and thoughts in the comments.&lt;/p&gt;




&lt;p&gt;These articles are fueled by coffee. So if you enjoy my work and found it useful, consider buying me a coffee! I would really appreciate it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.buymeacoffee.com/ubnZ8GgDJ"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Igf3SR33--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.buymeacoffee.com/buttons/default-red.png" alt="Buy Me A Coffee" width="434" height="100"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thank you for taking the time to read this post. If you've found this useful, please give it a ❤️ or 🦄, share and comment.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>ux</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Improving CSS performance and file size - an in-depth guide</title>
      <dc:creator>Adrian Bece</dc:creator>
      <pubDate>Wed, 16 Oct 2019 05:35:02 +0000</pubDate>
      <link>https://forem.com/prototyp/improving-css-performance-and-file-size-an-in-depth-guide-4mb5</link>
      <guid>https://forem.com/prototyp/improving-css-performance-and-file-size-an-in-depth-guide-4mb5</guid>
      <description>&lt;h2&gt;
  
  
  Table Of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
What makes CSS slow

&lt;ul&gt;
&lt;li&gt;File size&lt;/li&gt;
&lt;li&gt;CSS rendering&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

Reducing CSS file size

&lt;ul&gt;
&lt;li&gt;Minification&lt;/li&gt;
&lt;li&gt;Optimization&lt;/li&gt;
&lt;li&gt;Compression&lt;/li&gt;
&lt;li&gt;CSS structure&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

Improving CSS animation performance

&lt;ul&gt;
&lt;li&gt;Transitions and animations&lt;/li&gt;
&lt;li&gt;Promoting element into a composite layer&lt;/li&gt;
&lt;li&gt;will-change property&lt;/li&gt;
&lt;li&gt;contain property&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Bonus: CSS Performance tips&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  What makes CSS slow?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  File size
&lt;/h3&gt;

&lt;p&gt;Large file size results in larger download times and parsing times. It's simple as that.&lt;/p&gt;

&lt;h3&gt;
  
  
  CSS Rendering
&lt;/h3&gt;

&lt;p&gt;In order to understand where CSS rendering bottlenecks may come up, we need to look at the render pipeline steps:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Style&lt;/strong&gt; - Determines which CSS rules apply to which elements in an HTML document.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Layout&lt;/strong&gt; - Determines where the element is positioned on the screen and how much space does it take. CSS properties: &lt;code&gt;position&lt;/code&gt;, &lt;code&gt;top&lt;/code&gt;, &lt;code&gt;left&lt;/code&gt;, &lt;code&gt;right&lt;/code&gt;, &lt;code&gt;bottom&lt;/code&gt;, etc.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Paint&lt;/strong&gt; - Determines visual elements. CSS properties: &lt;code&gt;color&lt;/code&gt;, &lt;code&gt;border&lt;/code&gt;, &lt;code&gt;background-color&lt;/code&gt;, etc.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Composite&lt;/strong&gt; - Draws layers onto the screen. CSS properties: &lt;code&gt;opacity&lt;/code&gt; and &lt;code&gt;transform&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fisrw4tppnca6qdol6k5y.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fisrw4tppnca6qdol6k5y.jpg" alt="The full pixel pipeline"&gt;&lt;/a&gt;&lt;/p&gt;
The full pixel pipeline (from Google Developers site)



&lt;p&gt;CSS offers a lot of options in terms of animation and transitions. We can create a transition on a lot of CSS properties. There are transitions and animations which can use GPU and perform really well, resulting in smooth 60 FPS (Frames per second) experience with fluid and smooth animations. On the flipš side, there are CSS properties that, when animated, can result in low FPS and choppy animations.&lt;/p&gt;

&lt;p&gt;Any changes or updates happening on one of the steps cause updates on the following steps. We can see that the &lt;strong&gt;Layout step is the most expensive&lt;/strong&gt; to animate and &lt;strong&gt;Composite step is the least expensive&lt;/strong&gt; to animate.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reducing CSS file size
&lt;/h2&gt;

&lt;p&gt;By decreasing the file size of the CSS file, we can decrease the download time and loading time. Some improvements related to file size can be done even when we are finished working with CSS making it a very effective optimization. For maximum efficiency, it's best to keep in mind and apply these improvements from the start.&lt;/p&gt;

&lt;h3&gt;
  
  
  Minification
&lt;/h3&gt;

&lt;p&gt;CSS file minification is an easy win and should be the first step for achieving better CSS performance. Minification removes formatting characters inside the CSS file (whitespaces, line breaks, etc.) which are only useful to us (makes the code readable).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* Non-minified code */&lt;/span&gt;

&lt;span class="nc"&gt;.card&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1rem&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="m"&gt;#aaa&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;/* Minified code */&lt;/span&gt;

&lt;span class="nc"&gt;.card&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;1rem&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="m"&gt;#aaa&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;There are numerous online, automated and CDN &lt;strong&gt;"uglifying"&lt;/strong&gt; tools that will help you with minification. Tools for CSS minification:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://csscompressor.com/" rel="noopener noreferrer"&gt;CSS Compressor&lt;/a&gt; - simple online minification&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/cssnano/cssnano" rel="noopener noreferrer"&gt;CSSNano&lt;/a&gt; - Automated CSS optimizer + minifier&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/jakubpawlowicz/clean-css" rel="noopener noreferrer"&gt;Clean CSS&lt;/a&gt; - Automated CSS optimizer + minifier&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Optimization
&lt;/h3&gt;

&lt;p&gt;Wait... We've already minified our CSS file. Isn't it already optimized?&lt;/p&gt;

&lt;p&gt;Well, technically it is. When talking about CSS optimization, we are looking at removing unnecessary CSS or reformatting CSS properties that reduce the number of characters. Unlike minification, this improvement cannot be done on CDN and it must be done when code is being compiled (by a tool like Webpack or Gulp).&lt;/p&gt;

&lt;p&gt;Let's take a look at the following example.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* Unoptimized CSS */&lt;/span&gt;

&lt;span class="nc"&gt;.card&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="m"&gt;#aaaaaa&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.5rem&lt;/span&gt; &lt;span class="m"&gt;1rem&lt;/span&gt; &lt;span class="m"&gt;0.5rem&lt;/span&gt; &lt;span class="m"&gt;1rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.card__content&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.5rem&lt;/span&gt; &lt;span class="m"&gt;1rem&lt;/span&gt; &lt;span class="m"&gt;0.5rem&lt;/span&gt; &lt;span class="m"&gt;1rem&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;At first look, this looks like any regular CSS that anyone would write and not think twice about it. But there are some optimizations that could be done to reduce the number of characters.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* Optimized CSS */&lt;/span&gt;

&lt;span class="nc"&gt;.card&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="m"&gt;#aaa&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.card&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nc"&gt;.card__content&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;.5rem&lt;/span&gt; &lt;span class="m"&gt;1rem&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;We have reduced the number of characters from 132 to 85 (a 35% reduction). Let's apply minification and see the final result.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* Optimized CSS with minification */&lt;/span&gt;

&lt;span class="nc"&gt;.card&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="m"&gt;#aaa&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="nc"&gt;.card&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="nc"&gt;.card__content&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;.5rem&lt;/span&gt; &lt;span class="m"&gt;1rem&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;We have reduced the number of characters from 132 to 68 (48% reduction in total).&lt;/p&gt;

&lt;p&gt;You can see how much CSS impact the optimization has. The reduction is a result of removing the DRY code which can be avoided in this simple example. Looking at the CSS file with thousand of lines of code which is being changed on a regular basis (especially if split into several files), we can easily make our code less DRY. Luckily, there are tools that help us optimize our code and keep it DRY.&lt;/p&gt;

&lt;p&gt;In the previous section, we have mentioned two of the numerous tools for optimizing CSS with minification included:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/cssnano/cssnano" rel="noopener noreferrer"&gt;CSSNano&lt;/a&gt; - Automated CSS optimizer + minifier&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/jakubpawlowicz/clean-css" rel="noopener noreferrer"&gt;Clean CSS&lt;/a&gt; - Automated CSS optimizer + minifier&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Compression
&lt;/h3&gt;

&lt;p&gt;Compression is done server-side. It uses gzip or Brotli to compress and further reduce the file size in order to reduce the file download time.&lt;/p&gt;

&lt;p&gt;This topic is very extensive and covers more file types than just CSS. CSS tricks wrote a &lt;a href="https://css-tricks.com/brotli-static-compression/" rel="noopener noreferrer"&gt;great article on this topic&lt;/a&gt; if you are interested in implementing server-side compression.&lt;/p&gt;

&lt;p&gt;It's also important to note that most CDN services offer the compression option out of the box.&lt;/p&gt;
&lt;h3&gt;
  
  
  CSS structure
&lt;/h3&gt;

&lt;p&gt;Having a good CSS structure and using various CSS best practices and also result in a lower-sized CSS file when minified. For example, using BEM and only class selectors with low specificity will result in fewer characters in the file and the lower file size.&lt;/p&gt;

&lt;p&gt;I've covered CSS structure improvements in one of my previous posts. By following these CSS principles, you will have a CSS file with great structure, DRY code and a great foundation for optimization and minification.&lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/prototyp" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__org__pic"&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F1014%2F22219225-134b-498e-a18b-42f9ae9cd91d.jpg" alt="PROTOTYP"&gt;
      &lt;div class="ltag__link__user__pic"&gt;
        &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F91468%2Fcdda7312-9dc4-4b73-98ed-fb0c3d55a624.jpg" alt=""&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/prototyp/improve-your-css-with-these-5-principles-35jd" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Improve your CSS with these 5 principles&lt;/h2&gt;
      &lt;h3&gt;Adrian Bece for PROTOTYP ・ Oct 7 '19&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#css&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#webdev&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;



&lt;h2&gt;
  
  
  Improving CSS animation performance
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Transitions and animations
&lt;/h3&gt;

&lt;p&gt;We've covered the CSS rendering pipeline and what makes animation slow. So basically, &lt;strong&gt;Layout step is the most expensive&lt;/strong&gt; to animate and &lt;strong&gt;Composite step is the least expensive&lt;/strong&gt; to animate.&lt;/p&gt;

&lt;p&gt;If we look at the CSS properties that cause the Composite layer to update, they are &lt;code&gt;transform&lt;/code&gt; and &lt;code&gt;opacity&lt;/code&gt;. These two properties use hardware (GPU) acceleration, resulting in very performant updates.&lt;/p&gt;

&lt;p&gt;If you apply a transition to position attributes like &lt;code&gt;top&lt;/code&gt; or &lt;code&gt;left&lt;/code&gt;, animation performance will be poor because we are updating the &lt;strong&gt;Layout&lt;/strong&gt; step which forces both &lt;strong&gt;Paint&lt;/strong&gt; and &lt;strong&gt;Composite&lt;/strong&gt; to update. Replacing the position CSS properties with &lt;code&gt;transform&lt;/code&gt; and using transition on it, we are updating &lt;strong&gt;Composite&lt;/strong&gt; step only, resulting in smooth animation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Promoting element into a composite layer
&lt;/h3&gt;

&lt;p&gt;We aren't really restricted to using only &lt;code&gt;transform&lt;/code&gt; and &lt;code&gt;opacity&lt;/code&gt; for smooth animations. CSS offers quite a few ways of "promoting" an element into a composite layer to tell the browser that this element will have its Paint CSS properties updated often.&lt;/p&gt;

&lt;p&gt;The hacky and "old way" of doing this is by either adding &lt;code&gt;backface-visibility: hidden;&lt;/code&gt; or &lt;code&gt;transform: translate3d(0,0,0);&lt;/code&gt;. In most cases, these properties won't affect your element in any way (if they don't override anything), but they will make the browser move the element into Composite layer, significantly improving animation performance.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.example&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="m"&gt;20rem&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="m"&gt;20rem&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="m"&gt;#aaa&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nl"&gt;border-radius&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;backface-visibility&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;hidden&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;border-radius&lt;/span&gt; &lt;span class="m"&gt;0.3s&lt;/span&gt; &lt;span class="n"&gt;ease-in-out&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.example&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;50%&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;h3&gt;
  
  
  will-change property
&lt;/h3&gt;

&lt;p&gt;A modern way of telling browsers which element is expected to be updated (in terms of CSS rendering) is setting the &lt;code&gt;will-change&lt;/code&gt; property.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.card&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="m"&gt;#aaa&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;0.3s&lt;/span&gt; &lt;span class="n"&gt;ease-in-out&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;will-change&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.card&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;will-change&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.card&lt;/span&gt;&lt;span class="nd"&gt;:active&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="m"&gt;#bbb&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;There are several things to keep in mind when using &lt;code&gt;will-change&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Don't apply will-change to too many elements.&lt;/li&gt;
&lt;li&gt;Use sparingly&lt;/li&gt;
&lt;li&gt;Don't apply will-change to elements to perform premature optimization.&lt;/li&gt;
&lt;li&gt;Give it sufficient time to work.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  contain property
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;contain&lt;/code&gt; CSS property represents the future of CSS render pipeline optimization. Currently, it sits at limited browser support. It allows us to indicate that an element’s subtree is independent of the rest of the page. This allows the browser to recalculate layout, style, paint, size, or any combination of them for a limited area of the DOM and not the entire page.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;contain&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;none&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="c"&gt;/* No optimization */&lt;/span&gt;
&lt;span class="nt"&gt;contain&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;strict&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="c"&gt;/* Equivalent to contain: layout paint size style */&lt;/span&gt;
&lt;span class="nt"&gt;contain&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;content&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="c"&gt;/* Equivalent to contain: layout paint style */&lt;/span&gt;
&lt;span class="nt"&gt;contain&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;size&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="c"&gt;/* component size is set and no descendant will modify its size.  */&lt;/span&gt;
&lt;span class="nt"&gt;contain&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;layout&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="c"&gt;/*  changes to any descendant of this element will not affect the layout of any outside element and vice versa.  */&lt;/span&gt;
&lt;span class="nt"&gt;contain&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;style&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="c"&gt;/* descendant’s styles will not affect outside elements.  */&lt;/span&gt;
&lt;span class="nt"&gt;contain&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;paint&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="c"&gt;/* you specify that no descendant will display outside the elements bounds. Similar to overflow: hidden; */&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Example of using &lt;code&gt;contain&lt;/code&gt; CSS property:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.box&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;contain&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;layout&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c"&gt;/* Won't change size and won't affect layout */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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;"box"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&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;"box__content"&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;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

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

&lt;/div&gt;

&lt;h2&gt;
  
  
  Bonus: CSS Performance tips
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Avoid using @import
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;@import&lt;/code&gt; CSS property is used to include a CSS file within another CSS file. The issue is that these files are then loaded one after another instead of parallel when using the recommended HTML way of including stylesheets.&lt;/p&gt;
&lt;h3&gt;
  
  
  Use HTTP/2 if you have multiple stylesheets
&lt;/h3&gt;

&lt;p&gt;HTTP/2 brings noticeable performance improvements when loading a lot of smaller files. This is an ideal solution if you are using CSS structure that results in multiple CSS stylesheets (individual screens, modules, components, etc.).&lt;/p&gt;

&lt;p&gt;If you are not using HTTP/2, it's recommended to merge all your CSS files into a single CSS file.&lt;/p&gt;
&lt;h3&gt;
  
  
  Be aware of "expensive" properties
&lt;/h3&gt;

&lt;p&gt;Some properties are more resource-heavy and increase render time. If you are intending to support devices with lower processing power, being aware of these properties would be beneficial:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;border-radius
box-shadow
filter
position: fixed

*
:nth-child
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Serve CSS file(s) over the CDN
&lt;/h3&gt;

&lt;p&gt;Serving static files like images, CSS, JS, etc. from CDN improves download times. Additionally, some CDN services offer basic minification and optimization options, so you can easily get a performance boost if your code is not minified already.&lt;/p&gt;
&lt;h3&gt;
  
  
  Keep your selectors simple
&lt;/h3&gt;

&lt;p&gt;Having simple class selectors (consider using BEM and OOCSS or other flavors and combinations) can benefit performance when rendering your site, especially on devices with lower processing power. I've covered this topic more in-depth in one of my previous articles:&lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/prototyp" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__org__pic"&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F1014%2F22219225-134b-498e-a18b-42f9ae9cd91d.jpg" alt="PROTOTYP"&gt;
      &lt;div class="ltag__link__user__pic"&gt;
        &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F91468%2Fcdda7312-9dc4-4b73-98ed-fb0c3d55a624.jpg" alt=""&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/prototyp/improve-your-css-with-these-5-principles-35jd" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Improve your CSS with these 5 principles&lt;/h2&gt;
      &lt;h3&gt;Adrian Bece for PROTOTYP ・ Oct 7 '19&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#css&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#webdev&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;






&lt;p&gt;These articles are fueled by coffee. So if you enjoy my work and found it useful, consider buying me a coffee! I would really appreciate it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.buymeacoffee.com/ubnZ8GgDJ" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.buymeacoffee.com%2Fbuttons%2Fdefault-red.png" alt="Buy Me A Coffee"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thank you for taking the time to read this post. If you've found this useful, please give it a ❤️ or 🦄, share and comment.&lt;/p&gt;

</description>
      <category>css</category>
      <category>webdev</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Improve your CSS with these 5 principles</title>
      <dc:creator>Adrian Bece</dc:creator>
      <pubDate>Mon, 07 Oct 2019 05:39:20 +0000</pubDate>
      <link>https://forem.com/prototyp/improve-your-css-with-these-5-principles-35jd</link>
      <guid>https://forem.com/prototyp/improve-your-css-with-these-5-principles-35jd</guid>
      <description>&lt;p&gt;Writing CSS is really simple and straightforward, so why is there a need for principles and best-practices while writing CSS?&lt;/p&gt;

&lt;p&gt;As the project scope increases and as the number of people working on the project increases, the problems become more and more apparent and can cause serious issues down the line. Fixing issues may become harder, duplicated code, complex override chains and use of &lt;code&gt;!important&lt;/code&gt;, leftover / unused code (removed elements or features), code that is hard to read, etc.&lt;/p&gt;

&lt;p&gt;Writing CSS at a professional level will make the CSS code more maintainable, extensible, understandable and cleaner. We're going to look at the five simple and very effective principles that will take your CSS to the next level.&lt;/p&gt;

&lt;h2&gt;
  
  
  Naming principle
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;"There are only two hard things in Computer Science: cache invalidation and naming things." -- Phil Karlton&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Properly naming and structuring your CSS selectors is the first step to making your CSS more readable, structured and cleaner. Establishing rules and constraints in your naming convention makes your code standardized, robust and easier to understand.&lt;/p&gt;

&lt;p&gt;This is why concepts like &lt;a href="https://en.bem.info/methodology/" rel="noopener noreferrer"&gt;BEM (Block-Element-Modifier)&lt;/a&gt;, &lt;a href="http://smacss.com/" rel="noopener noreferrer"&gt;SMACSS (Scalable and Modular Architecture for CSS)&lt;/a&gt; and &lt;a href="https://www.smashingmagazine.com/2011/12/an-introduction-to-object-oriented-css-oocss/" rel="noopener noreferrer"&gt;OOCSS (Object Oriented CSS)&lt;/a&gt; are popular among many frontend developers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Low specificity principle
&lt;/h2&gt;

&lt;p&gt;Overriding CSS properties is very useful, but things can go out of hand pretty quickly on more complex projects. Overriding chains can get really long and complex, you might be forced to use &lt;code&gt;!important&lt;/code&gt; to solve the specificity issue and you could get really easily lost when debugging or adding new features.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* Low-specificity selector */&lt;/span&gt;
&lt;span class="nc"&gt;.card&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="c"&gt;/* High-specificity selectors */&lt;/span&gt;
&lt;span class="nc"&gt;.card&lt;/span&gt; &lt;span class="nc"&gt;.title&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="nc"&gt;.blog-list&lt;/span&gt; &lt;span class="nc"&gt;.card&lt;/span&gt; &lt;span class="nt"&gt;img&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="nc"&gt;.blog-list&lt;/span&gt; &lt;span class="nc"&gt;.card.featured&lt;/span&gt; &lt;span class="nc"&gt;.title&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="nf"&gt;#js-blog-list&lt;/span&gt; &lt;span class="nc"&gt;.blog-list&lt;/span&gt; &lt;span class="nc"&gt;.card&lt;/span&gt; &lt;span class="nt"&gt;img&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Browser and specificity
&lt;/h3&gt;

&lt;p&gt;One of the benefits of following the low specificity principle is performance. Browsers parse the CSS &lt;strong&gt;from right to left&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Let's take a look at the following example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.blog-list&lt;/span&gt; &lt;span class="nc"&gt;.card&lt;/span&gt; &lt;span class="nt"&gt;img&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Browsers parse the selector like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Find all &lt;code&gt;img&lt;/code&gt; elements on the page&lt;/li&gt;
&lt;li&gt;Keep selected elements that are the descendants of &lt;code&gt;.card&lt;/code&gt; class&lt;/li&gt;
&lt;li&gt;Keep selected elements that are the descendant of &lt;code&gt;.blog-list&lt;/code&gt; class&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You can see how high-specificity selectors impact performance, especially when we need to globally select generic elements like &lt;code&gt;div&lt;/code&gt;, &lt;code&gt;img&lt;/code&gt;, &lt;code&gt;li&lt;/code&gt;, etc.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using the same level of specificity
&lt;/h3&gt;

&lt;p&gt;By using low specificity CSS class selectors in combination with BEM or one of the other naming principles mentioned in the previous section, we can create a performant, flexible and understandable code.&lt;/p&gt;

&lt;p&gt;Why use CSS classes? We want to keep the same level of specificity, stay flexible and be able to target multiple elements. Element selectors and id selectors do not offer the flexibility that we need.&lt;/p&gt;

&lt;p&gt;Let's rewrite our previous example using BEM and keeping specificity low.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* Low-specificity selector */&lt;/span&gt;
&lt;span class="nc"&gt;.card&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="c"&gt;/* Fixed high-specificity selectors */&lt;/span&gt;
&lt;span class="nc"&gt;.card__title&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="nc"&gt;.blogList__image&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="nc"&gt;.blogList__title--featured&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="nc"&gt;.blogList__img--special&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 see how these selectors are simple, understandable and can be easily overridden and extended if needed. And by keeping them low-level (a single class), we are guaranteed optimal performance and flexibility.&lt;/p&gt;

&lt;h2&gt;
  
  
  DRY Principle
&lt;/h2&gt;

&lt;p&gt;DRY (Don't repeat yourself) principle can be also applied to CSS. Duplicated code in CSS can cause code bloat, unnecessary overrides, reduce maintainability, etc. This issue can be fixed by structuring the code appropriately and having high-quality documentation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://storybook.js.org/" rel="noopener noreferrer"&gt;Storybook&lt;/a&gt; is a great free tool that enables you to create an overview of available frontend components and write high-quality documentation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* Without DRY Princple */&lt;/span&gt;
&lt;span class="nc"&gt;.warningStatus&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.5rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;font-weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bold&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#eba834&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.errorStatus&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.5rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;font-weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bold&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#eb3d34&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.form-errorStatus&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.5rem&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;0&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;font-weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bold&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#eb3d34&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;Let's refactor the code so it follows the DRY principle.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* With DRY Principle */&lt;/span&gt;
&lt;span class="nc"&gt;.status&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.5rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;font-weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bold&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.status--warning&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#eba834&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.status--error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#eb3d34&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.form__status&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.5rem&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&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;h2&gt;
  
  
  Single responsibility principle
&lt;/h2&gt;

&lt;p&gt;By using the single responsibility principle in our CSS, we can ensure that our CSS classes are easily extended and overriden. Let's take a look at the following example.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.button&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1rem&lt;/span&gt; &lt;span class="m"&gt;2rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.2rem&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="m"&gt;#eb4934&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#fff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;font-weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bold&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.button--secondary&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;border-radius&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;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1rem&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="m"&gt;#888&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;We can see that if we want to extend &lt;code&gt;.button&lt;/code&gt; class with &lt;code&gt;.button--secondary&lt;/code&gt;, we are doing lots of overrides to achieve what we need, when we only want to apply a different background color and keep the default styles.&lt;/p&gt;

&lt;p&gt;The problem is that our &lt;code&gt;.button&lt;/code&gt; class is having several roles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sets layout (&lt;code&gt;padding&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Sets typography (&lt;code&gt;font-size&lt;/code&gt;,&lt;code&gt;font-weight&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Sets presentation (&lt;code&gt;color&lt;/code&gt;,&lt;code&gt;background-color&lt;/code&gt;, &lt;code&gt;border-radius&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;this makes our CSS classes very hard to extend and combine with other CSS classes. By keeping this in mind, let's use BEM and OOCSS to improve our CSS.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* Shared styles */&lt;/span&gt;
&lt;span class="nc"&gt;.button&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1rem&lt;/span&gt; &lt;span class="m"&gt;2rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;font-weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bold&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#fff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;/* Style extensions */&lt;/span&gt;
&lt;span class="nc"&gt;.button--radialBorder&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.2rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.button--large&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.button--primary&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="m"&gt;#eb4934&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.button--secondary&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="m"&gt;#888&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;We have broken down our &lt;code&gt;button&lt;/code&gt; styles into several classes that can be used to extend the base &lt;code&gt;button&lt;/code&gt; class. We can optionally apply the modifiers and add new ones as the design changes or new elements are being added.&lt;/p&gt;

&lt;h2&gt;
  
  
  Open/Close principle
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We've already used the open/close principle in the previous examples. All new features and options need to be added by extension. Let's take a look at this example.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;
&lt;span class="nc"&gt;.card&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.blog-list&lt;/span&gt; &lt;span class="nc"&gt;.card&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.5em&lt;/span&gt; &lt;span class="m"&gt;1rem&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;The &lt;code&gt;.blog-list .card&lt;/code&gt; selector has few potential issues:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Some styles can be applied only if the &lt;code&gt;.card&lt;/code&gt; element is a child of &lt;code&gt;.blog-list&lt;/code&gt; element.&lt;/li&gt;
&lt;li&gt;Styles are forcibly applied to the &lt;code&gt;.card&lt;/code&gt; element if placed inside the &lt;code&gt;.blog-list&lt;/code&gt; element, which can produce unexpected results and unecessary overrides.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's rewrite the previous example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.card&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.blogList__card&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.5em&lt;/span&gt; &lt;span class="m"&gt;1rem&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;We've fixed the issue by having a single class selector. With this selector, we can avoid unexpected effects and there are no conditional nested styles.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;We've seen how by applying these few simple principles we have significantly improved the way we write CSS:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Standardized naming and structure, and improved readability by using BEM, OCSS, etc.&lt;/li&gt;
&lt;li&gt;Improved performance and structure by using low-specificity selectors.&lt;/li&gt;
&lt;li&gt;Reduced code bloat and improved code quality with DRY principle&lt;/li&gt;
&lt;li&gt;Flexibility and maintainability by using open/close principle&lt;/li&gt;
&lt;li&gt;etc.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;These articles are fueled by coffee. So if you enjoy my work and found it useful, consider buying me a coffee! I would really appreciate it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.buymeacoffee.com/ubnZ8GgDJ" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.buymeacoffee.com%2Fbuttons%2Fdefault-red.png" alt="Buy Me A Coffee"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thank you for taking the time to read this post. If you've found this useful, please give it a ❤️ or 🦄, share and comment.&lt;/p&gt;

</description>
      <category>css</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Optimizing images for the web - an in-depth guide</title>
      <dc:creator>Adrian Bece</dc:creator>
      <pubDate>Tue, 01 Oct 2019 05:26:22 +0000</pubDate>
      <link>https://forem.com/prototyp/optimizing-images-for-the-web-an-in-depth-guide-4j7d</link>
      <guid>https://forem.com/prototyp/optimizing-images-for-the-web-an-in-depth-guide-4j7d</guid>
      <description>&lt;h2&gt;
  
  
  Table Of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Calculating JPG image file size&lt;/li&gt;
&lt;li&gt;Online image optimization&lt;/li&gt;
&lt;li&gt;Automated solutions&lt;/li&gt;
&lt;li&gt;Image loading optimization&lt;/li&gt;
&lt;li&gt;Using CDN&lt;/li&gt;
&lt;li&gt;WebP image format&lt;/li&gt;
&lt;li&gt;Optimization for higher pixel density screens&lt;/li&gt;
&lt;li&gt;Conclusion - Optimization priority&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Unoptimized (non-minified) images are one of the main causes of poor website performance, mainly on the initial (first) load. Depending on the resolution and image quality, you might end up with images that take up more than 70% of your total website size.&lt;/p&gt;

&lt;p&gt;It's very easy for unoptimized images to end up on a production site and slow down its initial load considerably. Inexperienced devs usually aren't aware of this potential problem. They also aren't aware of a wide range of tools and approaches for optimizing images.&lt;/p&gt;

&lt;p&gt;This article aims to cover most of the tools and approaches for optimizing images for the web.&lt;/p&gt;

&lt;h2&gt;
  
  
  Calculating JPG image file size
&lt;/h2&gt;

&lt;p&gt;The uncompressed image size can be easily calculated by multiplying image width &lt;code&gt;px&lt;/code&gt; value with image height &lt;code&gt;px&lt;/code&gt; value and multiply the result by &lt;strong&gt;3&lt;/strong&gt; bytes which is equivalent to 24 bits (RGB color system). We divide the result by &lt;code&gt;1,048,576 (1024 \* 1024)&lt;/code&gt; to convert the value from &lt;strong&gt;bytes&lt;/strong&gt; to &lt;strong&gt;megabytes&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;image_size = (image_width * image_height * 3) / 1048576
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For example, let's calculate file size for an uncompressed image that has &lt;code&gt;1366px x 768px&lt;/code&gt; dimensions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1366 * 768 * 3 / 1048576 = 3Mb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Considering that average website size today is between 2Mb and 3Mb, imagine having an image on your site that takes more than 80% the size of your site. 3Mb takes ages to load on slower mobile networks, so you might lose some traffic on your website if the user is waiting for your website to load and most time is spent on loading a single image. Scary thought, isn't it?&lt;/p&gt;

&lt;p&gt;So what we can do to avoid having optimized images on the web but preserve the acceptable quality and resolution?&lt;/p&gt;

&lt;h2&gt;
  
  
  Online image optimization
&lt;/h2&gt;

&lt;p&gt;If you are working on a simple static website that only has a handful of images that won't change often or won't change at all, you can just drag and drop your images in one of the numerous online tools. They do an amazing job at compressing images using various algorithms and are more than enough for simple projects.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F28fcxrwdc0xl9tz5av30.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F28fcxrwdc0xl9tz5av30.jpg" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Most notable websites, in my opinion, are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://compressor.io/" rel="noopener noreferrer"&gt;Compressor.io - JPG, PNG, SVG, GIF - 1 file at the time&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://squoosh.app/" rel="noopener noreferrer"&gt;Squoosh - JPG, PNG, SVG, GIF - 1 file at the time&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://imagecompressor.com/" rel="noopener noreferrer"&gt;Optimizilla - JPG and PNG - up to 20 images at the time&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tinypng.com/" rel="noopener noreferrer"&gt;TinyPNG - JPG and PNG - up to 20 images at the time&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.svgminify.com/" rel="noopener noreferrer"&gt;SVGMinify - SVG - 1 file at the time&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://jakearchibald.github.io/svgomg/" rel="noopener noreferrer"&gt;svgomg - SVG - 1 file at the time&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Automated solutions
&lt;/h2&gt;

&lt;p&gt;However, if you are working on more complex projects with multiple people and using a lot of images, optimizing each one as it is added to the project can become tedious. Also, there is a risk that some images may end up not optimized due to human error or some other factor.&lt;/p&gt;

&lt;p&gt;On complex projects, it's common to use an equally complex build system like &lt;strong&gt;Gulp&lt;/strong&gt;, &lt;strong&gt;Webpack&lt;/strong&gt;, &lt;strong&gt;Parcel&lt;/strong&gt;, etc. Image optimization plugins can be easily added to those build configs and fully automate the image optimization process. Images can be optimized as soon as they are added to the project.&lt;/p&gt;

&lt;p&gt;Most notable plugin, in my opinion, is &lt;a href="https://www.npmjs.com/package/imagemin" rel="noopener noreferrer"&gt;imagemin&lt;/a&gt; which can be easily integrated with any CLI or build tools:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/imagemin/imagemin-cli" rel="noopener noreferrer"&gt;imagemin - CLI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/sindresorhus/gulp-imagemin" rel="noopener noreferrer"&gt;imagemin - Gulp&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/itgalaxy/imagemin-webpack" rel="noopener noreferrer"&gt;imagemin - Webpack&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/DeMoorJasper/parcel-plugin-imagemin" rel="noopener noreferrer"&gt;imagemin - Parcel&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Image loading optimization
&lt;/h2&gt;

&lt;p&gt;We've looked at the image optimization strategies that reduce the file size by compressing the image without changing the image resolution and affecting image quality too much. Although optimizing image file reduces the file size of images considerably, having multiple optimized images (on the webshop catalog page for example) loaded all at once can have a poor effect on performance.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lazy Loading
&lt;/h3&gt;

&lt;p&gt;Lazy loading is a concept of only loading assets that are needed. In our case, only images that are currently within the user's viewport (screen) are loaded. Other images are not loaded until they appear within the user's viewport.&lt;/p&gt;

&lt;p&gt;Although native Lazy loading has just been recently introduced to browsers, there have been many JavaScript-based solutions available.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fa0d606o814ve4svfl391.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fa0d606o814ve4svfl391.gif" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Native Lazy Loading
&lt;/h4&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;"image.jpg"&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;alt=&lt;/span&gt;&lt;span class="s"&gt;"Sample image"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  JavaScript-based solutions
&lt;/h4&gt;

&lt;p&gt;Most notable JavaScript-based solutions, in my opinion, are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/verlok/lazyload" rel="noopener noreferrer"&gt;verlok/lazyload&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/malchata/yall.js/" rel="noopener noreferrer"&gt;yall.js&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/dinbror/blazy" rel="noopener noreferrer"&gt;Blazy (not actively maintained)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Progressive images
&lt;/h3&gt;

&lt;p&gt;Although lazy loading does a great job performance-wise, looking at the problem from UX perspective we can see that the user is waiting for the image to load and looking at the blank space. On slow connections, downloading images can take ages. This is where progressive images come into play.&lt;/p&gt;

&lt;p&gt;Basically, having a progressive image means that a low-quality image will be displayed to the user until a high-quality image has finished loading. A low-quality image has a considerably smaller file size due to the low quality and high compression rate, so this image will be loaded very fast. In between the low quality and high-quality image we can have as many images with varying quality as we need and we can load the higher quality image on each download.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fhz80q3fpry5qe0i3khi4.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fhz80q3fpry5qe0i3khi4.jpeg" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Similarly to the article on Skeleton loading I've written, this technique gives the user an illusion of speed. User is looking at an image that is loading and becoming more clearer as it loads higher and higher quality image, instead of looking at the empty space waiting for something to happen.&lt;/p&gt;

&lt;p&gt;This is an JavaScript implementation of progressive images: &lt;a href="https://www.npmjs.com/package/progressive-image" rel="noopener noreferrer"&gt;progressive-image&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Responsive images
&lt;/h3&gt;

&lt;p&gt;We also need to be careful of using properly-sized images.&lt;/p&gt;

&lt;p&gt;For example, let's say we have an image that is &lt;code&gt;1920px&lt;/code&gt; maximum width on desktop , &lt;code&gt;1024px&lt;/code&gt; maximum width on tablet devices and &lt;code&gt;568px&lt;/code&gt; maximum width on mobile devices. Simplest solution would be to just use the &lt;code&gt;1920px&lt;/code&gt; image and cover all the cases, right? In that case, an user on a smartphone with slow and unreliable connection would have to wait ages for the massive image to download and we'd be back at the square one of the problem.&lt;/p&gt;

&lt;p&gt;Luckily for us, we can use picture element to tell the browser which image to dowload, depending on the media query. Although this element is supported by more than 93% of globally used browsers, it has a pretty simple fallback with &lt;code&gt;img&lt;/code&gt; element already inside it.&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;picture&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;source&lt;/span&gt; &lt;span class="na"&gt;media=&lt;/span&gt;&lt;span class="s"&gt;"(min-width: 1025px)"&lt;/span&gt; &lt;span class="na"&gt;srcset=&lt;/span&gt;&lt;span class="s"&gt;"image_desktop.jpg"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;source&lt;/span&gt; &lt;span class="na"&gt;media=&lt;/span&gt;&lt;span class="s"&gt;"(min-width: 769px)"&lt;/span&gt; &lt;span class="na"&gt;srcset=&lt;/span&gt;&lt;span class="s"&gt;"image_tablet.jpg"&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;"image_mobile.jpg"&lt;/span&gt; &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"Sample image"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/picture&amp;gt;&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Using CDN
&lt;/h2&gt;

&lt;p&gt;CDN services like Cloudinary and Cloudflare can perform image optimization on the server and serve the optimized images to the user. If your website uses a CDN, it's worth looking into asset optimization options. This allows us not to worry about image quality optimization at all, and have all optimizations done server-side. We only need to look into optimizing image loading by either using lazy loading or progressive images.&lt;/p&gt;

&lt;h2&gt;
  
  
  WebP image format
&lt;/h2&gt;

&lt;p&gt;WebP image format is developed by Google and is an image format specifically optimized for the web. According to the canIUse data, current browser support for WebP image format is at around 80% which is great. Luckily, implementing a fallback to standard jpg image with &lt;code&gt;img&lt;/code&gt; element inside &lt;code&gt;picture&lt;/code&gt; element is easy.&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;picture&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;source&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"image/webp"&lt;/span&gt; &lt;span class="na"&gt;srcset=&lt;/span&gt;&lt;span class="s"&gt;"image.webp"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;source&lt;/span&gt; &lt;span class="na"&gt;srcset=&lt;/span&gt;&lt;span class="s"&gt;"image.jpg"&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;"image.jpg"&lt;/span&gt; &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"Sample image"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/picture&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Although there are numerous online file format converters that can convert images to WebP format, CDN services can easily perform format conversion server-side.&lt;/p&gt;

&lt;h2&gt;
  
  
  Optimization for high pixel density screens
&lt;/h2&gt;

&lt;p&gt;This is more UX improvement rather than performance, but it's also important to take into account devices that have higher pixel density.&lt;/p&gt;

&lt;p&gt;For example, let's assume that we are displaying an 768px x 320px banner image on 768px screen. But the screen has 2x density and the px width is actally: &lt;code&gt;2 x 768 = 1536px&lt;/code&gt;. Basically, we are stretching 768px over 1536px and this leads to blurry image on high pixel density devices.&lt;/p&gt;

&lt;p&gt;In order to fix that, we need to serve an image optimized for high pixel density screens. We need to create separate images that are 2 times or 3 times the resolution of regular screens and use the &lt;code&gt;srcset&lt;/code&gt; attribute with &lt;code&gt;2x&lt;/code&gt; tag marking for higher resolution image.&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;"image-1x.jpg"&lt;/span&gt; &lt;span class="na"&gt;srcset=&lt;/span&gt;&lt;span class="s"&gt;"image-2x.jpg 2x"&lt;/span&gt; &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"Sample image"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Example - Responsive WebP/PNG images with high-density screen support
&lt;/h2&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;picture&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;source&lt;/span&gt; &lt;span class="na"&gt;srcset=&lt;/span&gt;&lt;span class="s"&gt;"./images/webp/hero-image-420-min.webp 1x, ./images/webp/hero-image-760-min.webp 2x"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"image/webp"&lt;/span&gt; &lt;span class="na"&gt;media=&lt;/span&gt;&lt;span class="s"&gt;"(max-width: 440px)"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;source&lt;/span&gt; &lt;span class="na"&gt;srcset=&lt;/span&gt;&lt;span class="s"&gt;"./images/minified/hero-image-420-min.png 1x, ./images/minified/hero-image-760-min.png 2x"&lt;/span&gt; &lt;span class="na"&gt;media=&lt;/span&gt;&lt;span class="s"&gt;"(max-width: 440px)"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;source&lt;/span&gt; &lt;span class="na"&gt;srcset=&lt;/span&gt;&lt;span class="s"&gt;"./images/webp/hero-image-550-min.webp 1x, ./images/webp/hero-image-960-min.webp 2x"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"image/webp"&lt;/span&gt; &lt;span class="na"&gt;media=&lt;/span&gt;&lt;span class="s"&gt;"(max-width: 767px)"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;source&lt;/span&gt; &lt;span class="na"&gt;srcset=&lt;/span&gt;&lt;span class="s"&gt;"./images/minified/hero-image-550-min.png 1x, ./images/minified/hero-image-960-min.png 2x"&lt;/span&gt; &lt;span class="na"&gt;media=&lt;/span&gt;&lt;span class="s"&gt;"(max-width: 767px)"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;source&lt;/span&gt; &lt;span class="na"&gt;srcset=&lt;/span&gt;&lt;span class="s"&gt;"./images/webp/hero-image-420-min.webp 1x, ./images/webp/hero-image-760-min.webp 2x"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"image/webp"&lt;/span&gt; &lt;span class="na"&gt;media=&lt;/span&gt;&lt;span class="s"&gt;"(max-width: 1023px)"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;source&lt;/span&gt; &lt;span class="na"&gt;srcset=&lt;/span&gt;&lt;span class="s"&gt;"./images/minified/hero-image-420-min.png 1x, ./images/minified/hero-image-760-min.png 2x"&lt;/span&gt; &lt;span class="na"&gt;media=&lt;/span&gt;&lt;span class="s"&gt;"(max-width: 1023px)"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;source&lt;/span&gt; &lt;span class="na"&gt;srcset=&lt;/span&gt;&lt;span class="s"&gt;"./images/webp/hero-image-760-min.webp 1x, ./images/webp/hero-image-960-min.webp 2x"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"image/webp"&lt;/span&gt; &lt;span class="na"&gt;media=&lt;/span&gt;&lt;span class="s"&gt;"(max-width: 1919px)"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;source&lt;/span&gt; &lt;span class="na"&gt;srcset=&lt;/span&gt;&lt;span class="s"&gt;"./images/minified/hero-image-760-min.png 1x, ./images/minified/hero-image-960-min.png 2x"&lt;/span&gt; &lt;span class="na"&gt;media=&lt;/span&gt;&lt;span class="s"&gt;"(max-width: 1919px)"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;source&lt;/span&gt; &lt;span class="na"&gt;srcset=&lt;/span&gt;&lt;span class="s"&gt;"./images/webp/hero-image-960-min.webp"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"image/webp"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;source&lt;/span&gt; &lt;span class="na"&gt;srcset=&lt;/span&gt;&lt;span class="s"&gt;"./images/minified/hero-image-960-min.png"&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;"./images/minified/hero-image-960-min.png"&lt;/span&gt; &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"Example"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/picture&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion - Optimization priority
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Use optimized images (optimized by automated build tools, online services or CDN)&lt;/li&gt;
&lt;li&gt;Use lazy loading (JS solution until native becomes more supported)&lt;/li&gt;
&lt;li&gt;Optimize images for high pixel density screens&lt;/li&gt;
&lt;li&gt;Use WebP image format&lt;/li&gt;
&lt;li&gt;Use progressive images&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Optional:&lt;/strong&gt; Remember to serve images (and other static assets) over CDN if you are able to.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fcxca8h5h17rfcwpeza26.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fcxca8h5h17rfcwpeza26.jpg" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;These articles are fueled by coffee. So if you enjoy my work and found it useful, consider buying me a coffee! I would really appreciate it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.buymeacoffee.com/ubnZ8GgDJ" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.buymeacoffee.com%2Fbuttons%2Fdefault-red.png" alt="Buy Me A Coffee"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thank you for taking the time to read this post. If you've found this useful, please give it a ❤️ or 🦄, share and comment.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>beginners</category>
      <category>productivity</category>
      <category>html</category>
    </item>
    <item>
      <title>Draggable chat-heads in React Native</title>
      <dc:creator>Sebastijan Dumancic</dc:creator>
      <pubDate>Tue, 24 Sep 2019 08:20:39 +0000</pubDate>
      <link>https://forem.com/prototyp/draggable-chat-heads-in-react-native-4f1g</link>
      <guid>https://forem.com/prototyp/draggable-chat-heads-in-react-native-4f1g</guid>
      <description>&lt;p&gt;Most of us are familiar with Facebook’s floating heads that are screaming for your attention on top of all other apps. At the time, it was a novel concept, somewhat annoying, but still, something new.&lt;/p&gt;

&lt;p&gt;Recently, we’ve had a client that requested similar behavior, just in-app, which would show draggable profile photos which could be paired by overlapping one over another.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F7zfkuqgp507z0gfy6pau.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F7zfkuqgp507z0gfy6pau.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since you’re probably skimming this part to see if a solution you’re looking forward is here, let get straight to the point.&lt;/p&gt;

&lt;p&gt;We’ve used &lt;strong&gt;panResponder&lt;/strong&gt; and wrapped each person in one.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Props&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&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="nx"&gt;pan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addListener&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;position&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;value&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="nx"&gt;panResponder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;PanResponder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="na"&gt;onMoveShouldSetPanResponder&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;evt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;gestureState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

            &lt;span class="na"&gt;onPanResponderGrant&lt;/span&gt;&lt;span class="p"&gt;:&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;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setOffset&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                    &lt;span class="na"&gt;x&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="nx"&gt;position&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="na"&gt;y&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="nx"&gt;position&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&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="nx"&gt;pan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setValue&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&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;span class="c1"&gt;// Move object while onPress is active. Snapping is handled later.&lt;/span&gt;
            &lt;span class="na"&gt;onPanResponderMove&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Animated&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;event&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
                &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;dx&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="nx"&gt;pan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;dy&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="nx"&gt;pan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;]),&lt;/span&gt;

            &lt;span class="c1"&gt;// Handle swiper behaviour after object is released.&lt;/span&gt;
            &lt;span class="na"&gt;onPanResponderRelease&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;dx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;dy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;vx&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="c1"&gt;// Fix jumping when moving the object second time.&lt;/span&gt;
                &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;flattenOffset&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

                &lt;span class="c1"&gt;// Send ending position to parent component.&lt;/span&gt;
                &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;calculateOverlapping&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;dy&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="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

                &lt;span class="c1"&gt;// Animate springy tuff.&lt;/span&gt;
                &lt;span class="nx"&gt;Animated&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;spring&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="nx"&gt;pan&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;toValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&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;span class="nf"&gt;start&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  &lt;strong&gt;Register initial people position&lt;/strong&gt;
&lt;/h1&gt;

&lt;p&gt;Each person is wrapped in an Animated.View component which means it’s draggable. Animated.View, just as normal View, has an onLayout event which is invoked on mount and layout changes.&lt;/p&gt;

&lt;p&gt;Once that event is triggered, we can register this person initial position. They are positioned absolutely, but when reporting position it will use XY coordinates based on the parent they are on (0,0 will be top left corner of the parent element).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newPersonPosition&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PersonPosition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trunc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nativeEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;layout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; 
&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trunc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nativeEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;layout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The position is truncated since we don’t need extreme precision that horizontal and vertical displacements report (&lt;strong&gt;dx&lt;/strong&gt; and &lt;strong&gt;dy&lt;/strong&gt; in &lt;strong&gt;onPanResponderRelease&lt;/strong&gt;).&lt;/p&gt;

&lt;p&gt;PersonPosition here is just a constructor that creates an object with its horizontal and vertical position, together with userId which we can use later on to trigger events on that specific user.&lt;/p&gt;

&lt;p&gt;Also, I’ve added 30, a magic number, which is half of the width and height of a component. Reported location (&lt;strong&gt;event.nativeEvent.layout.x&lt;/strong&gt;) is a position in the top left corner of the component. If you want to be scientific about this, the proper way would be to check for a component's width and height and add half of it, but I know mine is 60, so I just added half manually. Now we save this since it’s a center of a component, and we need that for overlap calculation.&lt;/p&gt;

&lt;p&gt;Position for each person is then pushed into an array which is saved to state:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;peoplePosition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newPersonPosition&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="nf"&gt;setState&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;   &lt;span class="nx"&gt;peoplePosition&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is to have an easier way of comparing future dropped components to all of the others (using array’s find method).&lt;/p&gt;

&lt;h1&gt;
  
  
  &lt;strong&gt;Checking for overlapping&lt;/strong&gt;
&lt;/h1&gt;

&lt;p&gt;Main part is to check for overlapping after the user releases the person. We can get the drop coordinates like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;droppedX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trunc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;draggedPerson&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;startingPointX&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;dx&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;droppedY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trunc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;draggedPerson&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;startingPointY&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;dy&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where we take dragged person’s horizontal starting point and add the horizontal displacement and repeat for the vertical axis. The result is once again truncated to remove unneeded decimals.&lt;/p&gt;

&lt;p&gt;Then, that &lt;strong&gt;ending&lt;/strong&gt; position of the person is checked against the positions of all people that were not dragged:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;matchedPerson&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;notDraggedPeople&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;personPosition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PersonPosition&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; 
&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;personPosition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;startingPointX&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;droppedX&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;personPosition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;startingPointY&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;droppedY&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the dropped person is anywhere inside a set distance from any of the people, we have a match! Here the radius is hardcoded to 30px, but you can set it to whatever you want.&lt;/p&gt;

&lt;p&gt;Maybe the best is half the width of an element + some buffer to make it easier to overlap successfully. You definitely want to avoid making it larger than the total width of the elements you’re overlapping to avoid false positives.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F9tosuj4x8j5kl9bzmjki.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F9tosuj4x8j5kl9bzmjki.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The distance of 0 means that the two components are perfectly overlapped (their centers match). Distance of 30 (in our case) means that they are touched by the edges. Tweak this number to determine how precise you have to be in order to get a sucesfull match.&lt;/p&gt;

&lt;p&gt;If a match is successful, just push the person to the &lt;strong&gt;matchedPeople&lt;/strong&gt; array and save it to the state:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;matchedPeople&lt;/span&gt; &lt;span class="o"&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="nx"&gt;matchedPerson&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;matchedPeople&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;matchedPerson&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="nf"&gt;setState&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="nx"&gt;matchedPeople&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;h1&gt;
  
  
  &lt;strong&gt;Trigger action after ovelapping&lt;/strong&gt;
&lt;/h1&gt;

&lt;p&gt;Finally, you probably want to do something after the user overlaps two heads successfully.&lt;/p&gt;

&lt;p&gt;In our case, we just listened to state change for matchedPeople in ComponentWillUpdate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="nf"&gt;componentWillUpdate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;nextProps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Props&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;nextState&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;State&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="nx"&gt;nextState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;matchedPeople&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="c1"&gt;// Navigate away from the screen&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 should check for changes here to avoid excessive triggering for each component updates, but since we navigated away from this screen once a successful overlaps occur (matchedPeople array is populated), it’s a simple logic to check for.&lt;/p&gt;

&lt;p&gt;Provided you’re experienced with panResponder, this code should be easy to replicate. In case you need a refresher on panResponder, I’ve written another article which tackles rotateable circle to select items here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/prototyped/circular-swiper-using-pan-responder-and-animated-library-b78eee9784a4" rel="noopener noreferrer"&gt;https://medium.com/prototyped/circular-swiper-using-pan-responder-and-animated-library-b78eee9784a4&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Did I mess up somewhere? Have better ideas? Drop us an email at &lt;a href="mailto:hello@prototyp.digital"&gt;hello@prototyp.digital&lt;/a&gt; or visit us at &lt;a href="https://prototyp.digital" rel="noopener noreferrer"&gt;https://prototyp.digital&lt;/a&gt;. Cheers!&lt;/p&gt;

</description>
      <category>reactnative</category>
      <category>tutorial</category>
      <category>javascript</category>
      <category>react</category>
    </item>
    <item>
      <title>Avoiding the messy git history</title>
      <dc:creator>Vlatko Vlahek</dc:creator>
      <pubDate>Tue, 17 Sep 2019 08:56:25 +0000</pubDate>
      <link>https://forem.com/prototyp/avoiding-the-messy-git-history-470d</link>
      <guid>https://forem.com/prototyp/avoiding-the-messy-git-history-470d</guid>
      <description>&lt;h1&gt;
  
  
  Avoiding the messy git history
&lt;/h1&gt;

&lt;p&gt;If we try to name the things that have clearly defined modern software development, source control would most certainly be very high on the list, especially git which is probably the most widely used version control system today.&lt;/p&gt;

&lt;p&gt;Days of having our code versioned in different folders locally, often prone to corruption are long gone. However, a lot of developers use git just as a means to store the source files somewhere remotely, without actually utilising some of its more advanced features that allow us to have a great, easily readable git history.&lt;/p&gt;

&lt;p&gt;This article will cover one of the git-flow approaches, heavily based on git rebase, that will allow you to have a more streamlined git experience, especially when working inside a team. It’s a strict approach and takes a while to get used to.&lt;/p&gt;

&lt;p&gt;The experience stems from working on bigger projects, based on the in-house practices that we have at &lt;a href="https://prototyp.digital/"&gt;PROTOTYP&lt;/a&gt;. We put a strong emphasis on code review and easy readability of all changes that happen inside our codebase.&lt;/p&gt;

&lt;p&gt;Main goals of the approach are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;cleaner git history&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;fewer merge conflicts&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;enforcing code review&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;increasing branch stability&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All examples in the article are done via command line, but there will be links and references on how to achieve some parts of the process in the git provider dashboards.&lt;/p&gt;

&lt;h2&gt;
  
  
  Initialising git flow
&lt;/h2&gt;

&lt;p&gt;The first step of the journey is definitively initialising Git flow over your repository.&lt;/p&gt;

&lt;p&gt;More thorough documentation on the matter by its original author can be found &lt;a href="https://nvie.com/posts/a-successful-git-branching-model/"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In short, it’s a branching model that scaled really well for us in the past and is widely adopted.&lt;/p&gt;

&lt;p&gt;In order to use this from the command line, you will most probably need to install git-flow. GUI solutions such as Tower or Sourcetree usually have it integrated.&lt;/p&gt;

&lt;p&gt;You can check the installation instructions &lt;a href="https://danielkummer.github.io/git-flow-cheatsheet/"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Example:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// initialises git on your repository
git init

// initialises git flow on your git repo
git flow init
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;After initialising git flow on your repository, you will be asked for default branch names. We use the default values internally, only prefixing tags with letter &lt;strong&gt;“v”&lt;/strong&gt;, so our versions are v1.0.0, v1.0.1 etc.&lt;/p&gt;

&lt;p&gt;Feel free to use the versioning system for releases that has the most sense for your team and your product. However, semantic versioning or &lt;a href="https://semver.org/"&gt;semver&lt;/a&gt; has been our weapon of choice for some time now, and while it could be an overkill for smaller one-off projects, it has proven great for releasing new features for SaaS products or mobile apps inside our company.&lt;/p&gt;

&lt;p&gt;Each of the branches git flow introduces has its own place in the ecosystem, and understanding when to use each one is a must!&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// New features
Feature branches? [feature/]

// Tags a version and merges develop to master. 
// A short lived branch. Versions bumps are ok inside it.
Release branches? [release/]

// A branch done from master directly, for fast hotfix push
// We use bugfix/ name for a bugfix branch that is branched from  
// develop
Hotfix branches? [hotfix/]

// Need to add some client specific code ? Use a support branch
Support branches? [support/]

// Tag for release branches
Version tag prefix? [v]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Remember, git is not some sort of a magic wand that makes all of your issues go away just by itself, nor does it have any sense in having multiple branches if you don’t know what to do with them in the first place.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Locking down develop and master branches
&lt;/h2&gt;

&lt;p&gt;This one is maybe a tad controversial for people not used to this approach, but I would definitively note this as one of the most important steps in the process.&lt;/p&gt;

&lt;p&gt;Protecting &lt;em&gt;**develop&lt;/em&gt;* and &lt;strong&gt;&lt;em&gt;master&lt;/em&gt;&lt;/strong&gt; branches will require your team to merge code to them exclusively via merge requests, and through a code review process, which are both strongly encouraged practices.&lt;/p&gt;

&lt;p&gt;It will also save you from a lot of potential “instability” troubles that tend to happen when people push a &lt;strong&gt;&lt;em&gt;“small and insignificant fix that can’t break anything”&lt;/em&gt;&lt;/strong&gt; directly to one of these branches. This creates a lot of frustration when things go wrong, which happens, sooner or later.&lt;/p&gt;

&lt;p&gt;How to do it, differs from provider to provider, but here is the outline for more popular ones:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bitbucket:&lt;/strong&gt; &lt;a href="https://blog.bitbucket.org/2016/12/05/protect-your-master-branch-with-merge-checks/"&gt;https://blog.bitbucket.org/2016/12/05/protect-your-master-branch-with-merge-checks/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitHub&lt;/strong&gt;: &lt;a href="https://help.github.com/articles/configuring-protected-branches/"&gt;https://help.github.com/articles/configuring-protected-branches/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitLab:&lt;/strong&gt; &lt;a href="https://docs.gitlab.com/ee/user/project/protected_branches.html"&gt;https://docs.gitlab.com/ee/user/project/protected_branches.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I would suggest enabling some merge checks along, such as at least one code review and approval from another developer before merging any code to them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding new code through feature branches
&lt;/h2&gt;

&lt;p&gt;Considering that no code can be directly pushed to develop or master branches, it’s required that you create a new feature branch to add a new functionality to your app.&lt;/p&gt;

&lt;p&gt;You can either do it from the command line or use integrated functionality in tools such as &lt;a href="https://www.sourcetreeapp.com/"&gt;Sourcetree&lt;/a&gt; or &lt;a href="https://www.git-tower.com/mac"&gt;Tower&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Source branch:&lt;/strong&gt; develop&lt;br&gt;
&lt;strong&gt;Naming:&lt;/strong&gt; feature/feature-name&lt;/p&gt;

&lt;p&gt;Example:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git checkout develop
git branch feature/my-new-feature
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;After you have successfully created your feature branch, feel free to push the code to it, until you are ready to get your feature reviewed as merged by another member of your team.&lt;/p&gt;

&lt;h2&gt;
  
  
  Merging your code
&lt;/h2&gt;

&lt;p&gt;In order to merge the code via this approach, after you have finished your feature, you will first need to rebase it. This is a multi-step process.&lt;/p&gt;

&lt;h3&gt;
  
  
  Rebase by developer
&lt;/h3&gt;

&lt;p&gt;As a developer who created a feature, you generally want to pull the latest changes from the develop branch that happened in the meantime and test whether your feature is still working.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git checkout develop
git pull
git checkout feature/your-feature-name

// This line will return a hash of commit where your branch diverged // from develop
git merge-base develop feature/your-feature-name

// {hash} is the result from the previous step
git rebase --onto develop {hash}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This will start the rebase process where changes from develop are integrated to your own feature branch, &lt;strong&gt;commit by commit&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Once you understand the steps that happen with rebase, there are also shorter alternatives such as:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git checkout feature/your-feature-name
git pull --rebase origin develop
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You can check the complete rebase documentation &lt;a href="https://git-scm.com/docs/git-rebase"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why not simply use merge?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A valid point. You can, of course, merge the develop to your own feature branch and get similar results. However, there are a few key differences, why we prefer to rebase the branch over merge in this scenario.&lt;/p&gt;

&lt;p&gt;Rebase:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Integrates changes commit by commit.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Conflicts also come in commit by commit, and it’s easy to ask your colleague what was the change to resolve the conflict more efficiently together.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Rebase can easily be aborted and started over, reverting all changes that you made if you are unsure if you messed up somewhere.&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// After you stage files, you can continue to next commit
git rebase --continue

// Skips the current commit entirely
git rebase --skip

// Reverts all rebase changes that you did
// Returns the branch to pre-rebase state
git rebase --abort
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You have more flexibility when rebasing as you can rename incoming commits or completely skip them, if you think they are unnecessary or superseded by your changes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It doesn’t create unnecessary merge commits if you integrate changes to your feature branches often.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If you don’t have any conflicts, it will just pass through all commits and the process will be a breeze as it is with git merge.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Merge chaos:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--AnDSxyzH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://cdn-images-1.medium.com/max/2000/1%2AJzp7pSDzzJI5sTvGzucoQQ.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AnDSxyzH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://cdn-images-1.medium.com/max/2000/1%2AJzp7pSDzzJI5sTvGzucoQQ.gif" alt="Merge chaos"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Merge glues all changes together leading to a huge amount of potentially conflicting files, and resolving the differences is often quite a chore. Also, it makes it much harder to understand who and for what reason changed a file if a conflict occurs.&lt;/p&gt;

&lt;p&gt;However, regardless of your choice, the burden of testing the feature before creating a Pull Request is definitely on the developer, and shouldn’t be skipped.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating a pull request (merge request in some providers)
&lt;/h3&gt;

&lt;p&gt;Create a pull request from your feature branch to develop (or branch from which you branched off) inside the interface of your git provider.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bitbucket:&lt;/strong&gt; &lt;a href="https://confluence.atlassian.com/bitbucket/create-a-pull-request-to-merge-your-change-774243413.html"&gt;https://confluence.atlassian.com/bitbucket/create-a-pull-request-to-merge-your-change-774243413.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitHub&lt;/strong&gt;: &lt;a href="https://help.github.com/en/articles/creating-a-pull-request"&gt;https://help.github.com/en/articles/creating-a-pull-request&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitLab:&lt;/strong&gt; &lt;a href="https://docs.gitlab.com/ee/gitlab-basics/add-merge-request.html"&gt;https://docs.gitlab.com/ee/gitlab-basics/add-merge-request.html&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Gitlab offers automatic rebase option inside their interface, a feature that I really like, and would be hyped to see it in BitBucket, for example.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Rebase by reviewer
&lt;/h3&gt;

&lt;p&gt;This step is the main trick to have a readable and streamlined git history.&lt;/p&gt;

&lt;p&gt;Unlike the developer rebase, which is used for resolving potential conflicts and testing your feature with the latest changes, this one is primarily for readability reasons, although it can also catch issues if they do happen.&lt;/p&gt;

&lt;p&gt;We think it’s a critical step in the process because after you create a PR, you don’t know how long it will take for somebody to review and merge it.&lt;/p&gt;

&lt;p&gt;More often than not, before this happens, there are new features merged to your development branch, which means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;new features might have introduced conflicts which need to be resolved&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;git history has changed from the moment you first rebased&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the reviewer does another rebase, it should improve the testing of the feature, minimise the potential of the feature not working after being merged, and provide you with a very clean and linear git history.&lt;/p&gt;

&lt;h2&gt;
  
  
  How does it look in practice
&lt;/h2&gt;

&lt;p&gt;Here is a little screenshot from Sourcetree, showing features merged via this method, and mutual relationships between &lt;em&gt;develop&lt;/em&gt;, &lt;em&gt;master&lt;/em&gt; and &lt;em&gt;other branches&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--aeZNPoik--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/3100/1%2AbrfzRnafwUrh3sEpSJNDjA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--aeZNPoik--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/3100/1%2AbrfzRnafwUrh3sEpSJNDjA.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;it’s very readable&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;release point with the tag from develop to master can be found quickly&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;you can easily see in which order your features or bug fixes were merged.&lt;br&gt;
This can help a lot if you are unsure which feature broke something else in your project. It even makes using &lt;a href="https://git-scm.com/docs/git-bisect"&gt;*git bisect&lt;/a&gt;* much easier if things come to that.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Squash or no squash&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The example shown here uses &lt;em&gt;squash&lt;/em&gt; which is given as a feature by GitLab automatically, although you can squash commits locally if required, like this:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git checkout feature/my-new-feature // If you are not on it
git rebase -i HEAD~1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This would squash your whole branch to one commit. A word of advice, &lt;strong&gt;discuss internally if this is what you want.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;From our perspective, as long as you follow the rebase flow, squash will not significantly impact the readability of your git graph, unless your features have 100+ commits.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In that scenario, maybe you bundled too much inside a single feature :).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I prefer squashing commits in a branch before doing a PR because it forces you to go for a new &lt;em&gt;bugfix/&lt;/em&gt; or &lt;em&gt;hotfix/&lt;/em&gt; branch if you messed something up. Also, regardless of whether your developers do atomic commits or huge beasts of commits, it will not matter.&lt;/p&gt;

&lt;p&gt;However, it’s not mandatory to squash all to one single commit. If it’s a bigger feature, you can manually squash your branch to a few important commits via interactive rebase.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git checkout feature/your-feature-name
git merge-base develop feature/your-feature-name // returns {hash}
git rebase -i {hash}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You will get a screen like this:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pick be8606b Added localisation package and settings
pick 48e6aec Fixed an issue with payload not being propagated
squash 6085ce3 Added connected intl
squash 60ec657 Fixed connected intl
squash ba09d22 Modified tasks from package.json
pick 0bea497 Build android
pick 52c67b9 Updated packages
pick 21aa18c rm package lock
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Simply change &lt;em&gt;pick&lt;/em&gt; to &lt;em&gt;squash&lt;/em&gt; to merge a commit with the commit that preceded it.&lt;/p&gt;

&lt;p&gt;This is usually used to squash commit duplicates or commits with stupid names such as &lt;em&gt;removed console.log&lt;/em&gt; in order to maintain a git history that is relevant and informative.&lt;/p&gt;

&lt;h2&gt;
  
  
  Not convinced that this is a way to go?
&lt;/h2&gt;

&lt;p&gt;If you haven’t used git in this fashion it’s very understandable. At least try it out, and try to make an opinion of it after you use it for a few days or a single project.&lt;/p&gt;

&lt;p&gt;It did allow us to improve our git usage at &lt;a href="http://prototyp.digital"&gt;PROTOTYP&lt;/a&gt;, and to achieve better git readability with far fewer conflicts. And when they do happen, they are resolved quickly, allowing us to be more efficient and focus on features, and not pulling our hair out.&lt;/p&gt;

&lt;p&gt;The process of switching to the flow on all projects was not easy and took some time. After a while though, everybody is satisfied with the results that it has brought to our team.&lt;/p&gt;

&lt;p&gt;Here are a few final things about the process.&lt;/p&gt;

&lt;h3&gt;
  
  
  A few words about Git rebase
&lt;/h3&gt;

&lt;p&gt;Oh boy. Not rebase, right?&lt;/p&gt;

&lt;p&gt;For us, &lt;strong&gt;git rebase&lt;/strong&gt; is one of the most powerful features that comes with git. Think of it as a swiss-tool for managing your git history.&lt;/p&gt;

&lt;p&gt;However, opinions for this functionality are often highly opinionated and this can lead to a few misconceptions.&lt;/p&gt;

&lt;p&gt;Here are a few:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Don’t use rebase, it’s a destructive operation!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This has some merit. Rebase rewrites git history, and can lead to disastrous results, &lt;strong&gt;if not used properly!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;However, a lot of things with git are potentially destructive. This doesn’t mean they shouldn’t be used, just that you need to understand &lt;strong&gt;how&lt;/strong&gt; and &lt;strong&gt;when&lt;/strong&gt; to use them, and how to mitigate the issues if they do happen.&lt;/p&gt;

&lt;p&gt;Be mindful of using this approach on public repositories and open-source projects where a lot of different people are pushing code, and not everybody is using the same principle. It requires the whole team to be disciplined.&lt;/p&gt;

&lt;p&gt;Also, if several people are working on the same feature, only rebase &lt;strong&gt;before the pull request&lt;/strong&gt; or &lt;strong&gt;if you are completely sure everybody commited their changes.&lt;/strong&gt; Rebase does rewrite the history of your own branch, so it could lead to a lot of force pulling and pushing on the branch then, which can create a frustration of its own.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This flow only makes the git graph nicer, it doesn’t bring in any value.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I strongly disagree with this one, for multiple reasons.&lt;/p&gt;

&lt;p&gt;First off, you can write nice code and ugly spaghetti code, but both can be functional. However, do ask yourself which is faster to understand and refactor and less prone to issues?&lt;/p&gt;

&lt;p&gt;The same logic applies here. Added brevity is rarely a bad thing.&lt;/p&gt;

&lt;p&gt;Also, the whole point of enforcing rebase is to minimise issues with conflicts, as they happen to be much much easier to resolve via this approach. The nice graph is only a great side effect of the whole process.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What if using rebase messes my branch and project up?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To be honest, it’s very very hard to completely mess a project or a branch up, especially if multiple people are working on it, and have their own local versions which are ok. They can always force push the change to reset your state.&lt;/p&gt;

&lt;p&gt;You can also mess a branch up if you use merge and commit these changes, but you haven’t resolved issues properly.&lt;/p&gt;

&lt;p&gt;However, if you do manage to do to the unthinkable, resolving these issues is often a very easy thing to do.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Git reflog&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Behold, the magic eraser pen of all the bad things that we do to our repository.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git reflog
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Hitting that command will show hashes of all states in which your branch was, up to 90 days. You can simply check out or reset the branch to a state before the merge or rebase, and start over, by finding the last hash before you did any destructive operation.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Something is strange here. Sometimes I need to rebase, sometimes not. What’s the catch?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If your branch is &lt;strong&gt;ahead&lt;/strong&gt; of the tip of develop, you don’t need to do it. In the following scenario, ensure that you have pulled all changes from develop, and if you are still ahead of it, just do a Pull request.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--QuBGy_eW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2620/1%2AQb5X8sEsfN3wfnMCVWRXjg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QuBGy_eW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2620/1%2AQb5X8sEsfN3wfnMCVWRXjg.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If your branch is &lt;strong&gt;behind&lt;/strong&gt; the tip of develop, you will need to rebase. Observe how the &lt;em&gt;feature/other-feature&lt;/em&gt; is not connected to the foremost point of develop, but a point behind its tip.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--jWpwYhxA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2000/1%2AgGFdRS_FtAE1QONsEB4OXw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--jWpwYhxA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2000/1%2AgGFdRS_FtAE1QONsEB4OXw.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing words
&lt;/h2&gt;

&lt;p&gt;We hope that this article outlined both the process and its pros and cons, based on our experiences.&lt;/p&gt;

&lt;p&gt;If you are looking for a new process that could bring in improvements to readability and stability of your git process, making you and your team more efficient in the process, give it a shot. It could go a long way!&lt;/p&gt;

&lt;p&gt;If you have a similar or different workflow which works for your team, we would like to hear about it and see how it compares.&lt;/p&gt;

</description>
      <category>git</category>
    </item>
    <item>
      <title>Implementing Skeleton Loading in React</title>
      <dc:creator>Adrian Bece</dc:creator>
      <pubDate>Wed, 04 Sep 2019 20:26:40 +0000</pubDate>
      <link>https://forem.com/prototyp/implementing-skeleton-loading-in-react-kia</link>
      <guid>https://forem.com/prototyp/implementing-skeleton-loading-in-react-kia</guid>
      <description>&lt;p&gt;The idea and concept of &lt;strong&gt;Skeleton Loading&lt;/strong&gt; or &lt;strong&gt;Skeleton Screens&lt;/strong&gt; was introduced in 2013. in this &lt;a href="https://www.lukew.com/ff/entry.asp?1797" rel="noopener noreferrer"&gt;blog post by Luke Wroblewski&lt;/a&gt;. It describes the concept of a blank screen where dynamic content is replaced by styled blocks (skeleton) and is replaced with real content once it's finished loading.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fpfjcpg2xsfqwfpx5agy6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fpfjcpg2xsfqwfpx5agy6.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Skeleton loading is better than a spinner
&lt;/h2&gt;

&lt;p&gt;When a user looks at the page with a standard loading spinner, they're thinking &lt;strong&gt;"I'm waiting for it to load"&lt;/strong&gt;. We're making the user watch a &lt;strong&gt;dull repeating animation&lt;/strong&gt; and the user feels like it's staring at the clock.&lt;/p&gt;

&lt;p&gt;When a user looks at the page with skeleton loading or skeleton screen, they're thinking &lt;strong&gt;"It's almost loaded, something is happening"&lt;/strong&gt;. The user is watching a page that seems &lt;strong&gt;like it's right about to finish loading&lt;/strong&gt;. User is watching at a page that is not empty or dull, even though we haven't loaded any content yet. It gives an illusion of speed.&lt;/p&gt;

&lt;p&gt;But there is something to keep in mind about skeleton loading...&lt;/p&gt;

&lt;h2&gt;
  
  
  Skeleton loading is used when the final loaded state is predictable
&lt;/h2&gt;

&lt;p&gt;We cannot simply replace every dynamically loaded element on the page with skeleton loading. In some cases, we cannot predict how the &lt;strong&gt;final loaded state&lt;/strong&gt; of the page is going to look like.&lt;/p&gt;

&lt;p&gt;For example, if we try and assume a final state of the page (let's say we chose 1 out of 4 wildly different possible states) and create a skeleton screen based on our assumption. When a user looks at the skeleton and the loaded content wildly differs from the skeleton, the transition looks very out of place and jarring. This may hurt the UX and make your app feel chaotic.&lt;/p&gt;

&lt;p&gt;Let's assume we're looking at an eCommerce site and we are looking at the homepage. &lt;strong&gt;eCommerce homepages&lt;/strong&gt; often change their look and layout, depending on the events or sales that are going on. &lt;strong&gt;It wouldn't be a good idea&lt;/strong&gt; to implement the skeleton loading here. &lt;/p&gt;

&lt;p&gt;Looking at the &lt;strong&gt;catalog page&lt;/strong&gt;, where products are displayed in 4-column layout, with 24 items per page. We can safely assume that this &lt;strong&gt;state is not going to change&lt;/strong&gt;. Even if we finally load 12 or 15 products, instead of assumed 24 the transition remains smooth. Catalog page loading is also very load-intensive (if pagination, sorting, and filters are used) so &lt;strong&gt;skeleton loading may also help in keeping the users on the page&lt;/strong&gt; even in cases when loading times are larger than expected.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementing Skeleton loading in React
&lt;/h2&gt;

&lt;p&gt;In the following example, we are implementing a skeleton loading for the food recipe card component. This is how the component looks like.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fv5jnm0frgws3sib92psc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fv5jnm0frgws3sib92psc.png" alt="Alt Text"&gt;&lt;/a&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="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Link&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-router-dom&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;LazyImage&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;components&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getUri&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;util/getUri&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;RecipeCard&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nx"&gt;calories&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;ingredients&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;uri&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;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;li&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;"recipeCard"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Link&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;"recipeCard__link"&lt;/span&gt; &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;`/recipes/recipe/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nf"&gt;getUri&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&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;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;LazyImage&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;"recipeCard__image"&lt;/span&gt; &lt;span class="na"&gt;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;image&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;label&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;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&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;"recipeCard__wrapper"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h3&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;"recipeCard__title"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h3&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&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;"paragraph"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;strong&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                By &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&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;"gradient--text"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;strong&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&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;"recipeCard__info"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;strong&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;"gradient--text"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ceil&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;calories&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;strong&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            calories |&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;strong&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;"gradient--text"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;ingredients&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;strong&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            ingredients
          &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Link&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;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;This is a simple skeleton that doesn't use any animations, just a plain color which also looks fine. You can easily add the animation by animating the &lt;strong&gt;background gradient&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;First, we need to set up the &lt;strong&gt;styles&lt;/strong&gt; for our skeleton components. We are setting up our base skeleton styles in &lt;code&gt;.recipeCard__skeleton&lt;/code&gt; class.  We are setting &lt;code&gt;inline-block&lt;/code&gt; to mimic content behavior (like alignment), we use &lt;code&gt;padding&lt;/code&gt; to add height (equal to &lt;strong&gt;1 unit of line-height&lt;/strong&gt;, in this case) to an element. We also have two modifier classes that change the width of the content and additional class for an image that has aspect ratio 1:1 (this is why &lt;code&gt;padding&lt;/code&gt; is set to &lt;code&gt;100%&lt;/code&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.recipeCard__skeleton&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;inline-block&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__gray--lighter&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nl"&gt;padding-bottom&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;--spacing__vertical--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;.recipeCard__skeleton--medium&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="m"&gt;33%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.recipeCard__skeleton--large&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="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.recipeCard__image--skeleton&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;padding-bottom&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;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__gray--lighter&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;Let's create our Skeleton component:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Copy the content of the "real" component and paste it into the Skeleton component. Change the const name and export.&lt;/li&gt;
&lt;li&gt;Replace all dynamic content (coming from props) to skeleton elements. Spans work fine due to them not having any default styles. Let the layout styles and grids handle everything else and keep the skeletons in place.&lt;/li&gt;
&lt;li&gt;Conditionally load the skeleton in the main component
&lt;/li&gt;
&lt;/ol&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="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Link&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-router-dom&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Skeleton&lt;/span&gt; &lt;span class="o"&gt;=&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;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;li&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;"recipeCard"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&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;"recipeCard__link"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&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;"recipeCard__image--skeleton"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&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;"recipeCard__wrapper"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h3&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;"recipeCard__title"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&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;"recipeCard__skeleton recipeCard__skeleton--large"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h3&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&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;"paragraph"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&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;"recipeCard__skeleton recipeCard__skeleton--large"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&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;"recipeCard__info"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&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;"recipeCard__skeleton recipeCard__skeleton--medium"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&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;"recipeCard__skeleton recipeCard__skeleton--medium"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;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;And this is how the final component will look like.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fqnn3eavyjtus37djvub7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fqnn3eavyjtus37djvub7.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Pretty simple, right? Once you get a hang of replacing the content with skeletons, you can set up a very versatile library of classes for replacing skeleton content and create the skeleton screens really quickly.&lt;/p&gt;




&lt;p&gt;These articles are fueled by coffee. So if you enjoy my work and found it useful, consider buying me a coffee! I would really appreciate it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.buymeacoffee.com/ubnZ8GgDJ" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.buymeacoffee.com%2Fbuttons%2Fdefault-red.png" alt="Buy Me A Coffee"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thank you for taking the time to read this post. If you've found this useful, please give it a ❤️ or 🦄, share and comment.&lt;/p&gt;

</description>
      <category>react</category>
      <category>webdev</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
