<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ben Taylor</title>
    <description>The latest articles on DEV Community by Ben Taylor (@taybenlor).</description>
    <link>https://dev.to/taybenlor</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F437805%2F7baff3e8-0388-4182-b7fd-309f3a18318f.jpeg</url>
      <title>DEV Community: Ben Taylor</title>
      <link>https://dev.to/taybenlor</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/taybenlor"/>
    <language>en</language>
    <item>
      <title>How I got Ruby snippets to run browser side in less than a day</title>
      <dc:creator>Ben Taylor</dc:creator>
      <pubDate>Fri, 28 Jan 2022 04:22:01 +0000</pubDate>
      <link>https://dev.to/taybenlor/how-i-got-ruby-snippets-to-run-client-side-in-less-than-a-day-4pbk</link>
      <guid>https://dev.to/taybenlor/how-i-got-ruby-snippets-to-run-client-side-in-less-than-a-day-4pbk</guid>
      <description>&lt;p&gt;Over the last year I've been working on Runno, an open source library and tool for embedding code snippets that run client side in the browser.&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%2Fuploads%2Farticles%2Fcgrcfgtyy6uo7it8pb9h.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%2Fuploads%2Farticles%2Fcgrcfgtyy6uo7it8pb9h.png" alt="Screenshot of the Runno website with Ruby support"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I had some time yesterday to work on Runno and decided to see if I could add another language. I'd heard on twitter that Ruby was looking into adding official WASM support, so I had a look into what that was. Turns out the &lt;a href="https://bugs.ruby-lang.org/issues/18462" rel="noopener noreferrer"&gt;proposal to merge WASI based WebAssembly support&lt;/a&gt; was perfect for me!&lt;/p&gt;

&lt;p&gt;WASI stands for WebAssembly System Interface and it's a standard way for WebAssembly (wasm) binaries to talk to a system. This allows developers to use a single binding interface to interact with multiple different binaries. It's been primarily adopted for server-side execution, but for my use case of runnable code snippets it also works well.&lt;/p&gt;

&lt;p&gt;I went and had a look at the PR for WASI support, assuming this would be a many month long process. When I looked &lt;a href="https://github.com/ruby/ruby/pull/5407" rel="noopener noreferrer"&gt;it had already been merged&lt;/a&gt;! To get Ruby running on Runno I'd need to figure out how to compile it to WASM. I went to look at the building instructions, then I thought - I wonder if someone has already put it up on WAPM (the WebAssembly Package Manager)?&lt;/p&gt;

&lt;p&gt;And it turns out that &lt;a href="https://github.com/kateinoigakukun" rel="noopener noreferrer"&gt;kateinoigakukun&lt;/a&gt; who wrote that PR had also packaged &lt;a href="https://wapm.io/package/katei/ruby" rel="noopener noreferrer"&gt;Ruby for WAPM&lt;/a&gt;. Super handy!&lt;/p&gt;

&lt;p&gt;With all of that sorted I could try out the package to see if it worked. Because there's a standard interface I don't need to put it into Runno to try it out, I can just use it in my terminal. I wrote an example ruby file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"G'day legend, how are ya?"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then I tried running it with the Ruby package on WAPM:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;wapm &lt;span class="nb"&gt;install &lt;/span&gt;katei/ruby
&lt;span class="nv"&gt;$ &lt;/span&gt;wapm run &lt;span class="nt"&gt;--dir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt; ruby example.rb 
G&lt;span class="s1"&gt;'day legend, how are ya?
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That looks like it works! Heck yeah!&lt;/p&gt;

&lt;p&gt;Runno installs its packages using WAPM, it's based off a fork of &lt;a href="https://webassembly.sh" rel="noopener noreferrer"&gt;WebAssembly.sh&lt;/a&gt; and so adding support for a new language already on WAPM is quite simple. If you're interested &lt;a href="https://github.com/taybenlor/runno/pull/181" rel="noopener noreferrer"&gt;here's the PR&lt;/a&gt;. The important change is:&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="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ruby&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="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`cat &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;entryPath&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; | ruby --disable=gems`&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;I'm using &lt;code&gt;cat&lt;/code&gt; to pipe the code over STDIN due to a bug I was seeing when the file path was passed as args to Ruby. I've also disabled gems because of another error I was seeing. I'll look into these in the future, but for now it works.&lt;/p&gt;

&lt;p&gt;The result is that you can now make quick embeddable Ruby snippets for your website! Try clicking the run button below.&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/taybenlor/embed/rNYVzOJ?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>webassembly</category>
      <category>javascript</category>
    </item>
    <item>
      <title>How to embed runnable code samples using Runno</title>
      <dc:creator>Ben Taylor</dc:creator>
      <pubDate>Sat, 20 Nov 2021 21:19:31 +0000</pubDate>
      <link>https://dev.to/taybenlor/how-to-embed-runnable-code-samples-using-runno-4f5c</link>
      <guid>https://dev.to/taybenlor/how-to-embed-runnable-code-samples-using-runno-4f5c</guid>
      <description>&lt;p&gt;A year or so ago I was playing around with building an online Introduction to Python course, but I got distracted by this problem that seemed like it should be solved: how can I embed runnable snippets on the web?&lt;/p&gt;

&lt;p&gt;There’s a bunch of closed-source solutions that rely on other services. I was hoping for a way to just install a package, and then run code. Especially small snippets like &lt;code&gt;print&lt;/code&gt;, &lt;code&gt;input&lt;/code&gt; and &lt;code&gt;if&lt;/code&gt; statements. &lt;/p&gt;

&lt;p&gt;So I started building &lt;a href="https://runno.dev" rel="noopener noreferrer"&gt;Runno&lt;/a&gt;. It lets you embed snippets of code from a few different languages (Python, JavaScript, C, C++ and SQL currently) within your website and runs it all client side using Web Assembly. You can use it without any packages using an iframe, or install the npm package and use it as a set of Web Components.&lt;/p&gt;

&lt;p&gt;If you're looking to embed a runnable snippet of code it's pretty straightforward with Runno. Let's say I'm writing a blog post explaining how &lt;code&gt;if&lt;/code&gt; statements work in Python, and I have this snippet of code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;name = input("What's your name? ")
if "i" in name.lower():
  print("You've got an I in your name, how selfish.")
else:
  print("There's no I in your name.")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;To use Runno all I have to do is visit the &lt;a href="https://runno.dev" rel="noopener noreferrer"&gt;runno.dev&lt;/a&gt; website and paste in my code snippet:&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%2Fuploads%2Farticles%2F9pf3t7l2ujlm2irq02dh.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%2Fuploads%2Farticles%2F9pf3t7l2ujlm2irq02dh.png" alt="Paste code snippets into the "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then copy the HTML for the iframe out of Runno, and into your blog. Here the iframe code is:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;iframe src="https://runno.run/?editor=1&amp;amp;runtime=python&amp;amp;code=bmFtZSA9IGlucHV0KCJXaGF0J3MgeW91ciBuYW1lPyAiKQppZiAiaSIgaW4gbmFtZS5sb3dlcigpOgogIHByaW50KCJZb3UndmUgZ290IGFuIEkgaW4geW91ciBuYW1lLCBob3cgc2VsZmlzaC4iKQplbHNlOgogIHByaW50KCJUaGVyZSdzIG5vIEkgaW4geW91ciBuYW1lLiIp" crossorigin allow="cross-origin-isolated" width="640" height="320" frameBorder="0"&amp;gt;&amp;lt;/iframe&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;You'll notice that it has a fixed &lt;code&gt;width&lt;/code&gt; and &lt;code&gt;height&lt;/code&gt;. This is to make it easy to paste in, but you can change those values!&lt;/p&gt;

&lt;p&gt;On Dev you can't put an iframe in, but you can use a Codepen. Here I've made a codepen that just has the iframe inside of it, then styled the iframe to fit the full space of the codepen.&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/taybenlor/embed/MWvRYyd?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;And now you can run the example code snippet!&lt;/p&gt;

&lt;p&gt;If you're interested in learning more ways to use Runno there's some documentation on the website: &lt;a href="https://runno.dev/#examples" rel="noopener noreferrer"&gt;https://runno.dev/#examples&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Or if you're interested in contributing - it's Open Source:&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&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%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/taybenlor" rel="noopener noreferrer"&gt;
        taybenlor
      &lt;/a&gt; / &lt;a href="https://github.com/taybenlor/runno" rel="noopener noreferrer"&gt;
        runno
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Browser-based runtime for programming languages and WASI binaries.
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;



</description>
      <category>news</category>
      <category>webassembly</category>
      <category>webdev</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Web Component ideas: Building a carousel</title>
      <dc:creator>Ben Taylor</dc:creator>
      <pubDate>Tue, 31 Aug 2021 13:37:23 +0000</pubDate>
      <link>https://dev.to/taybenlor/web-component-ideas-building-a-carousel-2dfe</link>
      <guid>https://dev.to/taybenlor/web-component-ideas-building-a-carousel-2dfe</guid>
      <description>&lt;p&gt;Carousels are a useful component for displaying a series of images (or really any media). People often use carousels off the shelf, but they're quite easy to build yourself! In this post I'll go through building a Web Component for a carousel.&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%2Fuploads%2Farticles%2F93lcbv765k34nciqgc7e.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%2Fuploads%2Farticles%2F93lcbv765k34nciqgc7e.gif" alt="Screen Recording 2021-08-31 at 10.46.44 pm"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can see here that as you click the next and previous buttons it scrolls through a series of images.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building elements that don't exist
&lt;/h2&gt;

&lt;p&gt;Web Components are handy tools for abstracting out common patterns in HTML. There's lots of user interface patterns that are common on the web but don't have their own elements. Instead you have to build them up yourself from other elements.&lt;/p&gt;

&lt;p&gt;If you find yourself repeating HTML, or the complexity gets too much, it can be handy to abstract out the complexity. By creating a Web Component you can create a neat abstraction that becomes reusable and easier to understand. Plus it makes your HTML much tidier!&lt;/p&gt;

&lt;h2&gt;
  
  
  What does the HTML look like?
&lt;/h2&gt;

&lt;p&gt;This Web Component is rather simple, it doesn't have any attributes - you just put some elements 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;ben-carousel&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;"http://placekitten.com/360/200"&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;"http://placekitten.com/300/200"&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;"http://placekitten.com/420/200"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/ben-carousel&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The idea here is to stick to standard elements as much as possible and to keep it simple. The carousel is going to be in charge of displaying the content like a carousel. Then the content itself can be anything! In this case I've put in three images of cats at different sizes.&lt;/p&gt;

&lt;p&gt;A big benefit of this approach, using Web Components, is that I can put any HTML content inside my carousel. It's just HTML! Plus I can use it in any website, no matter the library. Sticking to standards often makes things easier.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating the template
&lt;/h2&gt;

&lt;p&gt;To begin with I wrote down the elements I'd need to create this carousel:&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;template&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"ben-carousel"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;button&amp;gt;&lt;/span&gt;Prev&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;slot&amp;gt;&amp;lt;/slot&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;button&amp;gt;&lt;/span&gt;Next&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can see here that I've got two buttons, one for going left and one for going right. Then I've put a &lt;code&gt;slot&lt;/code&gt; element in, this is where the content inside will go. But this isn't quite enough for it to look like a carousel. Right now it looks like this:&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%2Fuploads%2Farticles%2F1cwb1t6wwirmqpxztk9s.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%2Fuploads%2Farticles%2F1cwb1t6wwirmqpxztk9s.png" alt="Screen Shot 2021-08-31 at 11.09.30 pm"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So we'll need to add some styling. Once we've got it looking like a carousel we can move on to making it work like a carousel.&lt;/p&gt;

&lt;p&gt;Here's how I styled 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;template&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"ben-carousel"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
    &lt;span class="nd"&gt;:host&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="nb"&gt;block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nf"&gt;#container&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;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nf"&gt;#images&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;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nl"&gt;flex-shrink&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nl"&gt;overflow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;scroll&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"container"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"prev"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Prev&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"images"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;slot&amp;gt;&amp;lt;/slot&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;button&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"next"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Next&lt;span class="nt"&gt;&amp;lt;/button&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;/template&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First lets look at the extra elements I added. I've created a &lt;code&gt;div&lt;/code&gt; to wrap all of the other elements, this will be useful for layout. Then I've added another &lt;code&gt;div&lt;/code&gt; for the images, this will help to control how much of them is displayed.&lt;/p&gt;

&lt;p&gt;You can also see here that I've given every element an &lt;code&gt;id&lt;/code&gt;. When you're working with Web Components the HTML and CSS is all scoped to within the component. So you can use &lt;code&gt;id&lt;/code&gt; as much as you please, it won't overlap with others on your page. This makes it much easier to write JavaScript and CSS.&lt;/p&gt;

&lt;p&gt;Now lets look at the style here. The first part makes it so the element displays as a &lt;code&gt;block&lt;/code&gt; element:&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="nd"&gt;:host&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="nb"&gt;block&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;Next up I've styled the outer container to use &lt;code&gt;display: flex&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="nf"&gt;#container&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;flex&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 makes it so that the three child elements (&lt;code&gt;#prev&lt;/code&gt;, &lt;code&gt;#images&lt;/code&gt;, and &lt;code&gt;#next&lt;/code&gt;) all display next to eachother horizontally. Which is key to the whole layout!&lt;/p&gt;

&lt;p&gt;Finally we're looking at the &lt;code&gt;#images&lt;/code&gt; container. Here I use &lt;code&gt;flex-shrink: 1&lt;/code&gt; to make it so the &lt;code&gt;#images&lt;/code&gt; container will shrink down when the width of its parent is constrained.&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="nf"&gt;#images&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;flex-shrink&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;overflow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;scroll&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;I've also used &lt;code&gt;display: flex&lt;/code&gt; here to make the children of the &lt;code&gt;#images&lt;/code&gt; container (the images) all display next to eachother in a line. Finally &lt;code&gt;overflow: scroll&lt;/code&gt; says that the &lt;code&gt;#images&lt;/code&gt; container should create a scrollbar if its content has overflowed it.&lt;/p&gt;

&lt;p&gt;The result looks like this:&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%2Fuploads%2Farticles%2Fytkty12gjg620dr7n5so.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%2Fuploads%2Farticles%2Fytkty12gjg620dr7n5so.png" alt="Screen Shot 2021-08-31 at 11.21.59 pm"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That's just about everything! The only part we don't have is the buttons.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wiring up the buttons
&lt;/h2&gt;

&lt;p&gt;To wire up the buttons we'll need to write some JavaScript. First we'll set up some boilerplate for creating the carousel element:&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="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CarouselElement&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;HTMLElement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attachShadow&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;open&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;template&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;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;carousel&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;clone&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cloneNode&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shadowRoot&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;clone&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;customElements&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;define&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ben-carousel&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;CarouselElement&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we want to wire up the events. Because we've already given each of our elements an &lt;code&gt;id&lt;/code&gt; this is rather easy. First we get each of the elements we need from the &lt;code&gt;shadowRoot&lt;/code&gt;:&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="c1"&gt;// ... inside the constructor&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;images&lt;/span&gt; &lt;span class="o"&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;shadowRoot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;images&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;prev&lt;/span&gt; &lt;span class="o"&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;shadowRoot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;prev&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt; &lt;span class="o"&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;shadowRoot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;next&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;p&gt;Then we set up click handlers that scroll the images container:&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="c1"&gt;// ... inside the constructor&lt;/span&gt;

&lt;span class="nx"&gt;prev&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;click&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="o"&gt;=&amp;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="nx"&gt;scrollLeft&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="nx"&gt;nextButton&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;click&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="o"&gt;=&amp;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="nx"&gt;scrollLeft&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;100&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 bingo bango we've got a scrolling carousel!&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/taybenlor/embed/YzQwbGb?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Interested in Web Components?
&lt;/h2&gt;

&lt;p&gt;I'm speaking about &lt;a href="https://webdirections.org/code/speakers/ben-taylor.php" rel="noopener noreferrer"&gt;Practical Uses for Web Components&lt;/a&gt; at &lt;a href="https://webdirections.org/code/" rel="noopener noreferrer"&gt;Web Directions: Code&lt;/a&gt; on September 17 &amp;amp; 24 2021. If you're interested you can use the voucher &lt;code&gt;bensentme&lt;/code&gt; to get 20% off!&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%2Fuploads%2Farticles%2Fd19ypndwqrlp9wydz8t3.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%2Fuploads%2Farticles%2Fd19ypndwqrlp9wydz8t3.png" alt="DElgPan_Fq7SbJfAKW_1Q_L_U2HSzzpnKuwR1onC9M3VGLRykUod7atRXJTePP4wGCk3H995w7W7-BfXrNnw8jL52tDBrrmSASqYKUIT0i0nAWDRo59G0OPiRF_UVEP0A4tVGGxS"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Web Component ideas: Making an if element</title>
      <dc:creator>Ben Taylor</dc:creator>
      <pubDate>Mon, 30 Aug 2021 12:07:51 +0000</pubDate>
      <link>https://dev.to/taybenlor/web-component-ideas-making-an-if-element-5g8a</link>
      <guid>https://dev.to/taybenlor/web-component-ideas-making-an-if-element-5g8a</guid>
      <description>&lt;p&gt;I'm a big fan of Web Components, they're super useful. But have you ever thought of using them as an if statement?&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%2Fuploads%2Farticles%2Fu02dmjaku8uqqygvwww2.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%2Fuploads%2Farticles%2Fu02dmjaku8uqqygvwww2.gif" alt="Screen Recording 2021-08-30 at 9.23.59 pm"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;If the user selects the cat radio element, then the "Yep, cats are the best!" message will be displayed. If they select the dog radio element, then the other message will be displayed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Web Components for authoring content
&lt;/h2&gt;

&lt;p&gt;People tend to think about web development as a way of building apps. But that misses a big category of what people do on the web: making documents! Back in the day people used to write raw HTML straight to their web servers. But now we write in a CMS or a blogging system like this one. In those sorts of systems you tend to use a rich text editor, or markdown.&lt;/p&gt;

&lt;p&gt;HTML is still really good for making documents though! If you want to make something really custom, HTML is a great tool. Using Web Components lets you do even more interesting things, things that go beyond just bold, italic and headings. You can create custom logic as well! That's the kind of stuff that &lt;em&gt;hypertext&lt;/em&gt; should be used for.&lt;/p&gt;

&lt;p&gt;If you're maintaining a website, blog, or CMS with authors who like to do interesting things you should try out Web Components. They're easy to write and they're custom just for your purpose!&lt;/p&gt;

&lt;h2&gt;
  
  
  What does the HTML look like?
&lt;/h2&gt;

&lt;p&gt;In this example I wanted to create a kind of if-statement that changed which content was displayed based on which option was selected in some radio buttons. I started by writing out the HTML, so I could get a sense of how it should work:&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;label&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"radio"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"animal"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"cat"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  Cat
&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;label&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"radio"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"animal"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"dog"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  Dog
&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;ben-if&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"animal"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"cat"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;
    Yep, cats are the best!
  &lt;span class="nt"&gt;&amp;lt;/p&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;"http://placekitten.com/200/100"&lt;/span&gt; &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"kitten"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/ben-if&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;ben-if&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"animal"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"dog"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;
    Dogs are pretty good, but have you tried cats?
  &lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/ben-if&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can see here I'm creating a custom element called &lt;code&gt;ben-if&lt;/code&gt; which has two attributes &lt;code&gt;name&lt;/code&gt; and &lt;code&gt;value&lt;/code&gt;. The idea is that if the matching radio box is ticked, then the if statement will show. Otherwise it will be hidden.&lt;/p&gt;

&lt;p&gt;Because they're just HTML, I can put other HTML elements inside them without any issues. If you were using a markdown parser that allowed HTML, you could also put markdown inside the HTML. This makes it super flexible, so I could make lots of different sorts of things with just this one trick.&lt;/p&gt;

&lt;p&gt;There's a lot of other benefits here to using web components. You don't need to include any third party libraries, and you don't need to set up a rendering context. It will work across any framework, including React, Vue, Svelte etc. It's part of the way the browser works!&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating the template
&lt;/h2&gt;

&lt;p&gt;To write my web component, I needed a template. This template is really simple because it doesn't do much. This is the HTML for 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;template&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"ben-if"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
    &lt;span class="nd"&gt;:host&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="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;slot&amp;gt;&amp;lt;/slot&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the styling here the &lt;code&gt;:host&lt;/code&gt; element refers to the web component I'm building. I've made it &lt;code&gt;display: none&lt;/code&gt; so that it is hidden by default. The &lt;code&gt;&amp;lt;slot&amp;gt;&amp;lt;/slot&amp;gt;&lt;/code&gt; element is where child content will be put inside this element.&lt;/p&gt;

&lt;h2&gt;
  
  
  Writing the javascript
&lt;/h2&gt;

&lt;p&gt;The logic for this is a little bit more complicated. First I've set up some boilerplate. This renders the template I created into the web component, and keeps track of the &lt;code&gt;name&lt;/code&gt; and &lt;code&gt;value&lt;/code&gt; attributes. It also defines the custom element I've created as &lt;code&gt;ben-if&lt;/code&gt;.&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="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;IfElement&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;HTMLElement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nf"&gt;observedAttributes&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;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;name&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;value&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="nf"&gt;constructor&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attachShadow&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;open&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;template&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;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ben-if&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;clone&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cloneNode&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shadowRoot&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;clone&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;attributeChangedCallback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;oldValue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;newValue&lt;/span&gt;&lt;span class="p"&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;name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;newValue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Define this custom element&lt;/span&gt;
&lt;span class="nx"&gt;customElements&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;define&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ben-if&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;IfElement&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that I've got the boilerplate out of the way, it's time to do the logic. I created a &lt;code&gt;checkIf&lt;/code&gt; method on my &lt;code&gt;IfElement&lt;/code&gt; to show or hide my element:&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="nf"&gt;checkIf&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="nx"&gt;radio&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;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`[name="&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;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&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;value&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"]:checked`&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;radio&lt;/span&gt;&lt;span class="p"&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;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;block&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="k"&gt;else&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;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;none&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;This will query the document to find a checked element with the matching &lt;code&gt;name&lt;/code&gt; and &lt;code&gt;value&lt;/code&gt;. If there is one, it will set the element to &lt;code&gt;display: block&lt;/code&gt;. If there isn't one it will set the element to &lt;code&gt;display: none&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now we just need to wire that call up. I put it in two places:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;As an event that gets called any time a change event happens on the page&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;After the attributes change.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&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;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;change&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="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="nf"&gt;checkIf&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="nf"&gt;attributeChangedCallback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;oldValue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;newValue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&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;checkIf&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 that's everything! Now it should all work together. Here's a codepen:&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/taybenlor/embed/xxrZZpp?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Interested in Web Components?
&lt;/h2&gt;

&lt;p&gt;I'm speaking about &lt;a href="https://webdirections.org/code/speakers/ben-taylor.php" rel="noopener noreferrer"&gt;Practical Uses for Web Components&lt;/a&gt; at &lt;a href="https://webdirections.org/code/" rel="noopener noreferrer"&gt;Web Directions: Code&lt;/a&gt; on September 17 &amp;amp; 24 2021. If you're interested you can use the voucher &lt;code&gt;bensentme&lt;/code&gt; to get 20% off!&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%2Fuploads%2Farticles%2Fd19ypndwqrlp9wydz8t3.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%2Fuploads%2Farticles%2Fd19ypndwqrlp9wydz8t3.png" alt="DElgPan_Fq7SbJfAKW_1Q_L_U2HSzzpnKuwR1onC9M3VGLRykUod7atRXJTePP4wGCk3H995w7W7-BfXrNnw8jL52tDBrrmSASqYKUIT0i0nAWDRo59G0OPiRF_UVEP0A4tVGGxS"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Web Assembly should be a default binary target</title>
      <dc:creator>Ben Taylor</dc:creator>
      <pubDate>Fri, 20 Aug 2021 10:44:28 +0000</pubDate>
      <link>https://dev.to/taybenlor/web-assembly-should-be-a-default-binary-target-44m6</link>
      <guid>https://dev.to/taybenlor/web-assembly-should-be-a-default-binary-target-44m6</guid>
      <description>&lt;p&gt;WebAssembly (WASM) is no longer a toy environment for cool demos. It's a thriving ecosystem that enables high-performance web applications with rich functionality. It's a portable code system that can run binaries in the cloud, or on your local machine. This may seem like an extreme position, but hear me out: if you develop a compiled library, language, or other tool you should consider making WASM a default binary target.&lt;/p&gt;

&lt;p&gt;Here are my main reasons:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Front-end web apps can handle workloads we'd previously reserve for the backend. Like image processing, or file encoding. This reduces bandwidth and speeds up apps.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;WASM doesn't only run in the browser, you can use it from in-language bindings or an interpreter. This makes it a portable format that doesn't require compilation or platform-specific versions.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;WASM runs in a sandboxed environment controlled by the host. The host can be certain that a library will not have network or file access unless it's provided.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;With portability we get freedom. Freedom to run software anywhere we want without the arduous process of figuring out how to compile and run it.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let's dive into that a bit further.&lt;/p&gt;

&lt;h1&gt;
  
  
  What's changed with WASM
&lt;/h1&gt;

&lt;p&gt;When the whole concept of WASM started it was a big ol' hack. A neat trick to compile C programs so that they would run fast in a Javascript runtime. Emscripten really pushed the limits of what we thought was possible here and we saw some pretty amazing demos: some people compiled interpreted languages like Python to WASM, the legends at Unreal got their engine running inside the browser, it was all very cool. Gary Bernhardt even did a talk about &lt;a href="https://www.destroyallsoftware.com/talks/the-birth-and-death-of-javascript" rel="noopener noreferrer"&gt;how WASM would eat the world&lt;/a&gt;. But WASM has changed a lot since then.&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%2Fuploads%2Farticles%2Fq3612q3hm740g5vhcgbe.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%2Fuploads%2Farticles%2Fq3612q3hm740g5vhcgbe.png" alt="Screen Shot 2021-08-20 at 7.46.03 pm"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Gary talking about Unreal Engine running in WASM&lt;/em&gt; &lt;/p&gt;

&lt;p&gt;The most important change to happen to WASM is the &lt;a href="//wasi.dev"&gt;WebAssembly System Interface (WASI)&lt;/a&gt;. WASI is a system interface for a conceptual OS. It allows system software developers to bind to POSIX-like system calls, and for hosts to implement that functionality in a way that makes sense for their runtime. The result is flexible, but not so far from Linux, macOS, and Windows that it can't be practically implemented.&lt;/p&gt;

&lt;p&gt;What's really neat about WASI is that it's based on a capability-oriented sandboxing system. The host who runs the binary can scope file access, proxy network access, and disable system calls as they see fit. This sort of sandboxing can prevent whole categories of security issues because it creates an extra layer between your application and its dependencies, reducing the possible impact scope if a dependency does get compromised.&lt;/p&gt;
&lt;h1&gt;
  
  
  Running WASM outside the Browser
&lt;/h1&gt;

&lt;p&gt;With improvements in WASM has come an increased interest in running WASM outside the browser. This is a really interesting space of innovation that is made even easier with new tools for WASI. The obvious candidates are running WASM inside v8 runtimes. For example, Cloudflare's Workers platform uses v8 (Chrome's JS) as a way for them to run typical Javascript serverless functions as well as WASM-compiled binaries from other languages. &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%2Fuploads%2Farticles%2Fpffmjlwgl5k0663pmstd.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%2Fuploads%2Farticles%2Fpffmjlwgl5k0663pmstd.png" alt="Diagram about how Cloudflare Workers works"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;From Cloudflare: &lt;a href="https://developers.cloudflare.com/workers/learning/how-workers-works" rel="noopener noreferrer"&gt;How Workers Works&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;But why run v8 at all?&lt;/strong&gt; Good question! A WASM runtime is much simpler to implement than a Javascript runtime. You could absolutely implement one yourself. But rather than doing that, there's off the shelf solutions. &lt;a href="https://wasmer.io/" rel="noopener noreferrer"&gt;Wasmer&lt;/a&gt; has runtime bindings for the major languages: Python, Ruby, PHP, Go, Rust, C/C++, and Javascript. The &lt;a href="https://bytecodealliance.org/" rel="noopener noreferrer"&gt;Bytecode Alliance&lt;/a&gt; has their own runtime that works in .NET, Python, Go, Rust, C, and C++. Plus I'm sure they're both working on more.&lt;/p&gt;

&lt;p&gt;If you've got a WASM runtime for your language, you can load in a WASM binary, write some simple bindings to call into that binary and presto! You're running system code independent of OS or CPU. Just sling that bad boy on to whatever computer you want and press execute.&lt;/p&gt;

&lt;p&gt;Now you know you could run WASM from your Python code, but why on earth would you? Here's an experience you might be familiar with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;install &lt;/span&gt;cool-library
Downloading cool-library...
Downloading dependencies: cool-dependency, ruby, gcc
Compiling gcc...
Compiling ruby...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You wanted to install &lt;code&gt;cool-library&lt;/code&gt; but to get it, you need its dependencies. And those dependencies have dependencies. Meanwhile you're screaming, your computer's fan is screaming, and your package manager is compiling gcc, so that it can compile Ruby, so that some transitive dependency works. In most cases it's not really that bad, but it's familiar right?&lt;/p&gt;

&lt;p&gt;We've kind of solved this problem with Docker. Things are at least better than they were. But it's a lot of overhead. You're setting up this whole shared kernel isolation system just to run a single binary. Seems like a portable binary would be better.&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%2Fuploads%2Farticles%2Fk222dlxdmi72siks60b8.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%2Fuploads%2Farticles%2Fk222dlxdmi72siks60b8.png" alt="Portable binaries"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Go away &lt;a href="https://en.wikipedia.org/wiki/James_Gosling" rel="noopener noreferrer"&gt;James Gosling&lt;/a&gt;, inventor of Java. This is totally different.&lt;/em&gt; &lt;/p&gt;

&lt;p&gt;This is where WASM shines! &lt;code&gt;cool-library&lt;/code&gt; releases a WASM binary instead, we download it, and we run it from our favourite language. No need to install all of its dependencies, no need to compile gcc, no need to containerise an operating system. Just a binary you can run anywhere. It'll work on your linux server, it'll work on Windows, it'll work on your shiny new M1 MacBook. Heck, it'll work on the little chip running in your lightbulb that forwards your darkest secrets to Amazon. It'll run in more places than Javascript does!&lt;/p&gt;

&lt;p&gt;If you think the idea of managing binary versions sounds terrifying, don't even worry about it. There's WAPM the WASM Package Manager right there to solve that problem for you.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;wapm &lt;span class="nb"&gt;install &lt;/span&gt;cool-library
&lt;span class="o"&gt;[&lt;/span&gt;INFO] Installing _/cool-library@0.2.0
Package installed successfully to wapm_packages!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That was easier!&lt;/p&gt;

&lt;p&gt;This doesn't just stop at running WASM from your favourite language. Why do you need a language at all? With WAPM you can run the binary directly, like it's native. That means if a CLI tool is available in WAPM, you can install it and run it on your machine. No dependencies, no compilation, just a binary you install and run.&lt;/p&gt;

&lt;p&gt;And if you want to deploy that WASM binary to production, there's another suite of tools for doing that. &lt;a href="https://github.com/WasmEdge/WasmEdge" rel="noopener noreferrer"&gt;WasmEdge&lt;/a&gt; is a runtime made for cloud computing. Or skip the runtime altogether and compile your WASM to a native binary with &lt;a href="https://bytecodealliance.github.io/lucet/Overview.html" rel="noopener noreferrer"&gt;Lucet&lt;/a&gt;, giving you all the security and sandboxing benefits of WASM and all of the performance benefits of a native binary.&lt;/p&gt;

&lt;h1&gt;
  
  
  Running WASI inside the Browser
&lt;/h1&gt;

&lt;p&gt;Okay, so we know we can run these system binaries on your machine, or on your servers and that all makes sense. But why would you run them in the browser? What are you going to do? Run ffmpeg on the client side?!&lt;/p&gt;

&lt;p&gt;Yes, yes I bloody well am. I'm gonna run whatever I want! If I want to &lt;a href="https://ffmpegwasm.netlify.app/" rel="noopener noreferrer"&gt;run ffmpeg in the browser&lt;/a&gt; I should be able to. If I want to &lt;a href="https://www.youtube.com/watch?v=5N4b-rU-OAA&amp;amp;t=4s" rel="noopener noreferrer"&gt;run clang in the browser&lt;/a&gt; I should be able to. If I want to download some open source printer drivers and run them in the browser I should be able to!&lt;/p&gt;

&lt;p&gt;Portability is a matter of freedom. The whole promise of computing is universality. Being able to run libraries wherever I want is extremely powerful. Maybe you can't imagine why I might want to, but that's the point of open source tools. You put them out there and then other people do amazing and incredible things with them - things you never dreamed of.&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%2Fraw.githubusercontent.com%2Fffmpegwasm%2Fffmpeg.wasm%2Fmaster%2Fdocs%2Fimages%2Ftranscode.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%2Fraw.githubusercontent.com%2Fffmpegwasm%2Fffmpeg.wasm%2Fmaster%2Fdocs%2Fimages%2Ftranscode.gif" alt="ffmpeg running in the browser"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Because ffmpeg runs in the browser there are now video editors that run in the browser. That may seem like a silly idea, but even just the basics of video editing are useful for many people, and by running on the client-side you save a lot of bandwidth. The traditional way to process videos would be to transcode them server-side, then provide controls for the user to make small edits like trimming the video. Because the user is so "far away" from the video file, making this interactive is very difficult. Plus you have to upload the whole raw file first.&lt;/p&gt;

&lt;p&gt;By running on the client-side we can trim, and transcode before uploading. This means that the user gets an interactive trimming user interface that responds instantly. Then when they're done, instead of uploading a massive raw file, they can transcode locally and then upload just what the site needs. This isn't a fantasy, Discourse uses a similar workflow to &lt;a href="https://blog.discourse.org/2021/07/faster-user-uploads-on-discourse-with-rust-webassembly-and-mozjpeg/" rel="noopener noreferrer"&gt;optimise images before uploading them&lt;/a&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flrmnsizxeviswzzqkc93.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%2Fuploads%2Farticles%2Flrmnsizxeviswzzqkc93.png" alt="maconha"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Wouldn't you like to save bandwidth like Discourse did?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;But this isn't just for media. Have you ever used Jupyter Notebooks? It's a document format for data science where you can interweave text explanations with code that executes on data and shows intermediate results. It’s a great way to communicate research results, since the results of the research are sent along with how to get those results. If you want to check the working, it's right there!&lt;/p&gt;

&lt;p&gt;Jupyter Notebooks require a connection to a server that can run the Python code. The server can run locally, but you have to fire it up yourself. Or you could pay for someone to run a server for you, upload the document there and then run it. But what if you just want to have a quick look?&lt;/p&gt;

&lt;p&gt;This isn't how we're used to browsing documents. Nowadays you just open up a website and read the document right there. That's exactly what &lt;a href="https://blog.jupyter.org/jupyterlite-jupyter-%EF%B8%8F-webassembly-%EF%B8%8F-python-f6e2e41ab3fa" rel="noopener noreferrer"&gt;JupyterLite&lt;/a&gt; allows you to do. It's an entirely in-browser version of Jupyter based off the &lt;a href="https://pyodide.org/en/stable/" rel="noopener noreferrer"&gt;Pyodide project from Mozilla&lt;/a&gt;. Pyodide is Python compiled to WASM, and packaged up with a bunch of standard scientific python packages.&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%2Fuploads%2Farticles%2Fukss90l82fk7hnh6pcba.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%2Fuploads%2Farticles%2Fukss90l82fk7hnh6pcba.gif" alt="IPython running in the browser with JupyterLite"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Reproducible science is even more accessible because Mozilla put the effort into getting Python running in WASM. Imagine what other good could be done by getting the rest of our programming languages onto WASM? Library documentation could have runnable examples. Bug reports could include a runnable reproduction. Technical articles could run the real thing, in your browser, with no need to install locally. I could tweet a program, and you could run it &lt;em&gt;inside the tweet&lt;/em&gt;!&lt;/p&gt;

&lt;h1&gt;
  
  
  Ultimately it's about freedom
&lt;/h1&gt;

&lt;p&gt;One of the most frustrating parts of programming is finding good matches of tools and environments. You find a great library for solving your problem, but it's for another language. You find a database you want to use, but there's no binary for your architecture. You're familiar with a programming language, but it doesn't run where you want it. You deploy your code and it fails because your dev environment doesn't match your production environment!&lt;/p&gt;

&lt;p&gt;We emulate, we containerise, we port, we put in a lot of effort to solve these problems. But all these barriers could go away. We could run software wherever we wanted, however we wanted.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;WebAssembly should be a default binary target.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>webassembly</category>
      <category>webdev</category>
      <category>devops</category>
    </item>
    <item>
      <title>Understanding render-as-you-fetch with React &amp; Relay</title>
      <dc:creator>Ben Taylor</dc:creator>
      <pubDate>Thu, 03 Jun 2021 06:51:55 +0000</pubDate>
      <link>https://dev.to/taybenlor/understanding-render-as-you-fetch-with-relay-11c</link>
      <guid>https://dev.to/taybenlor/understanding-render-as-you-fetch-with-relay-11c</guid>
      <description>&lt;p&gt;I've been moving an existing codebase to a GraphQL API over the last few weeks using &lt;a href="https://relay.dev/" rel="noopener noreferrer"&gt;Relay&lt;/a&gt; as the front-end client. One thing I've been struggling with has been implementing the render-as-you-fetch (or fetch-as-you-render) pattern. A big part of the difficulty here is how our tools rely on the render path for coordinating work. I'm using this article as a way to write down what I've learned researching and figuring out this pattern in practice.&lt;/p&gt;

&lt;h1&gt;
  
  
  What is render-as-you-fetch?
&lt;/h1&gt;

&lt;p&gt;I'm not sure about the origin of the idea, but there's a great explanation of it in the &lt;a href="https://www.youtube.com/watch?v=JDDxR1a15Yo&amp;amp;t=3647s" rel="noopener noreferrer"&gt;ReactConf 2019 demo of Relay&lt;/a&gt;. There's also some good explanations in the &lt;a href="https://reactjs.org/docs/concurrent-mode-suspense.html#approach-3-render-as-you-fetch-using-suspense" rel="noopener noreferrer"&gt;React Docs for Suspense&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The basic idea is that the render path of your components is a bad place to load data. The simplest reason is that it can be blocked by other components loading. If you only load data on the render path, you can be susceptible to waterfalls of loads. The worst case is one component blocks a number of other components from rendering, then when it unblocks them all of those components need to load their own data.&lt;/p&gt;

&lt;p&gt;Imagine a profile page for a user:&lt;/p&gt;

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

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;ProfilePage&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;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;isLoaded&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;profileData&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useProfileDataFetcher&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;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;isLoaded&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;LoadingSpinner&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&amp;lt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ProfileHeader&lt;/span&gt; &lt;span class="na"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;profileData&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;PhotoCarousel&lt;/span&gt; &lt;span class="na"&gt;photoIds&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;profileData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;recentPhotoIds&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;PostList&lt;/span&gt; &lt;span class="na"&gt;postIds&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;profileData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;recentPostIds&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;/&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;You could imagine that the &lt;code&gt;PhotoCarousel&lt;/code&gt; component and the &lt;code&gt;PostList&lt;/code&gt; component both need to go get their own data. So you have one fetch (the profile data) blocking two more fetches. Each of those components could also be fetching data, such as comments, avatars etc. This creates a cascade of loading symbols 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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fevk6hgv8xdi7f4xan5lb.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%2Fuploads%2Farticles%2Fevk6hgv8xdi7f4xan5lb.png" alt="Waterfall Loading"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When the first component finishes loading, it reveals its dependent child components - which of course now need to load!&lt;/p&gt;

&lt;p&gt;These waterfalls show a real flaw in the pattern of loading data inside a component (on the render path). It creates an awkward UX and makes your page much slower to load (even if your individual components are quite performant).&lt;/p&gt;

&lt;h2&gt;
  
  
  An aside on Suspense for  Data Loading
&lt;/h2&gt;

&lt;p&gt;To fully grasp the render-as-you-fetch pattern you also need to understand how &lt;a href="https://reactjs.org/docs/concurrent-mode-suspense.html" rel="noopener noreferrer"&gt;Suspense for Data Loading works&lt;/a&gt;. It's a really nifty pattern that works kind of like an Error Boundary. You set it up by creating a &lt;code&gt;Suspense&lt;/code&gt; component with a fallback loading component:&lt;/p&gt;

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

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Suspense&lt;/span&gt; &lt;span class="na"&gt;fallback&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;LoadingSpinner&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;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ProfilePage&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;Suspense&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Then if a component starts rendering, but is not yet ready to render you &lt;code&gt;throw&lt;/code&gt; a &lt;code&gt;Promise&lt;/code&gt; that will resolve when it's ready. To use it in our example we could modify our &lt;code&gt;useFetchProfileData&lt;/code&gt; hook to throw if the data isn't finished loading.&lt;/p&gt;

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

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;profileFetcher&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;ProfileDataFetcher&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;useProfileDataFetcher&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;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;profileFetcher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loadFromNetworkOrCache&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;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;profileFetcher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isLoading&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;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;profileFetcher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getPromise&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;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;profileFetcher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getData&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;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;The Promise that we throw then gets waited on by the &lt;code&gt;Suspense&lt;/code&gt; component until it's complete. In its place the &lt;code&gt;LoadingSpinner&lt;/code&gt; is rendered. Once it's complete the component will continue rendering.&lt;/p&gt;

&lt;p&gt;A neat result of this, is that we don't need to handle managing loading state within our component. Instead we can assume we &lt;em&gt;always&lt;/em&gt; have the data we depend on. This simplifies our &lt;code&gt;ProfilePage&lt;/code&gt; quite a bit:&lt;/p&gt;

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

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;ProfilePage&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;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;profileData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useProfileDataFetcher&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;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&amp;lt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ProfileHeader&lt;/span&gt; &lt;span class="na"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;profileData&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;PhotoCarousel&lt;/span&gt; &lt;span class="na"&gt;photoIds&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;profileData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;recentPhotoIds&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;PostList&lt;/span&gt; &lt;span class="na"&gt;postIds&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;profileData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;recentPostIds&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;/&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;But it doesn't stop our waterfall cascade of loading spinners.&lt;/p&gt;

&lt;h2&gt;
  
  
  Back to our Waterfall
&lt;/h2&gt;

&lt;p&gt;The simplest solution to this problem would be to fetch all of the nested data in the &lt;code&gt;ProfilePage&lt;/code&gt; component at once. The &lt;code&gt;ProfilePage&lt;/code&gt; would load the profile data, the photos, the posts, the usernames etc. But this breaks down in a number of situations:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Nested routes - you can't know what data you'll need at each level until you evaluate the routes&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Concurrent mode - your data-loading could be inside a component that has paused rendering&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Slow components - the performance of your data loading is dependent on how fast your components evaluate&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Re-rendering - each time your component is rendered it needs to retry fetching the data, even if it's unnecessary (e.g. a theme change)&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The solution to all of these problems is &lt;strong&gt;render-as-you-fetch&lt;/strong&gt;. Instead of putting the fetching code inside of your component, you put it outside the component, and make sure it happens &lt;strong&gt;before&lt;/strong&gt; the render even occurs. Imagine something like:&lt;/p&gt;

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

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;ProfileButton&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;span class="nx"&gt;name&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="nx"&gt;router&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useRouter&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;clickAction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;profileFetcher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&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;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;navigateToProfilePage&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;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;clickAction&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;name&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;button&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;When the button is clicked the &lt;code&gt;clickAction&lt;/code&gt; first loads the profile data, and then triggers navigation. This way the loading happens not only before the &lt;code&gt;ProfilePage&lt;/code&gt; starts loading, but it happens outside of the render path. So complicated render logic has no way of impacting when the data gets loaded.&lt;/p&gt;

&lt;p&gt;In relay this is all achieved using two hooks:&lt;/p&gt;

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

&lt;span class="c1"&gt;// From a container&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;queryRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;loadQuery&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useQueryLoader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="cm"&gt;/*...*/&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// Inside your component&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;usePreloadedQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;queryRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="cm"&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 first provides us with a &lt;code&gt;loadQuery&lt;/code&gt; function that can be called to start the query loading, and a &lt;code&gt;queryRef&lt;/code&gt; that will refer to that state. The second takes the &lt;code&gt;queryRef&lt;/code&gt; and returns the data - or suspends if it hasn't loaded yet. There's also a less safe &lt;code&gt;loadQuery&lt;/code&gt; function provided by Relay that doesn't automatically dispose of data.&lt;/p&gt;

&lt;p&gt;Our &lt;code&gt;ProfileButton&lt;/code&gt; example above, when using Relay would become something like:&lt;/p&gt;

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

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;ProfileButton&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;span class="nx"&gt;name&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="nx"&gt;router&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useRouter&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;queryRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;loadQuery&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useQueryLoader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="cm"&gt;/*...*/&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;clickAction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;loadQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="cm"&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;userId&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;navigateToProfilePage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;queryRef&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;clickAction&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;name&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;button&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;And our &lt;code&gt;Profile&lt;/code&gt; component would look like:&lt;/p&gt;

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

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;ProfilePage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;queryRef&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="nx"&gt;profileData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;usePreloadedQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;queryRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="cm"&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;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ProfileHeader&lt;/span&gt; &lt;span class="na"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;profileData&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;PhotoCarousel&lt;/span&gt; &lt;span class="na"&gt;photos&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;profileData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;recentPhotos&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;PostList&lt;/span&gt; &lt;span class="na"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;profileData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;recentPosts&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;/&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;Here the &lt;code&gt;queryRef&lt;/code&gt; is passed on to the &lt;code&gt;ProfilePage&lt;/code&gt; so that it has a handle for the data loading. Then the &lt;code&gt;usePreloadedQuery&lt;/code&gt; call will suspend if the data is still loading.&lt;/p&gt;

&lt;h1&gt;
  
  
  Routing with render-as-you-fetch
&lt;/h1&gt;

&lt;p&gt;The big difficulty with all of this is that it starts falling apart when you consider routing. If you trigger fetching just before a navigation (like in the above example) what happens if the user visits that route directly? It would fail to load, because the &lt;code&gt;queryRef&lt;/code&gt; hasn't been created.&lt;/p&gt;

&lt;p&gt;In the &lt;a href="https://www.youtube.com/watch?v=JDDxR1a15Yo&amp;amp;t=3647s" rel="noopener noreferrer"&gt;ReactConf 2019 Relay demo video&lt;/a&gt; that I linked earlier they solve this with a thing called an "Entrypoint". This is a concept that wraps up two tasks together:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Preloading data with &lt;code&gt;preloadQuery&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Retrieving the &lt;code&gt;lazy&lt;/code&gt; component for the route&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In this case the idea is that each routing entrypoint contains a helper for loading its data, and it uses &lt;a href="https://webpack.js.org/guides/code-splitting/" rel="noopener noreferrer"&gt;webpack codesplitting&lt;/a&gt; for lazy-loading each route's component hierarchy.&lt;/p&gt;

&lt;p&gt;Using &lt;code&gt;react-router&lt;/code&gt; attempting this approach, the entrypoint would look something like:&lt;/p&gt;

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

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Profile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;lazy&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;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./Profile&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;function&lt;/span&gt; &lt;span class="nf"&gt;ProfileEntrypoint&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;profileId&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useParams&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;queryRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;loadQuery&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useQueryLoader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="cm"&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;profileId&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="nf"&gt;loadQuery&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;Profile&lt;/span&gt; &lt;span class="na"&gt;queryRef&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;queryRef&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;And our routes would look like:&lt;/p&gt;

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

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Router&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;Header&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;path&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"/profile/:profileId"&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;ProfileEntrypoint&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;Router&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;But this isn't going to work!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Unfortunately we've violated one of the rules we created going in: we've put the data fetching on the render path. Because our entrypoint is a component, and we call &lt;code&gt;loadQuery&lt;/code&gt; when the component renders, the loading happens in the render path. &lt;/p&gt;

&lt;p&gt;Our fundamental problem here is that the routing paths are evaluated during render, and not when the history object triggers a change. From what I understand it doesn't seem like its possible to resolve this. That means &lt;code&gt;react-router&lt;/code&gt; is out. So is any router that evaluates its routes through components!&lt;/p&gt;

&lt;h2&gt;
  
  
  Finding a suitable router
&lt;/h2&gt;

&lt;p&gt;So now we need to find a suitable router that can support this pattern of requesting data outside of the render path. The relay community has built an &lt;a href="https://github.com/relay-tools/found-relay" rel="noopener noreferrer"&gt;extension to Found&lt;/a&gt; - but it hasn't been updated for render-as-you-fetch. The &lt;a href="https://github.com/4Catalyzer/found" rel="noopener noreferrer"&gt;Found&lt;/a&gt; router itself is quite flexible and extensible and so you could potentially implement entrypoints on top, but I haven't seen an example of this. As for other routers, I haven't seen any that aren't taking the &lt;code&gt;react-router&lt;/code&gt; approach.&lt;/p&gt;

&lt;p&gt;It seems like this is a problem that the &lt;code&gt;relay&lt;/code&gt; team have seen in advance. Their &lt;a href="https://github.com/relayjs/relay-examples/tree/master/issue-tracker" rel="noopener noreferrer"&gt;Issue Tracker example&lt;/a&gt; rolls its own routing system based off the same primitives used by &lt;code&gt;react-router&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;There's also a couple of routers that people have built after encountering this problem: &lt;a href="https://github.com/dai-shi/react-suspense-router" rel="noopener noreferrer"&gt;React Suspense Router&lt;/a&gt; and &lt;a href="https://github.com/kyarik/pre-router" rel="noopener noreferrer"&gt;Pre-Router&lt;/a&gt;. Both are not very mature, but are promising. Pre-router particularly is quite clearly inspired by the Issue Tracker example.&lt;/p&gt;

&lt;p&gt;Since they are rather immature, I think right now the best idea is to use the Router in the Issue Tracker example and maintain it yourself. This isn't a great solution, but it seems to be the only way forward for now.&lt;/p&gt;

&lt;p&gt;Using the routing system from that example, our routes from before would instead look something like:&lt;/p&gt;

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

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;routes&lt;/span&gt; &lt;span class="o"&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;component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;JSResource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Root&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./Root&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
    &lt;span class="na"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="cm"&gt;/* ... */&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/profile/:id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;JSResource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Profile&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="o"&gt;=&amp;gt;&lt;/span&gt;
          &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./Profile&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="na"&gt;prepare&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;params&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="na"&gt;queryRef&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;loadQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="cm"&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;id&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="nx"&gt;id&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;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;Here we see the entrypoint pattern quite clearly. Each route is made up of a path to match, a component to fetch, and a prepare function that loads the appropriate query. The &lt;code&gt;JSResource&lt;/code&gt; helper here will cache the returned component to make sure it doesn't get lazily requested multiple times. While the &lt;code&gt;prepare&lt;/code&gt; function is used to trigger any preparation work for the route - in our case that's the &lt;code&gt;loadQuery&lt;/code&gt; function that Relay provides.&lt;/p&gt;

&lt;p&gt;What's particularly useful about this approach is how loading works with nested routes. Each of the nested routes will be matched all at once, and their prepare calls and components will be successively run. Once all the preparation work is done the rendering can start, and even if rendering blocks at a higher level the data has already started loading for the lower levels. Waterfall solved!&lt;/p&gt;

&lt;h1&gt;
  
  
  Wrapping up
&lt;/h1&gt;

&lt;p&gt;So that resolves our problem! But it does mean a lot of extra work for me, replacing our existing routing system with one that supports this new paradigm.&lt;/p&gt;

&lt;p&gt;I hope this has helped you understand the render-as-you-fetch pattern, and helped you see how it might be implemented in practice using relay. If you know of a better solution to the routing problem, I'd love to hear it in the comments. Understanding all of this has been a bit of a wild ride for me, and I'm still getting my head around each of the required components. What seems like a straightforward idea at first ends up being more than a little complex.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Edit:&lt;/strong&gt; Max Wheeler recommended on twitter that I check out &lt;a href="https://atlassian-labs.github.io/react-resource-router/" rel="noopener noreferrer"&gt;Atlassian's React Resource Router&lt;/a&gt;. It looks like a great solution for render-as-you-fetch for regular fetch requests, however its API isn't ideal for relay. It might work with some nice wrappers around its &lt;code&gt;useResource&lt;/code&gt; method. Worth checking out!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Edit2:&lt;/strong&gt; &lt;a class="mentioned-user" href="https://dev.to/gajus"&gt;@gajus&lt;/a&gt; has recommended using YARR (github.com/contra/yarr) which seems to be a great solution to this problem.&lt;/p&gt;

</description>
      <category>react</category>
      <category>relay</category>
      <category>javascript</category>
      <category>graphql</category>
    </item>
    <item>
      <title>Running Vagrant on an M1 Apple Silicon using Docker</title>
      <dc:creator>Ben Taylor</dc:creator>
      <pubDate>Thu, 22 Apr 2021 01:42:02 +0000</pubDate>
      <link>https://dev.to/taybenlor/running-vagrant-on-an-m1-apple-silicon-using-docker-3fh4</link>
      <guid>https://dev.to/taybenlor/running-vagrant-on-an-m1-apple-silicon-using-docker-3fh4</guid>
      <description>&lt;p&gt;I've recently started at a new job, and I'm fortunate enough to have a new M1 Macbook Air. I'd heard from the wider community that everything "just works" on the M1, even though it's not an Intel chip. "Even Docker" they said. "Wow" I thought, this will be seamless, incredible.&lt;/p&gt;

&lt;p&gt;So I sit there on my first day ready to get started. NPM installs everything fine, my python packages all install and both the backend and frontend are running, too easy! Then I hit an issue. This application requires a service which is run using Vagrant on Virtualbox.&lt;/p&gt;

&lt;p&gt;Virtualbox very much doesn't support Apple Silicon. It's unclear whether Virtualbox will ever support Apple Silicon. The preferred alternative is a product by VMWare which also doesn't (yet) support Apple Silicon. I'm in a bit of a pickle.&lt;/p&gt;

&lt;p&gt;But! Never fear! I can see that Vagrant also supports Docker. So over the next three days I dive into documentation, I frantically search the web for "vagrant docker M1" and "vagrant docker config" and "vagrant docker network issue" and "vagrant docker systemctl". After shaving at least three seperate yaks, and configuring a monstrosity I got it all running! Here's some lessons I learned.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclaimer: I am very much &lt;strong&gt;not&lt;/strong&gt; a linux / ops / devops person. My experience with linux is limited but there's not much help out there for this issue so I'm doing my best.&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Docker and Vagrant are frenemies
&lt;/h1&gt;

&lt;p&gt;Docker and Vagrant have very different philosophies about how you should run your development setup. Docker's ideal is a minimalist setup, with just enough to run the single process you need. If you want more processes, Docker wants you to create more containers. Vagrant prefers a maximalist style, install everything on the one virtual machine and get it all going together. &lt;/p&gt;

&lt;p&gt;To get Docker going you'll need a &lt;code&gt;Dockerfile&lt;/code&gt; this describes the setup needed to create the Docker container. For our Docker container to be friends with Vagrant we're going to configure it as if its a traditional linux machine. By default you'll need at least &lt;code&gt;sshd&lt;/code&gt; (which lets you SSH into the machine). I also needed &lt;code&gt;systemd&lt;/code&gt; (which runs services). This is very much not how you should do Docker normally.&lt;/p&gt;

&lt;p&gt;Here's the &lt;code&gt;Dockerfile&lt;/code&gt; I used:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Docker image to use with Vagrant&lt;/span&gt;
&lt;span class="c"&gt;# Aims to be as similar to normal Vagrant usage as possible&lt;/span&gt;
&lt;span class="c"&gt;# Adds Puppet, SSH daemon, Systemd&lt;/span&gt;
&lt;span class="c"&gt;# Adapted from https://github.com/BashtonLtd/docker-vagrant-images/blob/master/ubuntu1404/Dockerfile&lt;/span&gt;

FROM ubuntu:18.04
ENV container docker
RUN apt-get update &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get dist-upgrade &lt;span class="nt"&gt;-y&lt;/span&gt;

&lt;span class="c"&gt;# Install system dependencies, you may not need all of these&lt;/span&gt;
RUN apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="nt"&gt;--no-install-recommends&lt;/span&gt; ssh &lt;span class="nb"&gt;sudo &lt;/span&gt;libffi-dev systemd openssh-client

&lt;span class="c"&gt;# Needed to run systemd&lt;/span&gt;
&lt;span class="c"&gt;# VOLUME [ "/sys/fs/cgroup" ]&lt;/span&gt;
&lt;span class="c"&gt;# Doesn't appear to be necessary? See comments&lt;/span&gt;

RUN apt-get &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="nb"&gt;install &lt;/span&gt;puppet

&lt;span class="c"&gt;# Add vagrant user and key for SSH&lt;/span&gt;
RUN useradd &lt;span class="nt"&gt;--create-home&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; /bin/bash vagrant
RUN &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s1"&gt;'vagrant:vagrant'&lt;/span&gt; | chpasswd
RUN &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'vagrant ALL = NOPASSWD: ALL'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /etc/sudoers.d/vagrant
RUN &lt;span class="nb"&gt;chmod &lt;/span&gt;440 /etc/sudoers.d/vagrant
RUN &lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /home/vagrant/.ssh
RUN &lt;span class="nb"&gt;chmod &lt;/span&gt;700 /home/vagrant/.ssh
RUN &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA6NF8iallvQVp22WDkTkyrtvp9eWW6A8YVr+kz4TjGYe7gHzIw+niNltGEFHzD8+v1I2YJ6oXevct1YeS0o9HZyN1Q9qgCgzUFtdOKLv6IedplqoPkcmF0aYet2PkEDo3MlTBckFXPITAMzF8dJSIFo9D8HfdOV0IAdx4O7PtixWKn5y2hMNG0zQPyUecp4pzC6kivAIhyfHilFR61RGL+GPXQ2MWZWFYbAGjyiYJnAmCP3NOTd0jMZEnDkbUvxhMmBYSdETk1rRgm+R4LOzFUGaHqHDLKLX+FIPKcF96hrucXzcWyLbIbEgE98OHlnVYCzRdK8jlqm8tehUc9c9WhQ=="&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /home/vagrant/.ssh/authorized_keys
RUN &lt;span class="nb"&gt;chmod &lt;/span&gt;600 /home/vagrant/.ssh/authorized_keys
RUN &lt;span class="nb"&gt;chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; vagrant:vagrant /home/vagrant/.ssh
RUN &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s1"&gt;'s/Defaults.*requiretty/#&amp;amp;/'&lt;/span&gt; /etc/sudoers
RUN &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s1"&gt;'s/\(UsePAM \)yes/\1 no/'&lt;/span&gt; /etc/ssh/sshd_config

&lt;span class="c"&gt;# Start SSH&lt;/span&gt;
RUN &lt;span class="nb"&gt;mkdir&lt;/span&gt; /var/run/sshd
EXPOSE 22
RUN /usr/sbin/sshd

&lt;span class="c"&gt;# Start Systemd (systemctl)&lt;/span&gt;
CMD &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"/lib/systemd/systemd"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you don't need &lt;code&gt;systemd&lt;/code&gt; in your setup then you can remove that and change the &lt;code&gt;RUN&lt;/code&gt; of &lt;code&gt;sshd&lt;/code&gt; to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CMD ["/usr/sbin/sshd", "-D"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All of this sets up a Docker container which doesn't work like a regular Docker container. It runs more like a virtual machine. This means it will be difficult to manage using the normal Docker commands. Once you take it down it will also be difficult to get up again. Best to control it with Vagrant. I've had to delete the container and re-create it with Vagrant many times.&lt;/p&gt;

&lt;h1&gt;
  
  
  Vagrant is pretty easy to configure
&lt;/h1&gt;

&lt;p&gt;To get Vagrant running using your Dockerfile you just need to add a little bit of configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Vagrant&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;

&lt;span class="c1"&gt;# ... your existing config&lt;/span&gt;

  &lt;span class="c1"&gt;# Custom configuration for docker&lt;/span&gt;
  &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;vm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"docker"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;override&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="c1"&gt;# docker doesnt use boxes&lt;/span&gt;
    &lt;span class="n"&gt;override&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;vm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;box&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;

    &lt;span class="c1"&gt;# this is where your Dockerfile lives&lt;/span&gt;
    &lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build_dir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"."&lt;/span&gt;

    &lt;span class="c1"&gt;# Make sure it sets up ssh with the Dockerfile&lt;/span&gt;
    &lt;span class="c1"&gt;# Vagrant is pretty dependent on ssh&lt;/span&gt;
    &lt;span class="n"&gt;override&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ssh&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insert_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
    &lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has_ssh&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;

    &lt;span class="c1"&gt;# Configure Docker to allow access to more resources&lt;/span&gt;
    &lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;privileged&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# ...&lt;/span&gt;

&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You may need to override some other settings here. The &lt;code&gt;override&lt;/code&gt; variable allows you to change top-level settings, while the &lt;code&gt;docker&lt;/code&gt; variable allows you to setup docker-specific stuff. Have a read of the &lt;a href="https://www.vagrantup.com/docs/providers/docker"&gt;vagrant docker provider&lt;/a&gt; and &lt;a href="https://www.vagrantup.com/docs/provisioning/docker"&gt;vagrant docker provisioning&lt;/a&gt; documentation.&lt;/p&gt;

&lt;p&gt;Once you've set this up, you can run your Vagrant machine with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ vagrant up --provider=docker
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And if you need to do extra things inside the machine, you can ssh in with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ vagrant ssh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should have &lt;code&gt;sudo&lt;/code&gt; access as well if you need it.&lt;/p&gt;

&lt;h1&gt;
  
  
  Vagrant doesn't like to run multiple boxes
&lt;/h1&gt;

&lt;p&gt;When I first ran Vagrant, before I realised Virtualbox wouldn't work it started and then failed. Then when I tried to run vagrant with the Docker provider it gave me errors about not having multiple boxes. I also couldn't delete my box because virtualbox wasn't running.&lt;/p&gt;

&lt;p&gt;You can just delete the files in &lt;code&gt;.vagrant&lt;/code&gt; to resolve that. It seems like Vagrant doesn't have a good way of resolving this itself.&lt;/p&gt;

&lt;h1&gt;
  
  
  You can re-provision Vagrant
&lt;/h1&gt;

&lt;p&gt;Sometimes I found my setup would break, in my case it wasn't too hard to just run the Vagrant provisioning script again.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ vagrant up --provision
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once that was complete some of my issues got resolved. It's not a good idea to blindly do this, but in my case I'm just trying to get it running.&lt;/p&gt;

&lt;h1&gt;
  
  
  Docker's public network bridge feature is broken
&lt;/h1&gt;

&lt;p&gt;It doesn't seem to work on Mac! So if your Vagrant config has something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;config.vm.network "public_network"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Try to see if you can replace it with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;config.vm.network "private_network", type: "dhcp"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In my case this substitution was sufficient, but you may have other requirements.&lt;/p&gt;

&lt;h1&gt;
  
  
  Your linux binaries need to be arm64
&lt;/h1&gt;

&lt;p&gt;I ran into some issues because our provisioning scripts assumed that the architecture of the system would be &lt;code&gt;amd64&lt;/code&gt; (Intel and AMD 64-bit processors). It was a pretty safe bet that the CPU was &lt;code&gt;amd64&lt;/code&gt; up until Apple introduced their M1 chip! &lt;/p&gt;

&lt;p&gt;Running linux on the M1 chip uses the &lt;code&gt;arm64&lt;/code&gt; architecture instead. As a handy shortcut you can get the architecture of the current machine in linux with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ dpkg --print-architecture
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I used this in shell scripts by setting a variable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;readonly &lt;/span&gt;&lt;span class="nv"&gt;ARCH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;dpkg &lt;span class="nt"&gt;--print-architecture&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then substituting that in when binaries were downloaded and installed, usually that was relatively simple, e.g.:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;wget http://somedomain.com/some_binary_linux_amd64.zip
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Became&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;wget "http://somedomain.com/some_binary_linux_${ARCH}.zip"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  I learned a lot - but I still don't know much
&lt;/h1&gt;

&lt;p&gt;I learned a lot about Docker (and how not to do things) Vagrant (and how to hack it) and some various linux tricks trying to get this all working. However I'm very much not a linux, Docker or Vagrant person, I'm just trying to make things work. So if I've written anything here horribly wrong, or extremely misguided, please throw down a comment.&lt;/p&gt;

&lt;p&gt;If this helped you I'd love to hear it! I got stuck googling around for how to do this, and I imagine there are other people out there in the same boat. &lt;/p&gt;

</description>
      <category>docker</category>
      <category>vagrant</category>
      <category>linux</category>
      <category>arm64</category>
    </item>
    <item>
      <title>Safer Authentication with TypeScript</title>
      <dc:creator>Ben Taylor</dc:creator>
      <pubDate>Tue, 17 Nov 2020 10:30:45 +0000</pubDate>
      <link>https://dev.to/taybenlor/safer-authentication-with-typescript-2d8p</link>
      <guid>https://dev.to/taybenlor/safer-authentication-with-typescript-2d8p</guid>
      <description>&lt;p&gt;I’ve been going through the &lt;a href="https://developers.cloudflare.com/workers/tutorials/authorize-users-with-auth0"&gt;Auth0 tutorial on Cloudflare Workers&lt;/a&gt; and integrating it with my own codebase. One of the first things I noticed was how loose the use of types was in the example code. This isn’t necessarily bad, it’s a tutorial, it’s not written in TypeScript, and the audience is just wanting to get something to work. But to me authentication is mission critical, which means I want to be confident about my types.&lt;/p&gt;

&lt;p&gt;Let’s look at one of the first functions they have you implement:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tmaty3WB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/5wohkysscz3zm8tdncz1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tmaty3WB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/5wohkysscz3zm8tdncz1.png" alt="carbon-21"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This piece of code is pretty neat. One thing I like about it is that it returns a tuple. The first thing in the tuple is whether it was authorized or not, and the second thing is either the authorization information or the redirect information. What this means is that we can guard against the first item in the tuple and then use the second item.&lt;/p&gt;

&lt;p&gt;But, unfortunately, TypeScript gets something different when it infers types for this function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;boolean&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;authorization&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&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;boolean&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;redirectUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;})[]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Typescript thinks that this is an Array of either (&lt;code&gt;boolean&lt;/code&gt; or an &lt;code&gt;authorization&lt;/code&gt; object) OR it’s an Array of either (&lt;code&gt;boolean&lt;/code&gt; or a &lt;code&gt;redirect&lt;/code&gt; object). That’s messy! It means we can’t be confident that the implementation is correct. It would be valid to write it with the boolean flipped around, or with the object on either side. It also means we can’t use the types of this function to be confident that we’re only using the return type in the right way. For example to use the &lt;code&gt;authorization&lt;/code&gt; object we will have to use a type assertion on it:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Js_5569k--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/5gcy4dcxn2i2lxklf4t9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Js_5569k--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/5gcy4dcxn2i2lxklf4t9.png" alt="carbon-23"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We have to do this because the type of data is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;boolean&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;authorization&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;redirectUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If someone made a mistake with the if statement here the type assertion would prevent any type errors from being raised.&lt;/p&gt;

&lt;p&gt;We can improve the situation by encoding information about the sorts of return values the &lt;code&gt;authorize&lt;/code&gt; function can have within the return type.&lt;/p&gt;




&lt;p&gt;The full text of this post is available on my Substack, where I go into how we can use the right types to make this safer.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://taybenlor.substack.com/p/safer-authentication-with-typescript"&gt;Safer Authentication with Typescript&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thanks for reading!&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>serverless</category>
      <category>security</category>
    </item>
    <item>
      <title>BlurHash as a service with Cloudflare Workers</title>
      <dc:creator>Ben Taylor</dc:creator>
      <pubDate>Fri, 13 Nov 2020 23:11:42 +0000</pubDate>
      <link>https://dev.to/taybenlor/blurhash-as-a-service-with-cloudflare-workers-l8k</link>
      <guid>https://dev.to/taybenlor/blurhash-as-a-service-with-cloudflare-workers-l8k</guid>
      <description>&lt;p&gt;Last week I came across &lt;a href="https://blurha.sh/" rel="noopener noreferrer"&gt;BlurHash&lt;/a&gt; on twitter. It’s an interesting tool for dealing with image loading issues. Basically it allows you to show a blurred version of an image while the real image is loading over the network. So you can show a kind of preview while it’s loading.&lt;/p&gt;

&lt;p&gt;This is a pretty useful technique in terms of UX and perceived performance. It obviously looks a lot nicer, since you’ve got pops of colour and a bit of variety, but the more important part is that users can actively see that the website is loading.&lt;/p&gt;

&lt;p&gt;When a user sees just a blank screen or a blank area they can’t get a sense that the page is loading. Maybe it’s broken, or stuck. If you introduce intermediate loading steps it feels to the user like there is an active loading process going on. This is the same concept behind skeleton screens, where a skeleton of the user interface is displayed while the page is loading.&lt;/p&gt;

&lt;p&gt;The way BlurHash helps you out is through tooling, not through some specific component implementation. BlurHash gives you two main functions: an encode function and a decode function. The encode function turns the image into a short string of characters (similar to a hash), while the decode function turns a string into a blurred image. Here’s the diagram from their website:&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%2Ff7xnrhl34esnds5b6uoj.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%2Ff7xnrhl34esnds5b6uoj.png" alt="https---bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com-public-images-51cad030-5581-45e2-aeeb-747f4196876a_1508x434"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What’s really cool about this is that you can generate the BlurHash string server side when you’re processing images, and then save it along with your model. Then on the client side you can render the blurred image while you’re waiting for the full image to load. Since the blurred image is just a string, you don’t need any sort of binary storage or transfer - just throw it in your JSON or your HTML and get on with it.&lt;/p&gt;

&lt;p&gt;The situation this doesn’t work for is when you don’t have full control over server-side processing of your images. You might be consuming images from somebody else’s API, or you outsource your image uploads. I’ve been messing around with Cloudflare Workers a lot recently and it struck me that it would be pretty cool to have a worker do this processing for you.&lt;/p&gt;




&lt;p&gt;The full text of this post is available on my Substack, where I go deep into how I implemented BlurHash as a service using Cloudflare Workers.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://taybenlor.substack.com/p/blurhash-as-a-service-with-cloudflare" rel="noopener noreferrer"&gt;BlurHash as a Service using Cloudflare Workers&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thanks for reading!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>serverless</category>
    </item>
    <item>
      <title>Neater Icons with Web Components</title>
      <dc:creator>Ben Taylor</dc:creator>
      <pubDate>Sun, 09 Aug 2020 11:50:51 +0000</pubDate>
      <link>https://dev.to/taybenlor/neater-icons-with-web-components-5af9</link>
      <guid>https://dev.to/taybenlor/neater-icons-with-web-components-5af9</guid>
      <description>&lt;p&gt;Over the years we've seen steady changes in best practices for rendering icons. Icon fonts remain easy to use, SVGs render well on high definition screens and well, for some reason Facebook seems to still use a png sprite sheet?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--FCUs9atP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/b3fvsvgupj5ohpjnx3oq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--FCUs9atP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/b3fvsvgupj5ohpjnx3oq.png" alt="Facebook uses PNG sprite sheets for their icons"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;(I'm sure they have a good reason, they're smart people)&lt;/p&gt;

&lt;p&gt;However the actual use of these different techniques still feels... imperfect. If you're using icon fonts you'll be writing some HTML 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;button&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"icon-plus"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/span&amp;gt;&lt;/span&gt; Add
&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or you might inject the icon from your CSS with 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;button&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"add"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Add&lt;span class="nt"&gt;&amp;lt;/button&amp;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 css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.add&lt;/span&gt;&lt;span class="nd"&gt;::before&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="s2"&gt;'iconfont'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;'\addicon'&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're using SVG you could just drop that SVG straight into the DOM (not a bad idea).&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;button&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;svg&lt;/span&gt; &lt;span class="na"&gt;viewBox=&lt;/span&gt;&lt;span class="s"&gt;"0 0 16 16"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;path&lt;/span&gt; &lt;span class="na"&gt;d=&lt;/span&gt;&lt;span class="s"&gt;"big series of numbers"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/path&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;path&lt;/span&gt; &lt;span class="na"&gt;d=&lt;/span&gt;&lt;span class="s"&gt;"maybe another path"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/path&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/svg&amp;gt;&lt;/span&gt;
  Add
&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But more likely you're using an SVG sprite sheet and so your code looks a bit tidier, 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;button&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"btn"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;svg&lt;/span&gt; &lt;span class="na"&gt;viewBox=&lt;/span&gt;&lt;span class="s"&gt;"0 0 16 16"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;use&lt;/span&gt; &lt;span class="na"&gt;xlink:href=&lt;/span&gt;&lt;span class="s"&gt;"#icon-add"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/use&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/svg&amp;gt;&lt;/span&gt;
  Add
&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then if you're using a PNG sprite sheet... uhh lets not go into it. &lt;/p&gt;

&lt;p&gt;My main point is that you end up mixing the implementation of your icon rendering system, with the markup and CSS you're writing to implement your webpage. This isn't necessarily bad, but abstraction can create neat boundaries. If this was JavaScript we would have long ago written a helper function like &lt;code&gt;icon('name')&lt;/code&gt; that returns us an icon with that name.&lt;/p&gt;

&lt;p&gt;On the web we have a great new friend for abstraction: Web Components.&lt;/p&gt;

&lt;p&gt;With Web Components we could write this code example as:&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;button&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;my-icon&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"plus"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/my-icon&amp;gt;&lt;/span&gt;
  Add
&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This lets us &lt;em&gt;hide&lt;/em&gt; the implementation details of our icon rendering system and allows for a short and semantic syntax in its place. If someone unfamiliar with your codebase had a read of that, they'd think "Hey this thing renders an icon".&lt;/p&gt;

&lt;p&gt;This is a great way to use Web Components - to hide the implementation details of something you do all the time. You don't have to build a huge design system to reap these rewards. Just write a bit of JavaScript.&lt;/p&gt;

&lt;p&gt;Speaking of writing JavaScript - it's probably time I showed you how to implement this element. I'm going to show it here without any frameworks or magic, but feel free to &lt;code&gt;npm install&lt;/code&gt; your way to a solution that works for you. I'll be showing you the SVG version - but you could use a similar strategy with icon fonts.&lt;/p&gt;

&lt;h1&gt;
  
  
  Implementing our Icon
&lt;/h1&gt;

&lt;p&gt;First off you'll need a template element. This will represent the stuff that lives "inside" the &lt;code&gt;my-icon&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;template&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"my-icon"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;svg&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;use&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"use"&lt;/span&gt; &lt;span class="na"&gt;xlink:href=&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/use&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/svg&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That wasn't so scary. Here we're defining a &lt;code&gt;template&lt;/code&gt; element which can be used inside our custom element. I've used an &lt;code&gt;id&lt;/code&gt; for the &lt;code&gt;use&lt;/code&gt; element so we can set its &lt;code&gt;xlink:href&lt;/code&gt; later. Since the &lt;code&gt;id&lt;/code&gt; is inside a &lt;code&gt;template&lt;/code&gt; it won't conflict with the rest of the document.&lt;/p&gt;

&lt;p&gt;Then in JavaScript we create a custom element.&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="c1"&gt;// URL to your SVG&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;baseURL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/sheet.svg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;MyIconElement&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;HTMLElement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// This tells the browser we want to be told&lt;/span&gt;
  &lt;span class="c1"&gt;// if the `name` attribute changes.&lt;/span&gt;
  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nx"&gt;observedAttributes&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;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;name&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="kd"&gt;constructor&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="c1"&gt;// Here we create the DOM elements from the template&lt;/span&gt;
    &lt;span class="c1"&gt;// and put them in the ~~spooky~~ shadow DOM.&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;attachShadow&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;open&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;template&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;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my-icon&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;clone&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cloneNode&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shadowRoot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;clone&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Lets also grab a reference to that use element&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;useEl&lt;/span&gt; &lt;span class="o"&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;shadowRoot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;use&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="c1"&gt;// This is called whenever an attribute in the&lt;/span&gt;
  &lt;span class="c1"&gt;// observed attributes changes. It means you can&lt;/span&gt;
  &lt;span class="c1"&gt;// change `name` and it will update.&lt;/span&gt;
  &lt;span class="nx"&gt;attributeChangedCallback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;oldValue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;newValue&lt;/span&gt;&lt;span class="p"&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;useEl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;xlink:href&lt;/span&gt;&lt;span class="dl"&gt;'&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;baseURL&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;#icon-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;newValue&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="p"&gt;}&lt;/span&gt;


&lt;span class="c1"&gt;// Finally lets define this custom element&lt;/span&gt;
&lt;span class="nx"&gt;customElements&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;define&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my-icon&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;MyIconElement&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And we're done! &lt;/p&gt;

&lt;p&gt;We can now write:&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;button&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;my-icon&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"plus"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/my-icon&amp;gt;&lt;/span&gt;
  Add
&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To have it render like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--LaKdRqGy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/bs6p5flyt3pzl8f3yfc5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--LaKdRqGy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/bs6p5flyt3pzl8f3yfc5.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Understanding that JavaScript
&lt;/h1&gt;

&lt;p&gt;There's a lot going on in this JavaScript so let's talk about it a bit. If you haven't seen the ~spooky~ shadow DOM before it can seem a bit scary. A better name for it would be a "private" DOM. It's a small little private space for you to set styles, create elements, and do weird things without impacting the "public" DOM. Or, if you like, a dark shadow realm where you can banish horrors of CSS and HTML - and nobody will know.&lt;/p&gt;

&lt;p&gt;A big benefit of using the shadow DOM is that we can pollute our namespace with &lt;code&gt;id&lt;/code&gt; all we like, it won't impact anyone else. If you look at the page in the inspector, the template &lt;code&gt;SVG&lt;/code&gt; will all be hidden. You can still view it - but it's not there by default.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xe0l9bBN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/ohn33lgcrfguojlpqkym.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xe0l9bBN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/ohn33lgcrfguojlpqkym.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Then if we expand the shadow root.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vYWJ_m_A--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/opsa2cp0pj96fjsin7ic.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vYWJ_m_A--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/opsa2cp0pj96fjsin7ic.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Another trick here is in the &lt;code&gt;attributeChangedCallback&lt;/code&gt; function. Here is the real "work" of this element. It turns the icon name that a human would use, into a &lt;code&gt;xlink:href&lt;/code&gt; friendly value that we then set on the &lt;code&gt;use&lt;/code&gt; element.&lt;/p&gt;

&lt;p&gt;If we changed the &lt;code&gt;name&lt;/code&gt; attribute to be &lt;code&gt;"cross"&lt;/code&gt; as an argument then the HTML would end up looking 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;svg&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;use&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"use"&lt;/span&gt; &lt;span class="na"&gt;xlink:href=&lt;/span&gt;&lt;span class="s"&gt;"sheet.svg/#icon-cross"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/use&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/svg&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Basically the same as before!&lt;/p&gt;

&lt;p&gt;You can see a demo of this, with two different icons in &lt;a href="https://taybenlor.com/dev-to/webcomponent-icons/demo.html"&gt;this example&lt;/a&gt;. I've also made it a &lt;a href="https://codepen.io/taybenlor/pen/WNwvoje"&gt;codepen here&lt;/a&gt; but the SVG won't load due to cross-site protections.&lt;/p&gt;

&lt;p&gt;If instead you wanted to use icon fonts, then you could change the &lt;code&gt;attributedChangedCallback&lt;/code&gt; to set the properties you need for your icon font.&lt;/p&gt;

&lt;h1&gt;
  
  
  Making it useful (extensions)
&lt;/h1&gt;

&lt;p&gt;So at this point you're basically done, you have icons on the page - what more could you want? Well, probably a lot more. You'll want to set up some default CSS, you may want to add some accessibility defaults. Maybe there's some helpers specific to your application you could add.&lt;/p&gt;

&lt;p&gt;There's plenty of guides out there about how best to do SVG icons, so I'll leave the details to them. However here's some quick tricks to make things work well. We'll revisit our template from earlier:&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;template&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"my-icon"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- Web components can have a scoped style tag, this only impacts elements inside the shadow DOM. --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;/* :host indicates the "host" element of the
   * shadow DOM. So our custom my-icon element.
   */&lt;/span&gt;
  &lt;span class="nd"&gt;:host&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="nb"&gt;inline&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c"&gt;/* Because this is scoped, we can use very
   * simple selectors.
   */&lt;/span&gt;
  &lt;span class="nt"&gt;svg&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;1em&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;1em&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="py"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;currentColor&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;svg&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;use&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"use"&lt;/span&gt; &lt;span class="na"&gt;xlink:href=&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/use&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/svg&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here I've added a &lt;code&gt;style&lt;/code&gt; element into the template. In this context &lt;code&gt;style&lt;/code&gt; elements are not considered a code smell - in fact they're necessary. This &lt;code&gt;style&lt;/code&gt; element is entirely scoped to the Shadow DOM and lets us be "lazy" in our CSS. This scoped approach is one of the greatest abstraction tricks Web Components can give us, when you're writing CSS here you don't have to think "how will this impact my other elements and classes" you only need to keep the one scope in your head.&lt;/p&gt;

&lt;p&gt;The downside and upside of using the Shadow DOM is that regular CSS can't "penetrate" it. You start off with everything at browser defaults - so if you want any styles you'll have to set them here. Some things will inherit through the boundary, mostly text styles, but you can't use any classes you've set up outside the Shadow DOM.&lt;/p&gt;

&lt;p&gt;In my style here I've set the &lt;code&gt;:host&lt;/code&gt; to be &lt;code&gt;display: inline&lt;/code&gt; - this is mostly to indicate "this is an inline element". Next I've added some simple styles to the &lt;code&gt;svg&lt;/code&gt; element. These styles ensure it resizes as the &lt;code&gt;font-size&lt;/code&gt; increases, and changes its &lt;code&gt;fill&lt;/code&gt; to whatever &lt;code&gt;color&lt;/code&gt; is set.&lt;/p&gt;

&lt;p&gt;Now if you use these icons as part of your website, in buttons, links etc - they fit in. They resize and change colour based on their context. This is extremely smooth, and gives you many of the benefits of icon fonts but with SVG, a much easier to work with format.&lt;/p&gt;

&lt;h1&gt;
  
  
  Final Note
&lt;/h1&gt;

&lt;p&gt;I'm new to publishing on dev.to if you like this and you'd like to see more things by me just shoot through a comment or message!&lt;/p&gt;

&lt;h2&gt;
  
  
  Further Reading
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://css-tricks.com/svg-sprites-use-better-icon-fonts/"&gt;Icon System with SVG Sprites on CSS Tricks&lt;/a&gt; - great introduction to using SVG Sprites.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.24a11y.com/2018/accessible-svg-icons-with-inline-sprites/"&gt;Accessible SVG Icons with Inline Sprites on 24a11y&lt;/a&gt; - fantastic article about making SVG icons accessible.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://css-tricks.com/an-introduction-to-web-components/"&gt;An Introduction to Web Components on CSS Tricks&lt;/a&gt; - good place to start learning about Web Components.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/Web_Components"&gt;Web Components on MDN&lt;/a&gt; - reference for Web Component information.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>html</category>
    </item>
  </channel>
</rss>
