<?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: iO</title>
    <description>The latest articles on DEV Community by iO (@iodigital-com).</description>
    <link>https://dev.to/iodigital-com</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F4794%2F6873d721-e3b5-4f9a-ac2d-06130dc17c21.jpg</url>
      <title>DEV Community: iO</title>
      <link>https://dev.to/iodigital-com</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/iodigital-com"/>
    <language>en</language>
    <item>
      <title>Storyblok live editing with Next.js App Router and React Server Components</title>
      <dc:creator>iO</dc:creator>
      <pubDate>Tue, 04 Jun 2024 00:00:00 +0000</pubDate>
      <link>https://dev.to/iodigital-com/storyblok-live-editing-with-nextjs-app-router-and-react-server-components-cdc</link>
      <guid>https://dev.to/iodigital-com/storyblok-live-editing-with-nextjs-app-router-and-react-server-components-cdc</guid>
      <description>&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;When working with Storyblok there are &lt;strong&gt;two&lt;/strong&gt; ways to set up your Next.js app if you use the app router. The first way is to wrap your entire app in a provider which then takes care of updating the app when you edit anything in the preview environment of Storyblok, thus giving you live editing. Live editing is a cool feature because an editor can directly see the changes they made without constantly saving the page.&lt;/p&gt;

&lt;p&gt;The second approach keeps everything server-side. This is nice because we can then leverage the full power of server components. But this approach comes with a big limitation... you lose the live editing support and the editor can only see their changes after they've hit the save button.&lt;/p&gt;

&lt;p&gt;Or is there a way...&lt;/p&gt;

&lt;p&gt;I found this &lt;a href="https://gist.github.com/Ventanas95Dev/2683f50accac68369ef6bdc3fc62e392" rel="noopener noreferrer"&gt;Gist&lt;/a&gt; from someone who stumbled across the same issue and solved it with a clever solution. I expanded on their solution and replaced their database (&lt;code&gt;@vercel/kv&lt;/code&gt;) with something free and local. Let's dive in on how I did it!&lt;/p&gt;

&lt;h2&gt;
  
  
  The solution
&lt;/h2&gt;

&lt;p&gt;When you are in the live editing environment of Storyblok, they add a class to the browser's &lt;code&gt;window&lt;/code&gt; object called &lt;code&gt;StoryblokBridge&lt;/code&gt;. This bridge allows you to listen to live editing events happening with &lt;code&gt;on()&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;const&lt;/span&gt; &lt;span class="nx"&gt;sbBridge&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;StoryblokBridge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nx"&gt;sbBridge&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;input&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;published&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;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="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;story&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;story&lt;/span&gt;
  &lt;span class="c1"&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 event returned contains the updated story with the live editing data the user entered. Awesome! We can use this live story and save it somewhere, then revalidate the page using Next.js' &lt;code&gt;revalidatePath()&lt;/code&gt; API. Let's see how:&lt;/p&gt;

&lt;p&gt;Let's first tackle the "save it somewhere" part of the solution. I used &lt;a href="https://github.com/node-cache/node-cache" rel="noopener noreferrer"&gt;node-cache&lt;/a&gt; for this.&lt;/p&gt;

&lt;p&gt;Create an &lt;code&gt;instrumention.ts&lt;/code&gt; file in the root or src folder of your project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;NodeCache&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node-cache&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;register&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;NodeCache&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&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;node-cache&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;stdTTL&lt;/span&gt;&lt;span class="p"&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="nb"&gt;global&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;storyblokCache&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;NodeCache&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;config&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;Add this to &lt;code&gt;next.config&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="nx"&gt;experimental&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;instrumentationHook&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="p"&gt;},&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;This setup makes sure the cache won't be reset with each request, only on server startup.&lt;/p&gt;

&lt;p&gt;Next, let's take a look at how we get the live editing data from Storyblok and save it in this cache. For this we first need to create a custom Storyblok bridge:&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;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;registerStoryblokBridge&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;onInput&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isServer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;undefined&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isBridgeLoaded&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;isServer&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;storyblokRegisterEvent&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;undefined&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

  &lt;span class="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;isBridgeLoaded&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="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;storyblokRegisterEvent&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sbBridge&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;StoryblokBridge&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nx"&gt;sbBridge&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;input&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&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;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;event&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;story&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;

      &lt;span class="nf"&gt;onInput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;story&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;This function listens to live editing events, as we found out above, and makes a callback with the story containing the latest live editing data.&lt;/p&gt;

&lt;p&gt;We then use this function in a client component&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;use client&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;previewUpdateAction&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@/actions/previewUpdateAction&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;registerStoryblokBridge&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@/utils/storyblok&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;startTransition&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;StoryblokPreviewSyncer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;pathToRevalidate&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handleInput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;story&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;startTransition&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
      &lt;span class="nf"&gt;previewUpdateAction&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="nx"&gt;story&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;pathToRevalidate&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="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;registerStoryblokBridge&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;onInput&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;handleInput&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="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;This client component makes sure the window event is fired with &lt;code&gt;useEffect&lt;/code&gt;. The &lt;code&gt;handleInput&lt;/code&gt; function uses React's &lt;code&gt;startTransition&lt;/code&gt; to call a Next.js Server Action containing the latest data. Let's find out what this server action looks like.&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;use server&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;revalidatePath&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;next/cache&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ISbStoryData&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@storyblok/react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;previewUpdateAction&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;story&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;pathToRevalidate&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;story&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;No story provided&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;global&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;storyblokCache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;story&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;story&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;pathToRevalidate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;revalidatePath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pathToRevalidate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&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 function takes the story with the live editing data and saves it to our cache. It then calls &lt;code&gt;revalidatePath&lt;/code&gt; to make sure Next.js knows it needs to update the page.&lt;/p&gt;

&lt;p&gt;We now only need one more piece to solve the puzzle and that is the function that takes care of fetching the story:&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;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getStoryblokData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;storyblokApi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getStoryblokApi&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

  &lt;span class="k"&gt;try&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;story&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;global&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;storyblokCache&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;slug&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;story&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;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;storyblokApi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`cdn/stories/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;slug&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="na"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;draft&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;

      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;story&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;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;story&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;This function first tries to fetch the story from the database (this would be the story with the latest live editing). If this fails it falls back to the Storyblok API.&lt;/p&gt;

&lt;p&gt;We can then use this function on a page together with our &lt;code&gt;&amp;lt;StoryblokPreviewSync /&amp;gt;&lt;/code&gt; component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;StoryblokComponent&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@storyblok/react/rsc&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;StoryblokPreviewSyncer&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@/components/StoryblokPreviewSync&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getStoryblokData&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@/utils/storyblok&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Home&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;story&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetchData&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;StoryblokPreviewSyncer&lt;/span&gt; &lt;span class="nx"&gt;pathToRevalidate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;StoryblokComponent&lt;/span&gt; &lt;span class="nx"&gt;blok&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;story&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="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/main&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;fetchData&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="nf"&gt;getStoryblokData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;home&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;There you go! Now you should have live editing support, with the full power of React Server Components!&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>storyblok</category>
    </item>
    <item>
      <title>Deferrable Views, skeletons and named chunks in Angular</title>
      <dc:creator>iO</dc:creator>
      <pubDate>Fri, 24 Nov 2023 00:00:00 +0000</pubDate>
      <link>https://dev.to/iodigital-com/deferrable-views-skeletons-and-named-chunks-in-angular-3jcl</link>
      <guid>https://dev.to/iodigital-com/deferrable-views-skeletons-and-named-chunks-in-angular-3jcl</guid>
      <description>&lt;h2&gt;
  
  
  Deferrable Views
&lt;/h2&gt;

&lt;p&gt;To be fair lazy-loading is not a new thing in Angular. It has been provided for modules, based on the routes a long time ago. This feature still exists. But since the moment the Angular team presented standalone components that don't require modules at all, it looks like the next logical step is to provide lazy-loading for them as well. So they did and called it Deferrable Views.&lt;/p&gt;

&lt;p&gt;Another feature of Deferrable Views is the opportunity to show a placeholder as the component is loaded. To avoid the fast flickering of the placeholder in the case that the deferred component was fetched quickly, it's possible to provide a &lt;code&gt;minimum&lt;/code&gt; timer.&lt;/p&gt;

&lt;p&gt;This is how to implement that using standalone wrapper component template, &lt;code&gt;@defer&lt;/code&gt; block and &lt;code&gt;@placeholder&lt;/code&gt; block:&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="nd"&gt;defer&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;huge&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;placeholder&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;minimum&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="nx"&gt;s&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;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Loading&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Skeletons
&lt;/h2&gt;

&lt;p&gt;The next thing that could improve user experience is the skeleton shown as the placeholder. Instead of building it from scratch, I would recommend using the package called &lt;a href="https://github.com/willmendesneto/ngx-skeleton-loader"&gt;NGX Skeleton loader&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This package is easy to configure and it supports ARIA attributes to keep it accessible.&lt;/p&gt;

&lt;p&gt;To prevent the content of the screen from moving as the placeholder would be replaced with the component (so called layout shift), we need to pass the height of the skeleton. That height needs to be equal to the component height.&lt;/p&gt;

&lt;p&gt;Suppose that component height is 200px. This is how you can implement skeleton with the same height:&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="nd"&gt;defer&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;huge&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;placeholder&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;minimum&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="nx"&gt;s&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;lt;&lt;/span&gt;&lt;span class="nx"&gt;ngx&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;skeleton&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;loader&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;{height: '200px'}&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&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;I found that one more step is required to adjust default styling for NGX Skeleton loader. It contains redundant margin and it's displayed as inline-block. Let's fix that in order to have consistent height of all skeletons in the app:&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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;NgModule&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;BrowserModule&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/platform-browser&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;NgxSkeletonLoaderModule&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ngx-skeleton-loader&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;AppComponent&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./app.component&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;WrapperComponent&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./wrapper/wrapper.component&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;HugeComponent&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./huge/huge.component&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="nd"&gt;NgModule&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;declarations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;AppComponent&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nx"&gt;BrowserModule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;NgxSkeletonLoaderModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forRoot&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;extendsFromRoot&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="na"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;block&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="nx"&gt;WrapperComponent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;HugeComponent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;bootstrap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;AppComponent&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AppModule&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Named chunks
&lt;/h2&gt;

&lt;p&gt;The last thing that goes well together with Deferrable Views is named chunks. By default, the lazy-loaded modules and components are shown in dev tools as &lt;code&gt;.js&lt;/code&gt; files with non-human-readable names. For example &lt;code&gt;chunk-IVE23K2G.js&lt;/code&gt; is a terrible name for debugging purposes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--S27hMJIs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://techhub.iodigital.com/articles/deferrable-views/deferrable-views-default-js-chunks.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--S27hMJIs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://techhub.iodigital.com/articles/deferrable-views/deferrable-views-default-js-chunks.gif" alt="Example of default JS chunk" width="660" height="544"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's fix that by adding &lt;code&gt;"namedChunks": true&lt;/code&gt; into &lt;code&gt;angular.json&lt;/code&gt; file:&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;content&lt;/span&gt; &lt;span class="nx"&gt;omitted&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;projects&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;angular-defer-test&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;architect&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;build&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;configurations&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;development&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;optimization&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;extractLicenses&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sourceMap&lt;/span&gt;&lt;span class="dl"&gt;"&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;namedChunks&lt;/span&gt;&lt;span class="dl"&gt;"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After that chunk names would look like that: &lt;code&gt;huge.component-HRZM3FTC.js&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Final result
&lt;/h2&gt;

&lt;p&gt;Here's how final result looks like:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--KKlt820O--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://techhub.iodigital.com/articles/deferrable-views/deferrable-views-final-example.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KKlt820O--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://techhub.iodigital.com/articles/deferrable-views/deferrable-views-final-example.gif" alt="Final example of Deferrable Views" width="800" height="658"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And here's the code if you would like to play with it:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://stackblitz.com/%7E/github.com/GanjaGanja/angular-defer-test"&gt;https://stackblitz.com/~/github.com/GanjaGanja/angular-defer-test&lt;/a&gt;&lt;/p&gt;

</description>
      <category>frontend</category>
      <category>angular</category>
    </item>
    <item>
      <title>Reducing latency in AI Speech Synthesis</title>
      <dc:creator>iO</dc:creator>
      <pubDate>Thu, 16 Nov 2023 00:00:00 +0000</pubDate>
      <link>https://dev.to/iodigital-com/reducing-latency-in-ai-speech-synthesis-2b92</link>
      <guid>https://dev.to/iodigital-com/reducing-latency-in-ai-speech-synthesis-2b92</guid>
      <description>&lt;p&gt;In &lt;a href="https://techhub.iodigital.com/articles/interacting-with-chat-gpt-through-voice-ui-on-the-web"&gt;my previous article&lt;/a&gt;, I showed how you can interact with ChatGPT through Voice UI on the web. If you haven’t already, read that article first to know what I built. What sells the illusion of having a real-time conversation with the AI is the low latency. Because the response is so quick, it doesn’t feel like the AI needs to process your information and create an audio file to playback to you. Even though this part feels realistic, the robotic voice for the speech synthesis doesn’t. My colleague &lt;a href="https://www.linkedin.com/in/christoferfalkman/"&gt;Christofer Falkman&lt;/a&gt; pointed me to a way I could make Aiva, the ChatGPT-powered assistant, even more realistic. Using &lt;a href="https://elevenlabs.io/"&gt;ElevenLabs&lt;/a&gt;’s AI-powered speech synthesis I can replace this robotic voice with an incredibly realistic voice. Time to upgrade Aiva!&lt;/p&gt;

&lt;h2&gt;
  
  
  Let’s implement it
&lt;/h2&gt;

&lt;p&gt;As you might remember from the previous article, Aiva works like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6oDSm9i---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://techhub.iodigital.com/articles/reducing-latency-in-ai-speech-synthesis/old-aiva-interaction-schematic.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6oDSm9i---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://techhub.iodigital.com/articles/reducing-latency-in-ai-speech-synthesis/old-aiva-interaction-schematic.png" alt="Schema showing the turn based conversation flow of Aiva" width="800" height="398"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Instead of using the native SpeechSynthesis Web API, we now replace it with a call to the ElevenLabs API where we get returned binary data in the form of a buffer. Or, simplified, we get a bit of data we can play as audio. As soon as the entire piece of text is sent to that API, returned to the application, and played as audio, the user gets to talk again. This yields a pretty cool result with an incredibly realistic voice:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vCrniG-x--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://techhub.iodigital.com/articles/reducing-latency-in-ai-speech-synthesis/new-aiva-interaction-schematic.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vCrniG-x--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://techhub.iodigital.com/articles/reducing-latency-in-ai-speech-synthesis/new-aiva-interaction-schematic.png" alt="Schema showing the turn based conversation flow of Aiva with sidestep to the ElevenLabs API" width="800" height="497"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  It’s slow!
&lt;/h2&gt;

&lt;p&gt;You might notice that the AI-powered speech synthesis is quite a bit slower. The latency increases with the length of the text. It first needs to turn all of the text into audio before it can play the first sentence. The longer the text, the slower the response. This is breaking the user experience of having a natural conversation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Low latency over realistic voice
&lt;/h3&gt;

&lt;p&gt;In my opinion, having a low-latency robotic voice in a real-time conversation is a better user experience. It doesn’t take you out of the conversation as much. So what now? Don’t use the AI version? Of course not, let’s fix this!&lt;/p&gt;

&lt;h2&gt;
  
  
  Time to take some well-known approaches
&lt;/h2&gt;

&lt;p&gt;First, I looked into whether the audio could be streamed from the API. Unfortunately, I couldn’t find this option and didn’t want to limit my choice of which AI-power speech synthesis products I could use. Then, I thought about how, as a developer, I would normally handle large slow requests. Imagine you are making a dashboard with 10.000 rows of data. You could opt for pagination where you click a button to go to the next page of results. Chunking the data in pages of rows works great. Taking this into a conversation, we could chunk the text into sentences and play the audio for each sentence. Whilst this is the approach I took, this posed a new problem. Let’s imagine I retrieve audio for a sentence of the text, play the audio, and then do this over and over again for the entire piece of text. This means that the latency is pretty much just split up, but still present:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--YPTTorhJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://techhub.iodigital.com/articles/reducing-latency-in-ai-speech-synthesis/ai-speech-synthesis-schematic-chunked.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--YPTTorhJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://techhub.iodigital.com/articles/reducing-latency-in-ai-speech-synthesis/ai-speech-synthesis-schematic-chunked.png" alt="Schema showing the conversation chunked into sentences in a serialized manner" width="800" height="263"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see, there is quite a bit of time while the audio is playing which I could utilize to retrieve the audio data for the next sentence. This is pretty similar to how humans talk. You think of what to say and while you are talking you’re already thinking of the next sentence. This results in a fluent flow where there is always audio playing:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--IlRKJ7va--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://techhub.iodigital.com/articles/reducing-latency-in-ai-speech-synthesis/ai-speech-synthesis-schematic-chunked-optimised.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--IlRKJ7va--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://techhub.iodigital.com/articles/reducing-latency-in-ai-speech-synthesis/ai-speech-synthesis-schematic-chunked-optimised.png" alt="Schema showing the conversation chunked into sentences in a synchronous manner" width="800" height="143"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The end result
&lt;/h2&gt;

&lt;p&gt;That did the trick! The latency is now low enough to not intrude on the natural flow of the conversation:&lt;/p&gt;

</description>
      <category>frontend</category>
      <category>ai</category>
      <category>chatgpt</category>
      <category>voice</category>
    </item>
    <item>
      <title>Interacting with ChatGPT through Voice UI on the web</title>
      <dc:creator>iO</dc:creator>
      <pubDate>Thu, 02 Nov 2023 00:00:00 +0000</pubDate>
      <link>https://dev.to/iodigital-com/interacting-with-chatgpt-through-voice-ui-on-the-web-4h71</link>
      <guid>https://dev.to/iodigital-com/interacting-with-chatgpt-through-voice-ui-on-the-web-4h71</guid>
      <description>&lt;p&gt;I probably don’t have to tell you what an enormous impact the rise of AI has had on the industry in recent times. It’s amazing to see all the progress that is being made. I knew when I was looking at an upcoming two-day hackathon at iO, I just needed to build something cool with ChatGPT. More particularly, the way you interact with ChatGPT.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why create a different way to interact with ChatGPT
&lt;/h2&gt;

&lt;p&gt;Personally, I don’t develop AI. I do however interact with it a lot. Like most of you reading, I interact the most with ChatGPT. While the chat interface is great to use while working, It always felt slightly awkward to interact with. When given the choice, I will always rather walk over to someone (or have a call) than use chat to discuss something. It feels a bit more natural as it is the way of communicating that is powered by our natural hardware: speaking and listening. So, if ChatGPT requires me to input text and read outputted text, can’t I just do that by speaking and listening?&lt;/p&gt;

&lt;p&gt;I’ve wanted to build a demo application that enables just that. A user can start a conversation with ChatGPT and just talk. Once they’re done talking, ChatGPT processes the text and sends back a response. This response is then read out loud to the user after which the cycle continues.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2ZISXJ53--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://techhub.iodigital.com/articles/interacting-with-chat-gpt-through-voice-ui-on-the-web/user-interaction-schematic.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2ZISXJ53--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://techhub.iodigital.com/articles/interacting-with-chat-gpt-through-voice-ui-on-the-web/user-interaction-schematic.png" alt="A schematic of the flow described above" width="800" height="278"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Being a developer for the web, I naturally gravitated to the techniques I would use there. As it turned out in the end, this gave a surprising advantage in the user experience. Here is a quick glimpse of the result called “Aiva”:&lt;/p&gt;

&lt;h2&gt;
  
  
  What techniques to use?
&lt;/h2&gt;

&lt;p&gt;For front-end frameworks, any will do. I’m most comfortable with React.js and Next.js so decided to use those. Also, I always want to try out at least one new thing when creating a demo. This time, that was the component library &lt;a href="https://www.radix-ui.com/"&gt;Radix&lt;/a&gt; which focusses on important aspects as accessibility. Also, I can highly recommend using a component library when building demos so building a nice UI doesn’t take away from your time and focus on the thing you are actually trying to build.&lt;/p&gt;

&lt;h3&gt;
  
  
  Input: SpeechRecognition Web API
&lt;/h3&gt;

&lt;p&gt;For the input, I used the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/SpeechRecognition"&gt;SpeechRecognition Web API&lt;/a&gt;. Fun fact, I built an application over six years ago called &lt;a href="https://presi-parrot.davebitter.com/"&gt;PresiParrot&lt;/a&gt; to automatically create captions when giving a presentation. What I find awesome about using web standards is that even after six years, the application still runs perfectly fine, perhaps even better, as it will always be supported by the browser.&lt;/p&gt;

&lt;p&gt;In essence, the SpeechRecognition Web API provides you with a way to use the user’s microphone to capture audio and once they’re done talking, provide you with a text string to use. You can even use interim, or live updated, results if needed.&lt;/p&gt;

&lt;h4&gt;
  
  
  Why use this?
&lt;/h4&gt;

&lt;p&gt;A big upside for me is the before-mentioned benefit that you get with using a web standard. Another major benefit of using the Web API that is built into the browser is that it’s incredibly quick. This goes for the SpeechRecognition Web API (input) but especially for the SpeechSynthesis Web API (output) which we’ll look at in a bit. Your browser already ships with the logic and voices that you might need, mitigating the need for another service that takes time. This greatly benefits the performance which is always important. But as it turned out, it became a nice surprise while building the demo. Have you ever tried to talk with your Google/Apple/Amazon home device? Whenever you ask something, there is always a few seconds of delay. This takes you out of the illusion that you’re actually having a conversation. As this Web API is so quick and needs no loading time, it feels nearly instant helping to sell the conversation effect.&lt;/p&gt;

&lt;h4&gt;
  
  
  How to use this?
&lt;/h4&gt;

&lt;p&gt;Using the SpeechRecognition Web API is fairly straightforward. You first see if the Web API is supported in the user’s browser:&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="o"&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;SpeechRecognition&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;webkitSpeechRecognition&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// handle fallback&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Next, as seen in the example above, the Web API can either named &lt;code&gt;SpeechRecognition&lt;/code&gt; or &lt;code&gt;webkitSpeechRecognition&lt;/code&gt;. We assign it like this and set it up:&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;const&lt;/span&gt; &lt;span class="nx"&gt;SpeechRecognition&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SpeechRecognition&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;webkitSpeechRecognition&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;recognition&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;SpeechRecognition&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nx"&gt;recognition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;interimResults&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="nx"&gt;recognition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lang&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;en-US&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;We change two settings, first we set &lt;code&gt;interimResults&lt;/code&gt; to &lt;code&gt;true&lt;/code&gt; so we can receive the interim results while the user is speaking. Next, we set the language the user is going to speak in. Naturally, you can provide UI to alter this, but for the demo english is fine.&lt;/p&gt;

&lt;p&gt;With everything configured, our next step is to create an &lt;code&gt;onresult&lt;/code&gt; and &lt;code&gt;onend&lt;/code&gt; callback:&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="nx"&gt;recognition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onresult&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;transcript&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="c1"&gt;// do something with transcript&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;recognition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onend&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;transcript&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="c1"&gt;// do something with transcript&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Now, for every interim result while the user is talking, you can do something with that transcript string once &lt;code&gt;onresult&lt;/code&gt; is called. For instance, you show some live feedback on the screen with what text the Web API interpreted the user’s speech as. Once the user stops talking, the &lt;code&gt;onend&lt;/code&gt; is called with the end result. Great! We now have turned speech into a text string to use as input!&lt;/p&gt;

&lt;h3&gt;
  
  
  Output: SpeechSynthesis Web API
&lt;/h3&gt;

&lt;p&gt;What the SpeechRecognition Web API is for input, the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/SpeechSynthesis"&gt;SpeechSynthesis Web API&lt;/a&gt; is for output. In it’s simplest form, the Web API allows your computer to read a text string out loud.&lt;/p&gt;

&lt;h4&gt;
  
  
  Why use this?
&lt;/h4&gt;

&lt;p&gt;As mentioned before, this Web API comes with the browser already built-in! It’s not just the Web API itself, but also a wide range of voices to use. These voices can be from difference countries and even different accents.&lt;/p&gt;

&lt;h4&gt;
  
  
  How to use this?
&lt;/h4&gt;

&lt;p&gt;Let’s make it read out a text string. First we check whether the user’s browser supports the Web API again and create a constant to use later on. Finally, we need to make a &lt;code&gt;SpeechSynthesisUtterance&lt;/code&gt; which is basically a unit of text that the Web API needs to read out loud:&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="o"&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;speechSynthesis&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SpeechSynthesisUtterance&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// handle fallback&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;synth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;speechSynthesis&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;voices&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;synth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getVoices&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;preferredVoice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;voices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;voice&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;voice&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;voiceURI&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Karen&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;utterance&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;SpeechSynthesisUtterance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hello from the computer!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;utterance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="nx"&gt;utterance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pitch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&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;preferredVoice&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;utterance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;voice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;preferredVoice&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;As you can see, I can retrieve a list of all the supported voices on the user’s browser. I like the one named &lt;code&gt;"Karen"&lt;/code&gt; so decided to store that one if found. Naturally, you can create a dropdown with voices for the user to select the one they like.&lt;/p&gt;

&lt;p&gt;Next, we create a &lt;code&gt;SpeechSynthesisUtterance&lt;/code&gt; with the text string to read out loud. We can then tweak the utterance a bit more by changing the rate (or speed) and the pitch of the voice. Finally, if our preferred voice is available, we assign it to the utterance as the voice to use.&lt;/p&gt;

&lt;p&gt;Now all that is left is to speak:&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="nx"&gt;utterance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onend&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// do something once all the text has been spoken&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;speechSynthesis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;speak&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;utterance&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;That’s it! We now have both input and output covered.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tying the two together
&lt;/h3&gt;

&lt;p&gt;I’ve created a &lt;code&gt;useSpeechRecognition&lt;/code&gt; hook that exposes utilities to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;get the interim result to display in the UI&lt;/li&gt;
&lt;li&gt;check whether there is permission to use the microphone to show UI whether it is available or not&lt;/li&gt;
&lt;li&gt;request listening permission to the user&lt;/li&gt;
&lt;li&gt;know whether the Web API is currently listening the the user&lt;/li&gt;
&lt;li&gt;start listening to the user&lt;/li&gt;
&lt;li&gt;stop listening to the user&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I’ve also created a &lt;code&gt;useSpeechSynthesis&lt;/code&gt; hook that exposes a utility to speak to the user with a passed text string.&lt;/p&gt;

&lt;p&gt;Finally, I’ve created a &lt;code&gt;useConversation&lt;/code&gt; hook that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Keeps track of the conversation state

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;"UNPERMITTED"&lt;/code&gt; - there is no permission yet to listen to the user’s microphone&lt;/li&gt;
&lt;li&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--GZYYCvLV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://techhub.iodigital.com/articles/interacting-with-chat-gpt-through-voice-ui-on-the-web/aiva-unpermitted.gif" alt="calm visualisation of Aiva unpermitted" width="666" height="546"&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;"IDLING"&lt;/code&gt; - the application is currently not listening or responding&lt;/li&gt;
&lt;li&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--35QHkCtV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://techhub.iodigital.com/articles/interacting-with-chat-gpt-through-voice-ui-on-the-web/aiva-idling.gif" alt="calm visualisation of Aiva idling" width="666" height="546"&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;"LISTENING"&lt;/code&gt; - the &lt;code&gt;useSpeechRecognition&lt;/code&gt; hook is listening the to the user’s microphone&lt;/li&gt;
&lt;li&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bACNZYf3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://techhub.iodigital.com/articles/interacting-with-chat-gpt-through-voice-ui-on-the-web/aiva-listening.gif" alt="more moving visualisation of Aiva listening" width="666" height="546"&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;"RESPONDING"&lt;/code&gt; - the &lt;code&gt;useSpeechSynthesis&lt;/code&gt; hook is speaking to the user&lt;/li&gt;
&lt;li&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bqZMqHjv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://techhub.iodigital.com/articles/interacting-with-chat-gpt-through-voice-ui-on-the-web/aiva-responding.gif" alt="moving visualisation of Aiva unpermitted" width="666" height="546"&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;"STOPPED"&lt;/code&gt; the user stopped the application&lt;/li&gt;
&lt;li&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--kdlub3zV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://techhub.iodigital.com/articles/interacting-with-chat-gpt-through-voice-ui-on-the-web/aiva-stopped.gif" alt="calm visualisation of Aiva stopped" width="666" height="546"&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Uses the utilities exposed by the &lt;code&gt;useSpeechRecognition&lt;/code&gt; and &lt;code&gt;useSpeechSynthesis&lt;/code&gt; hooks to create a turn-based conversation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--aHhFTtFU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://techhub.iodigital.com/articles/interacting-with-chat-gpt-through-voice-ui-on-the-web/aiva-interaction-schematic.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--aHhFTtFU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://techhub.iodigital.com/articles/interacting-with-chat-gpt-through-voice-ui-on-the-web/aiva-interaction-schematic.png" alt="A schematic of the flow described above" width="800" height="398"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  ChatGPT integration
&lt;/h4&gt;

&lt;p&gt;So far, the application has yet to be hooked up to the ChatGPT API and is just responding with a fake answer. Let’s make the magic happen. All I need to do is create an API route in my Next.js application that receives two things in the form of a POST request:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;messages&lt;/code&gt; - an array that consists of objects with a key of

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;role&lt;/code&gt; - who is the message from? The &lt;code&gt;"user"&lt;/code&gt; or the &lt;code&gt;"assistant"&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;content&lt;/code&gt; - the text string&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;aivaRole&lt;/code&gt; - what is the role of the AI to give it a bit of context on what it should behave like

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;"assistant"&lt;/code&gt; - a general AI assistant that can virtually take up any role as long as the user instructs it to&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;"tech-interviewer"&lt;/code&gt; - behave like a tech interviewer so a developer can practice an interview&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;"scrum-master"&lt;/code&gt; - act like a scrum master and help a team with their scrum sessions&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;"counselor"&lt;/code&gt; - be a counselor to the user providing them with a listening ear or advice&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I added the &lt;code&gt;aivaRole&lt;/code&gt; to give people using the demo an idea on what Aiva could be used for.&lt;/p&gt;

&lt;p&gt;The user sends over a list of the whole conversation for context for the AI as it needs this for every request. I prepend this list with a bit of context for the AI so it knows it runs in a Voice UI, how it should behave/respond and finally the role the user set the AI to. This request then goes to the ChatGPT API, returns a response, returns that response to the front-end to finally be consumed by the &lt;code&gt;useConversation&lt;/code&gt; hook.&lt;/p&gt;

&lt;h3&gt;
  
  
  React Three Fiber Visualisation
&lt;/h3&gt;

&lt;p&gt;So yes, much like &lt;a href="https://openai.com/blog/chatgpt-can-now-see-hear-and-speak"&gt;the OpenAI solution&lt;/a&gt;, you can now speak with the conversation. Besides mine feeling a bit more natural in the sense that it responds a lot quicker (~800ms), it still feels like you are talking with a ChatGPT API. We’ve stripped the need for traditional UI like inputs and text on the screen, but created a great new opportunity to give some personality to Aiva and simultaneously provide the needed feedback to the user (is it listening, responding etc.).&lt;/p&gt;

&lt;h4&gt;
  
  
  Why use this?
&lt;/h4&gt;

&lt;p&gt;A few weeks back at the &lt;a href="https://frontmania.com/"&gt;Frontmania Conference&lt;/a&gt;, &lt;a href="https://twitter.com/tim_beeren"&gt;Tim Beeren&lt;/a&gt; showed some amazing examples of &lt;a href="https://threejs.org/"&gt;Three.js&lt;/a&gt; using &lt;a href="https://docs.pmnd.rs/react-three-fiber/getting-started/introduction"&gt;React Three Fiber&lt;/a&gt;. Another opportunity to try out something I haven’t worked with before! React Three Fiber basically allows a simpeler integration for using Three.js in a React.js application. This was good for me as I was not only using React.js to render the application, but also have the state of the conversation there which I wanted the visualisation to respond to.&lt;/p&gt;

&lt;h4&gt;
  
  
  How to use this?
&lt;/h4&gt;

&lt;p&gt;I quickly realised that creating an awesome interactive visualisation would take quite a bit knowledge on subjects like shaders. Due to my incredibly short timeframe of two days to build the entire application, I took an &lt;a href="https://www.youtube.com/watch?v=6YJ-2MvDqhc"&gt;existing visualisation&lt;/a&gt; and made it interact with the state of the conversation. I had control over two parameters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;speed&lt;/code&gt; the speed of the movement of the blob&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;intensity&lt;/code&gt; the intensity of the spikes in the blob&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I could then create a little hook to change these values based on the conversation state:&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;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;switch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;conversationState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;RESPONDING&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="nx"&gt;speed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
      &lt;span class="nx"&gt;intensity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.75&lt;/span&gt;
      &lt;span class="k"&gt;break&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;LISTENING&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="nx"&gt;speed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;1.25&lt;/span&gt;
      &lt;span class="nx"&gt;intensity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.35&lt;/span&gt;
      &lt;span class="k"&gt;break&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;STOPPED&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;IDLING&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="nx"&gt;speed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.8&lt;/span&gt;
      &lt;span class="nx"&gt;intensity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.1&lt;/span&gt;
      &lt;span class="k"&gt;break&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;UNPERMITTED&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="nx"&gt;speed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;
      &lt;span class="nx"&gt;intensity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.05&lt;/span&gt;
      &lt;span class="k"&gt;break&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;conversationState&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  The end result
&lt;/h2&gt;

&lt;p&gt;With all the parts connected, I now have an application that caters a turn-based conversation where the state of the conversation is visualised through an interactive data visualisation. All of that without the need of a single piece of text or required user interaction on the screen! To showcase how these conversation can go, I’ve recorded this short screencast:&lt;/p&gt;

&lt;h3&gt;
  
  
  What are some practical use cases?
&lt;/h3&gt;

&lt;p&gt;In this article I already highlighted a few use cases for demo purposes. The easy answer is that it can cater all use cases. If I boil down my application to its simplest form I created a nice input-output system for a conversation. Whatever the user wants to use it for they can tell the AI and it will, if appropriate, follow. A few more examples (which I naturally asked ChatGPT for):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Customer Support&lt;/strong&gt; : ChatGPT can be used as a virtual customer support agent, handling common queries, providing information, and resolving issues.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sales Assistance&lt;/strong&gt; : ChatGPT can act as a virtual sales assistant, engaging with potential customers, answering questions about products or services, and providing recommendations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Personal Assistant&lt;/strong&gt; : ChatGPT can assist individuals in managing their schedules, setting reminders, making appointments, and helping with day-to-day tasks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Content Creation&lt;/strong&gt; : ChatGPT can generate blog posts, social media captions, or marketing copy, helping businesses with their content creation needs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Language Translation&lt;/strong&gt; : ChatGPT can facilitate real-time language translation, allowing users to communicate with individuals who speak different languages.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lead Generation&lt;/strong&gt; : ChatGPT can engage website visitors, qualify leads, and collect contact information, enhancing lead generation efforts for businesses.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Training and Education&lt;/strong&gt; : ChatGPT can be utilized as a virtual tutor or trainer, providing personalized learning experiences, answering questions, and delivering educational content.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Market Research&lt;/strong&gt; : ChatGPT can conduct surveys, gather feedback, and analyze customer preferences, helping businesses gain insights for market research purposes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Virtual Event Host&lt;/strong&gt; : ChatGPT can serve as a virtual event host, guiding attendees, answering questions, and providing information about the event agenda or sessions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Personal Entertainment&lt;/strong&gt; : ChatGPT can engage users in entertaining conversations, tell jokes, share interesting facts, or even play interactive storytelling games for personal enjoyment.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The sky(net) is the limit!&lt;/p&gt;

&lt;h3&gt;
  
  
  What are the takeaways?
&lt;/h3&gt;

&lt;p&gt;ChatGPT, or AI in general, is being developer at an incredible pace. What we mustn’t forget is that we also need to take into account how the user interacts with AI. By creating a more natural conversation with the use of voice and sound we vastly improve the user experience in interacting with these products. By no means is a regular chat interface bad, it just is really good for quite some use cases. That doesn’t mean we shouldn’t explore other possibilities and create awesome new user experiences!&lt;/p&gt;

&lt;p&gt;I feel like the match between AI and voice is great! As mentioned earlier, I’ve created an application that used the SpeechRecognition Web API over six years ago and never since! Even though I found it cool, I hadn’t find the actual use case for the technique up until creating this demo application. Sometimes combining older techniques and principles with new state-of-the art technology can lead to some magic.&lt;/p&gt;

&lt;p&gt;Take my concept, think about some practical applications you might have (at your company) and try it out! Let’s make super cool products!&lt;/p&gt;

</description>
      <category>frontend</category>
      <category>ai</category>
      <category>chatgpt</category>
      <category>voice</category>
    </item>
    <item>
      <title>Make awesome animated interactions with two lines of code</title>
      <dc:creator>iO</dc:creator>
      <pubDate>Wed, 18 Oct 2023 00:00:00 +0000</pubDate>
      <link>https://dev.to/iodigital-com/make-awesome-animated-interactions-with-two-lines-of-code-4775</link>
      <guid>https://dev.to/iodigital-com/make-awesome-animated-interactions-with-two-lines-of-code-4775</guid>
      <description>&lt;p&gt;You might have heard about the View Transitions API in the context of animating between web pages. Whilst this is certainly awesome, you can apply this approach on a way smaller level to create awesome animated interactions in your web applications!&lt;/p&gt;

&lt;p&gt;A few weeks ago I was lucky enough to visit the Google Campus in San Francisco through the Google Developer Expert program I am part of.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9wXfIGi0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://techhub.iodigital.com/articles/view-transitions-api-animated-interactions/dave-google-campus.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9wXfIGi0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://techhub.iodigital.com/articles/view-transitions-api-animated-interactions/dave-google-campus.jpeg" alt="Dave bitter on the Google Campus in San Francisco riding a Google bike" width="800" height="1067"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There, &lt;a href="https://twitter.com/Una"&gt;Una Kravets&lt;/a&gt; showed &lt;a href="https://codepen.io/una/pen/eYbOOQp"&gt;a demo&lt;/a&gt; she built using the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API"&gt;View Transitions API&lt;/a&gt; to animate the deletion of cards. This excited me to look at using the API for not just page transitions, but more for micro animations!&lt;/p&gt;

&lt;h2&gt;
  
  
  Give feedback to the user
&lt;/h2&gt;

&lt;p&gt;As always, I made to see what we can do with this approach:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://codepen.io/davebitter/full/JjwgdRZ"&gt;View on CodePen&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;First, I created a layout like I would normally do. Next, I wrote some JavaScript to remove a list item with a small delay to see it get the checked state first. Removing an item looked like this:&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="nx"&gt;list&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="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;removeListItemFromInputChangeEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8PlIGVu6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://techhub.iodigital.com/articles/view-transitions-api-animated-interactions/removing-unanimated.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8PlIGVu6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://techhub.iodigital.com/articles/view-transitions-api-animated-interactions/removing-unanimated.gif" alt="Screen recording showing an item being removed and the grid of cards immediately snap into place" width="800" height="377"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The user experience of removing an item is not great. Because the item is removed, the item next to it now takes it place. Suddenly, this item is in the place of the removed one which makes it look like it was always there. What if we can remove it, have all the items neatly animate to their new position and by doing that give the user the proper feedback they need? In the past, this took quite a bit of JavaScript to do and even then it worked a bit janky. Now let’s (technically) add two lines of code. First, wrap the call of the remove function in a &lt;code&gt;startViewTransition&lt;/code&gt;. For good measure, we’ll also feature check to progressively enhance the user experience. Meaning, if the View Transitions API is supported, we enhance the default behaviour with a nice animation:&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="nx"&gt;list&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="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;setTimeout&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;if &lt;/span&gt;&lt;span class="p"&gt;(&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;startViewTransition&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;removeListItemFromInputChangeEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startViewTransition&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;removeListItemFromInputChangeEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;300&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, we add a unique name to each of the list items:&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;li&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;view-transitions-name: card-1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;checkbox&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nx"&gt;Check&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="nx"&gt;card&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/label&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/li&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now if we delete an item, we see this behaviour:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tpSOaIav--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://techhub.iodigital.com/articles/view-transitions-api-animated-interactions/removing-animated.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tpSOaIav--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://techhub.iodigital.com/articles/view-transitions-api-animated-interactions/removing-animated.gif" alt="Screen recording showing an item being removed and the grid of cards animate into place" width="800" height="378"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With just adding the &lt;code&gt;document.startViewTransition&lt;/code&gt; and a unique &lt;code&gt;view-transitions-name&lt;/code&gt; for each list item, the entire grid repositioning is animated providing better feedback to the user. Awesome, right? One of the biggest benefits I find of this approach is that the View Transitions API just takes a before and after state. It doesn’t matter how you build your layout, what properties change or how different the two views are. As long as before and after your change (the removal of the one list item node) items have matching &lt;code&gt;view-transitions-name&lt;/code&gt; properties, the browser will animate between them.&lt;/p&gt;

&lt;p&gt;Now, let’s add a button that allows you to add a new item:&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="nx"&gt;addButton&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="nf"&gt;addListItem&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--sWrlNaJy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://techhub.iodigital.com/articles/view-transitions-api-animated-interactions/adding-unanimated.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--sWrlNaJy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://techhub.iodigital.com/articles/view-transitions-api-animated-interactions/adding-unanimated.gif" alt="Screen recording showing an item being added and the grid of cards immediately snap into place" width="800" height="378"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We see the same unanimated behaviour as previously when deleting an item. Now let’s add the &lt;code&gt;document.startViewTransition&lt;/code&gt; again:&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="nx"&gt;addButton&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="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="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;startViewTransition&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;addListItem&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="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startViewTransition&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;addListItem&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;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--N_D-3-De--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://techhub.iodigital.com/articles/view-transitions-api-animated-interactions/adding-animated.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--N_D-3-De--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://techhub.iodigital.com/articles/view-transitions-api-animated-interactions/adding-animated.gif" alt="Screen recording showing an item being added and the grid of cards animate into place" width="800" height="378"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see, now when an item is added, the rest of the items animate to their new position. We can even add a bit of JavaScript that will randomly reorder the list and see what this technique will do:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--72ypcY1J--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://techhub.iodigital.com/articles/view-transitions-api-animated-interactions/reordering-animated.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--72ypcY1J--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://techhub.iodigital.com/articles/view-transitions-api-animated-interactions/reordering-animated.gif" alt="Screen recording showing an items being randomly reordered and the grid of cards animate into place" width="800" height="378"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Some other use cases
&lt;/h2&gt;

&lt;p&gt;While having a grid of cards that may be filtered is a pretty common use case, you can naturally take this approach to other interactions as well. More than likely, you are working/have worked with tables. When adding to, removing from, sorting or filtering these tables the rows may change position. When calling the &lt;code&gt;startViewTransition&lt;/code&gt; you can animate the changing of the rows like so:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hKO4poRs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://techhub.iodigital.com/articles/view-transitions-api-animated-interactions/table-animated.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hKO4poRs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://techhub.iodigital.com/articles/view-transitions-api-animated-interactions/table-animated.gif" alt="Screen recording showing an items being reordered, removed and added and the grid of cards aniamte into place" width="800" height="378"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You may have noticed I added a toggle to this demo for showing the items in a grid or underneath each other. When switching, that looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--nOuc1ThS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://techhub.iodigital.com/articles/view-transitions-api-animated-interactions/toggling-unanimated.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nOuc1ThS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://techhub.iodigital.com/articles/view-transitions-api-animated-interactions/toggling-unanimated.gif" alt="Screen recording showing the grid of cards toggle between grid and table view and immediately snap into place" width="800" height="378"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I though to myself, why not use this technique when switching between the two views?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zy2uiIFr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://techhub.iodigital.com/articles/view-transitions-api-animated-interactions/toggling-animated.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zy2uiIFr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://techhub.iodigital.com/articles/view-transitions-api-animated-interactions/toggling-animated.gif" alt="Screen recording showing the grid of cards toggle between grid and table view and animating into place" width="800" height="378"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Start using this now
&lt;/h2&gt;

&lt;p&gt;It has never been easier to animate between different views on the page. Whilst animating between pages might be a bit much to start with, try implementing the View Transitions API on a smaller part of a page to make smoother experiences in your web application whilst simultaneously providing feedback for a user’s interactions through animation. It only takes a line or two!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://codepen.io/davebitter/full/JjwgdRZ"&gt;View on CodePen&lt;/a&gt;&lt;/p&gt;

</description>
      <category>frontend</category>
      <category>css</category>
    </item>
    <item>
      <title>JavaScript Module Systems</title>
      <dc:creator>iO</dc:creator>
      <pubDate>Wed, 11 Oct 2023 00:00:00 +0000</pubDate>
      <link>https://dev.to/iodigital-com/javascript-module-systems-9j</link>
      <guid>https://dev.to/iodigital-com/javascript-module-systems-9j</guid>
      <description>&lt;h2&gt;
  
  
  What is a JavaScript module?
&lt;/h2&gt;

&lt;p&gt;In JavaScript, a module system allows developers to organize code into reusable pieces or components, which can be imported and used in other application parts. The module system provides a way to encapsulate code, manage dependencies, and control the scope of variables and functions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why do we need module systems?
&lt;/h2&gt;

&lt;p&gt;Using a module system in JavaScript, or in any programming language, is crucial for several reasons, especially as applications grow in complexity. Here are some of the key reasons why a module system is essential:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Code Organization:
&lt;/h3&gt;

&lt;p&gt;Modules allow developers to split their code into smaller, more manageable pieces, each responsible for a specific feature or functionality. This makes the codebase more &lt;strong&gt;organized, readable, and maintainable&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--npRc8oEX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://techhub.iodigital.com/articles/javascript-module-systems/code-organization.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--npRc8oEX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://techhub.iodigital.com/articles/javascript-module-systems/code-organization.webp" alt="The image shows an example of organizing code" width="446" height="326"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Scope Management and Collision Avoidance
&lt;/h3&gt;

&lt;p&gt;Each module has its own scope, meaning variables and functions defined in a module are not globally accessible unless explicitly exported. This helps in avoiding variable conflicts and polluting the global namespace.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--HkG-WHro--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://techhub.iodigital.com/articles/javascript-module-systems/scope-management.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--HkG-WHro--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://techhub.iodigital.com/articles/javascript-module-systems/scope-management.png" alt="The image shows an example of organizing code" width="582" height="328"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Dependency Management
&lt;/h3&gt;

&lt;p&gt;Dependency management is clearer with modules, as dependencies are explicitly imported, making it easier to track and manage the relationships between different parts of an application.&lt;/p&gt;

&lt;p&gt;Suppose you have three JavaScript files: &lt;code&gt;utility.js&lt;/code&gt;, &lt;code&gt;dataProcessor.js&lt;/code&gt;, and &lt;code&gt;app.js&lt;/code&gt;. The &lt;code&gt;dataProcessor.js&lt;/code&gt; file depends on a function from &lt;code&gt;utility.js&lt;/code&gt;, and &lt;code&gt;app.js&lt;/code&gt; depends on a function from &lt;code&gt;dataProcessor.js&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ExJZbK9N--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://techhub.iodigital.com/articles/javascript-module-systems/dependency-management.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ExJZbK9N--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://techhub.iodigital.com/articles/javascript-module-systems/dependency-management.png" alt="The image shows an example of dependency management" width="690" height="455"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Available module systems in JavaScript
&lt;/h2&gt;

&lt;p&gt;In JavaScript, there are several module systems that allow developers to organize and structure their code. Here are the most commonly used module systems:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;CommonJS (CJS)&lt;/li&gt;
&lt;li&gt;Asynchronous Module Definition (AMD)&lt;/li&gt;
&lt;li&gt;Universal Module Definition (UMD)&lt;/li&gt;
&lt;li&gt;ES6 Modules (ESM)&lt;/li&gt;
&lt;li&gt;SystemJS&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  1. CommonJS (CJS)
&lt;/h3&gt;

&lt;p&gt;CommonJS is a module system implemented by &lt;a href="https://nodejs.org/"&gt;Node.js&lt;/a&gt; for organizing and sharing JavaScript code. Before the advent of ES6 modules, CommonJS was the primary way to include and manage dependencies in Node.js applications. While browsers primarily use the ES6 module system, tools like &lt;a href="https://browserify.org/"&gt;Browserify&lt;/a&gt; and &lt;a href="https://webpack.js.org/"&gt;Webpack&lt;/a&gt; can transform CommonJS modules to be browser-compatible.&lt;/p&gt;

&lt;h4&gt;
  
  
  Key Concepts:
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;Module Definition:&lt;/em&gt;&lt;/strong&gt; In CommonJS, every file is its own module. The variables, functions, and objects you define in a file are local to that file unless explicitly exported.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;Export:&lt;/em&gt;&lt;/strong&gt; To make parts of your code available for use in other files, you use &lt;code&gt;module.export&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;Import:&lt;/em&gt;&lt;/strong&gt; To use a module in another file, you use the &lt;code&gt;require&lt;/code&gt; function. This function returns whatever the target module exported.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;Synchronous Loading:&lt;/em&gt;&lt;/strong&gt; Modules are loaded synchronously, meaning the program waits for the module to be fully loaded and executed before moving on.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;&lt;a href="https://medium.com/@mahabadi/understanding-the-statefulness-of-js-modules-2bc353af0e8e"&gt;Module Caching&lt;/a&gt;:&lt;/em&gt;&lt;/strong&gt; Modules are cached after the first time they are loaded, improving performance and ensuring that module initialization only happens once.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;No tree shaking:&lt;/em&gt;&lt;/strong&gt; because when you import a module you get an object.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  How to use CommonJS?
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Setting up:&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Ensure you have Node.js installed. If not, download and install it from &lt;a href="https://nodejs.org/"&gt;Node.js official website&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Step 1:&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Create a file named &lt;code&gt;counter.js&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;let&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="o"&gt;++&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;getCount&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;count&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;getCount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;em&gt;Step2:&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Create a file named &lt;code&gt;app.js&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;const&lt;/span&gt; &lt;span class="nx"&gt;counter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./counter.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getCount&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="c1"&gt;// Outputs: 0&lt;/span&gt;
&lt;span class="nx"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getCount&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="c1"&gt;// Outputs: 1&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;em&gt;Running the Code:&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
In your terminal, run the file using Node.js &lt;code&gt;node app.js&lt;/code&gt;. You’ll see that the &lt;code&gt;count&lt;/code&gt; variable in the &lt;code&gt;counter.js&lt;/code&gt; module retains its state between function calls.&lt;/p&gt;
&lt;h3&gt;
  
  
  2. Asynchronous Module Definition (AMD)
&lt;/h3&gt;

&lt;p&gt;It is a specification for the modular development of JavaScript applications and libraries. It allows developers to define modules and their dependencies &lt;strong&gt;asynchronously&lt;/strong&gt; , making it particularly well-suited for the &lt;strong&gt;browser environment&lt;/strong&gt; where synchronous loading of modules can result in performance issues. The most popular implementation of AMD is the &lt;a href="https://requirejs.org/"&gt;RequireJS&lt;/a&gt; library.&lt;/p&gt;
&lt;h4&gt;
  
  
  Key Concepts:
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;Define a Module:&lt;/em&gt;&lt;/strong&gt; Use the &lt;code&gt;define&lt;/code&gt; function to specify a module, its dependencies, and a factory function that returns the module's exports.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;Load and Use a Module:&lt;/em&gt;&lt;/strong&gt; Use the &lt;code&gt;require&lt;/code&gt; function to load a module and its dependencies.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;Asynchronous Loading:&lt;/em&gt;&lt;/strong&gt; One of the primary benefits of AMD is its support for asynchronous module loading. This means that modules and their dependencies can be fetched in parallel, which can lead to faster page loads in a browser environment.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;Module ID:&lt;/em&gt;&lt;/strong&gt; Each module can have a unique module ID, which is typically its file path (minus the &lt;code&gt;.js&lt;/code&gt; extension). This ID is used when specifying dependencies.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  How to use AMD?
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Setting up:&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Include RequireJS in your HTML file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;data-main=&lt;/span&gt;&lt;span class="s"&gt;"scripts/app"&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"path/to/require.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;data-main&lt;/code&gt; attribute points to the main script of your application.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Step 1:&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Create a file named &lt;code&gt;scripts/math.js&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;// math.js&lt;/span&gt;

&lt;span class="nf"&gt;define&lt;/span&gt;&lt;span class="p"&gt;([],&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&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;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;b&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;subtract&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&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;a&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;b&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;add&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;subtract&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;subtract&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’ve defined a module without any dependencies (empty array) and exported two functions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Step 2:&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Create a file named &lt;code&gt;scripts/app.js&lt;/code&gt; (your main script):&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;// app.js&lt;/span&gt;

&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;math&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;math&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="c1"&gt;// Outputs: 8&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subtract&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="c1"&gt;// Outputs: 2&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’re loading the &lt;code&gt;math&lt;/code&gt; module and using its functions. Once the &lt;code&gt;math&lt;/code&gt; module is loaded, the function will be called.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Running the Code:&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Simply open the HTML file containing the RequireJS script tag in a browser. The main script (&lt;code&gt;app.js&lt;/code&gt;) will be executed, and it will asynchronously load the &lt;code&gt;math.js&lt;/code&gt; module.&lt;/p&gt;
&lt;h3&gt;
  
  
  3. Universal Module Definition (UMD)
&lt;/h3&gt;

&lt;p&gt;It is a pattern for writing JavaScript modules that can work &lt;strong&gt;both in the browser and on the server&lt;/strong&gt;. The idea behind UMD is to make a module compatible with the most popular script loaders of the day. In fact, it is a combination of &lt;strong&gt;CommonJs + AMD&lt;/strong&gt;.&lt;/p&gt;
&lt;h4&gt;
  
  
  Key Concepts:
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;em&gt;Compatibility:&lt;/em&gt;&lt;/strong&gt; UMD aims to support the three most popular module formats:&lt;br&gt;&lt;br&gt;
&lt;em&gt;AMD (Asynchronous Module Definition):&lt;/em&gt; Used by browsers and includes libraries like RequireJS.&lt;br&gt;&lt;br&gt;
&lt;em&gt;CommonJS (CJS):&lt;/em&gt; Used primarily on the server side, especially with Node.js.&lt;br&gt;&lt;br&gt;
&lt;em&gt;Global variable definition:&lt;/em&gt; For browsers without any module loader.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;em&gt;Flexibility:&lt;/em&gt;&lt;/strong&gt; UMD allows developers to write a module once and have it work in many environments without modification.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;em&gt;Wrapper Pattern:&lt;/em&gt;&lt;/strong&gt; UMD typically wraps the module definition inside a function that can detect the module system in use and act accordingly.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;
  
  
  How to use UMD?
&lt;/h4&gt;

&lt;p&gt;Here’s a basic example of how you might define a module using UMD:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;1. Define a UMD module:&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Create a file named &lt;code&gt;my-umd.js&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="p"&gt;;(&lt;/span&gt;&lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;define&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;function&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;define&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;amd&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// AMD. Register as an anonymous module.&lt;/span&gt;
    &lt;span class="nf"&gt;define&lt;/span&gt;&lt;span class="p"&gt;([],&lt;/span&gt; &lt;span class="nx"&gt;factory&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;object&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Node. Does not work with strict CommonJS, but&lt;/span&gt;
    &lt;span class="c1"&gt;// only CommonJS-like environments that support module.exports,&lt;/span&gt;
    &lt;span class="c1"&gt;// like Node.&lt;/span&gt;
    &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;factory&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="c1"&gt;// Browser globals (root is window)&lt;/span&gt;
    &lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;myModule&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="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;hello&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hello, World!&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;&lt;strong&gt;&lt;em&gt;2. Running the UMD module:&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;a. In the Browser:&lt;/em&gt;&lt;br&gt;&lt;br&gt;
Include the UMD module in a script tag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"my-umd.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;myModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="c1"&gt;// Outputs: "Hello, World!"&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;b. Using AMD (e.g., RequireJS):&lt;/em&gt;&lt;br&gt;&lt;br&gt;
First, set up RequireJS and then define the module:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"path_to_require.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  &lt;span class="nf"&gt;require&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-umd&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;myModule&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;myModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="c1"&gt;// Outputs: "Hello, World!"&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;c. In Node.js:&lt;/em&gt;&lt;br&gt;&lt;br&gt;
First, set up Node.js and then just require the file:&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;const&lt;/span&gt; &lt;span class="nx"&gt;myModule&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&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-umd.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;myModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="c1"&gt;// Outputs: "Hello, World!"&lt;/span&gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. ES6 Modules (ESM)
&lt;/h3&gt;

&lt;p&gt;It is the native module system introduced in ECMAScript 6 (ES6), also known as ECMAScript 2015 (ES2015). ESM allows you to modularize your JavaScript code, making it more maintainable, scalable, and organized for &lt;strong&gt;both the browser and the server&lt;/strong&gt;. With its native support in modern environments and tools, it’s become an essential part of the JavaScript ecosystem.&lt;/p&gt;

&lt;h4&gt;
  
  
  Key Concepts:
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;Export &amp;amp; Import:&lt;/em&gt;&lt;/strong&gt; The core of ESM is the ability to export and import functions, objects, or primitives from one module to another.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;Static Analysis:&lt;/em&gt;&lt;/strong&gt; Imports and exports are determined statically at compile time, rather than runtime. This means tools can analyze a module’s dependencies without executing it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;File-based Modules:&lt;/em&gt;&lt;/strong&gt; In ESM, each file is its own module. Anything not exported remains private to that module.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;Dynamic Imports:&lt;/em&gt;&lt;/strong&gt; Introduced later, dynamic imports allow you to load modules on the fly using promises.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;Static Structure:&lt;/em&gt;&lt;/strong&gt; One of the defining characteristics of ESM is its static structure. This means you can’t conditionally import/export inside functions or conditionals. The structure is determined at compile time, not runtime.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;Cyclic Dependencies:&lt;/em&gt;&lt;/strong&gt; ESM handles cyclic (or circular) dependencies between modules. If module A imports from module B, and module B imports from module A, the language has mechanisms to handle this without causing errors.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;&lt;a href="https://medium.com/@mahabadi/understanding-the-statefulness-of-js-modules-2bc353af0e8e"&gt;Module Caching:&lt;/a&gt;&lt;/em&gt;&lt;/strong&gt; If multiple modules import from a single module, that module will only be executed once. This ensures that side effects in a module (like setting up an event listener) don’t occur multiple times.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;Tree shakeable:&lt;/em&gt;&lt;/strong&gt; because of &lt;code&gt;static analysis&lt;/code&gt; supported by ES6.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  How to use it?
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Exporting from a module:&lt;/em&gt;&lt;/strong&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;// math.js&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;add&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;subtract&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;em&gt;Importing into another module:&lt;/em&gt;&lt;/strong&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;// app.js&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;subtract&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./module.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;add&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="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="c1"&gt;// Outputs: 5&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;em&gt;Using Default Exports:&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Each module can have one default export, which can be imported without curly braces.&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;// greet.js&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;function &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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hello!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// app.js&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;greet&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./greet.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;greet&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="c1"&gt;// Outputs: Hello!&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;em&gt;Combining Imports:&lt;/em&gt;&lt;/strong&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;// app.js&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;greet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;subtract&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./module.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;em&gt;Renaming Imports:&lt;/em&gt;&lt;/strong&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;// app.js&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;add&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;addition&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./math.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;addition&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="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="c1"&gt;// Outputs: 5&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;em&gt;Dynamic Imports:&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Useful for &lt;code&gt;code-splitting&lt;/code&gt; and &lt;code&gt;lazy-loading&lt;/code&gt; modules.&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;const&lt;/span&gt; &lt;span class="nx"&gt;loadModule&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&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;./dynamicModule.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;someFunction&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  5. SystemJS
&lt;/h3&gt;

&lt;p&gt;SystemJS is a dynamic module loader that provides a way to load ES6 modules, CommonJS, AMD, and global scripts. It’s a powerful tool that bridges the gap between different module formats and allows developers to write code that can be loaded in various environments without modification.&lt;/p&gt;

&lt;h4&gt;
  
  
  Where do we use SystemJs?
&lt;/h4&gt;

&lt;p&gt;SystemJS is primarily designed for &lt;strong&gt;the browser&lt;/strong&gt; to handle module loading in environments that may not natively support certain module formats. However, &lt;strong&gt;it's not limited to just the browser&lt;/strong&gt;. You can use SystemJS on the server side, particularly with Node.js, but there are some considerations to keep in mind:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;em&gt;Native Module Support in Node.js:&lt;/em&gt;&lt;/strong&gt; Node.js has built-in support for CommonJS modules, and recent versions of Node.js also support ES6 modules natively. This often reduces the need for a module loader like SystemJS on the server side.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;em&gt;Use Cases on the Server:&lt;/em&gt;&lt;/strong&gt; One potential use case for SystemJS on the server is when you have a mixed module environment (e.g., a combination of CommonJS, AMD, and ES6 modules) and you want a unified way to load them. Another scenario might be dynamically loading modules at runtime based on certain conditions.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;em&gt;Integration with Other Tools:&lt;/em&gt;&lt;/strong&gt; If you’re considering using SystemJS on the server side, you might also want to look into how it integrates with other server-side tools and frameworks you’re using.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Key Concepts:
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;Universal Module Loading:&lt;/em&gt;&lt;/strong&gt; can load multiple module formats, including: — ES6 modules
— CommonJS
— AMD
— UMD
— Global scripts (non-modular scripts)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;Dynamic Loading:&lt;/em&gt;&lt;/strong&gt; allows for the dynamic loading of modules at runtime. This is particularly useful for applications that load modules based on certain conditions or user interactions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;Transpilation Support:&lt;/em&gt;&lt;/strong&gt; can be integrated with transpilers like Babel or TypeScript. This means you can write code in modern ES6 or TypeScript and have SystemJS load and transpile it on the fly in browsers that only support ES5.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;Plugin System:&lt;/em&gt;&lt;/strong&gt; supports plugins, enabling it to load not just JavaScript modules but also other resources like JSON, CSS, text, and images as modules.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;Configuration and Flexibility:&lt;/em&gt;&lt;/strong&gt; provides a rich configuration system, allowing developers to specify paths, aliases, default extensions, and other settings to control how modules are loaded and resolved.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;Production Optimizations:&lt;/em&gt;&lt;/strong&gt; Because of its dynamic loading features, SystemJS thrives in development contexts, but it also gives options to optimize for production. Tools like &lt;code&gt;systemjs-builder&lt;/code&gt; can be used to bundle modules into fewer files, reducing the number of HTTP requests and improving load times.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;Compatibility:&lt;/em&gt;&lt;/strong&gt; provides a way to use modules in environments that don't natively support them. For instance, before ES6 module support was widespread in browsers, SystemJS allowed developers to use ES6 module syntax and load them effectively.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;Production Bundles:&lt;/em&gt;&lt;/strong&gt; While SystemJS is great for development with its dynamic loading capabilities, for production, it's often recommended to create bundles to reduce the number of requests and improve performance.&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  How to use it?
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Installation:&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
You can include SystemJS via a script tag or install it via npm &lt;code&gt;npm install systemjs&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"path-to-systemjs/dist/system.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;em&gt;Configuration:&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Set up a configuration for SystemJS. This tells SystemJS where to find modules and how to load them.&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="nx"&gt;SystemJS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;map&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;jquery&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;path-to-jquery&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;packages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;defaultExtension&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;em&gt;Loading Modules:&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Use SystemJS to load your main module.&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="nx"&gt;SystemJS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app/main.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Use the module here&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

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

&lt;/div&gt;



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

&lt;p&gt;JavaScript has seen various module systems over the years, each with its unique strengths. However, ESM Modules stand out due to their efficiency and widespread adoption in modern frameworks. For developers aiming for streamlined and future-ready applications, using ESM Modules is a clear choice.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>modulesystems</category>
      <category>frontend</category>
    </item>
    <item>
      <title>I want it in my terminal!</title>
      <dc:creator>iO</dc:creator>
      <pubDate>Tue, 10 Oct 2023 00:00:00 +0000</pubDate>
      <link>https://dev.to/iodigital-com/i-want-it-in-my-terminal-2j1h</link>
      <guid>https://dev.to/iodigital-com/i-want-it-in-my-terminal-2j1h</guid>
      <description>&lt;h2&gt;
  
  
  I Don't Want to Leave My Terminal
&lt;/h2&gt;

&lt;p&gt;The terminal, often called the command line or shell, is a powerful interface for interacting with your computer. While it may appear daunting to newcomers, mastering the terminal can significantly enhance your productivity and open up a world of possibilities. In this article, we'll explore a few handy tools that can be accessed directly from the terminal, providing valuable information and shortcuts for various tasks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cheat Sheets for Tools and Languages
&lt;/h2&gt;

&lt;p&gt;Whether you're a programmer, developer, or system administrator, you often need quick access to syntax, commands, and documentation for various programming languages and tools. &lt;code&gt;curl cht.sh&lt;/code&gt; offers an extensive collection of cheat sheets, making it easier to find the information you need without leaving the terminal.&lt;/p&gt;

&lt;p&gt;To access a cheat sheet, use the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl cht.sh/[Tool or Language]

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

&lt;/div&gt;



&lt;p&gt;Replace &lt;code&gt;[Tool or Language]&lt;/code&gt; with the name of the programming language or tool you want a cheat sheet for. For example, to get a cheat sheet for Git, use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl cht.sh/git

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

&lt;/div&gt;



&lt;p&gt;Get information about &lt;code&gt;aws-cli&lt;/code&gt; or &lt;code&gt;gcloud&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl cht.sh/aws
curl cht.sh/gcloud

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

&lt;/div&gt;



&lt;p&gt;You can get language-specific syntax quickly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl cht.sh/go/web-server
curl cht.sh/rust/loops
curl cht.sh/typescript/generic-type

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

&lt;/div&gt;



&lt;p&gt;There can be many examples since this command will provide concise and relevant information about the language or tool, including commonly used commands, syntax, and examples. If you need assistance or more options, you can use the &lt;code&gt;:help&lt;/code&gt; feature like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl cht.sh/:help

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Weather Information
&lt;/h2&gt;

&lt;p&gt;Are you curious about the current weather in your city or planning a trip and need to check the forecast? The &lt;code&gt;curl&lt;/code&gt; command and &lt;code&gt;wttr.in&lt;/code&gt; can be your go-to tool.&lt;/p&gt;

&lt;p&gt;To get the weather information for a specific location, type the following command into your terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl wttr.in/[Location]

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

&lt;/div&gt;



&lt;p&gt;Replace &lt;code&gt;[Location]&lt;/code&gt; with the name of your city or the location you want to check. For instance, to get the weather in Amsterdam, you would type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl wttr.in/Amsterdam

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

&lt;/div&gt;



&lt;p&gt;This command will return a detailed weather report, including current conditions, temperature, humidity, wind speed, and more. If you need help or want to explore additional options, you can use the &lt;code&gt;:help&lt;/code&gt; feature by appending it to the URL like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--0poI5HRF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://techhub.iodigital.com/articles/i-dont-want-to-leave-my-terminal/weather.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0poI5HRF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://techhub.iodigital.com/articles/i-dont-want-to-leave-my-terminal/weather.png" title="Amsterdam weather" alt="Amsterdam weather" width="800" height="485"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl wttr.in/:help

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Cryptocurrency Information
&lt;/h2&gt;

&lt;p&gt;Cryptocurrency markets are known for their rapid fluctuations, and staying updated on the latest prices and trends is essential for traders and enthusiasts. &lt;code&gt;curl rate.sx&lt;/code&gt; is a handy tool that allows you to retrieve cryptocurrency information directly from the terminal.&lt;/p&gt;

&lt;p&gt;To check the price of a specific cryptocurrency on a specific date, use the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl rate.sx/[Token]@[Date]

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

&lt;/div&gt;



&lt;p&gt;Replace &lt;code&gt;[Token]&lt;/code&gt; with the cryptocurrency symbol or name and &lt;code&gt;[Date]&lt;/code&gt; with the date you want to check. For example, to get the price of XRP on October 1st, you can use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl rate.sx/btc
curl rate.sx/xrp@2023-10-01

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

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--BnPpFrGH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://techhub.iodigital.com/articles/i-dont-want-to-leave-my-terminal/crypto.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BnPpFrGH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://techhub.iodigital.com/articles/i-dont-want-to-leave-my-terminal/crypto.png" title="BTC daily" alt="BTC daily chart" width="800" height="615"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This command will provide historical data for the selected cryptocurrency on the specified date.&lt;/p&gt;

&lt;p&gt;To seek help or explore additional options, use the &lt;code&gt;:help&lt;/code&gt; feature as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl rate.sx/:help

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Authorship
&lt;/h2&gt;

&lt;p&gt;It's important to acknowledge the creator of these useful tools. All the tools mentioned in this article were built by &lt;a href="https://twitter.com/igor_chubin"&gt;@igor_chubin&lt;/a&gt;, who has contributed significantly to the open-source community by creating accessible and efficient terminal-based utilities.&lt;/p&gt;

&lt;p&gt;In conclusion, these terminal-based tools, offer quick and convenient ways to access weather information, cheat sheets for various tools and languages, and cryptocurrency data. Mastering these commands and exploring their capabilities can enhance your command-line skills and streamline your daily tasks. So, embrace the terminal's power and make your computing experience more efficient and informative.&lt;/p&gt;

</description>
      <category>terminal</category>
      <category>development</category>
      <category>geek</category>
    </item>
    <item>
      <title>Speedup unit tests development in Angular with NG-Mocks</title>
      <dc:creator>iO</dc:creator>
      <pubDate>Wed, 04 Oct 2023 00:00:00 +0000</pubDate>
      <link>https://dev.to/iodigital-com/speedup-unit-tests-development-in-angular-with-ng-mocks-51b8</link>
      <guid>https://dev.to/iodigital-com/speedup-unit-tests-development-in-angular-with-ng-mocks-51b8</guid>
      <description>&lt;h2&gt;
  
  
  Why does it take so much time to create unit tests?
&lt;/h2&gt;

&lt;p&gt;Unit tests are an important part of any application. However, the development of robust unit tests takes time. One of the things that takes a lot of that time is mocking all the dependencies. Angular CLI doesn't help you a lot, because it only generates the initial &lt;code&gt;.spec&lt;/code&gt; file with default TestBed configuration. So how can you speed up unit test development in Angular?&lt;/p&gt;

&lt;h2&gt;
  
  
  Mock the dependencies with NG-Mocks library
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://ng-mocks.sudo.eu/"&gt;NG-Mocks&lt;/a&gt; is a testing library that helps to mock all the dependencies. It has plenty of helper functions. I would like to show you two of them that might help you the most.&lt;/p&gt;

&lt;h3&gt;
  
  
  Speedup TestBed module configuration with MockProvider function
&lt;/h3&gt;

&lt;p&gt;Each component or service might depend on other components and services. Here's an example of a service that depends on HttpClient, Router and OAuthService:&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="nd"&gt;Injectable&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;providedIn&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="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AuthService&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="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HttpClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;router&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="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;oauthService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;OAuthService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configureSSO&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="cm"&gt;/**
     * OAuthService needs to be configured in order to use its Single Sign-On feature
     */&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nf"&gt;configureSSO&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;oauthService&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="nx"&gt;authConfig&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;oauthService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tokenValidationHandler&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;JwksValidationHandler&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;oauthService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loadDiscoveryDocumentAndTryLogin&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nf"&gt;loginWithSSO&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nf"&gt;logoutWithSSO&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nf"&gt;loginUserAndRedirect&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;Observable&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;boolean&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://localhost:4200/&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;some payload&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nf"&gt;switchMap&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;navigate&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/dashboard&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;of&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="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;Let's implement the test config for that service. The default approach would be to use the &lt;code&gt;TestBed.configureTestingModule()&lt;/code&gt; function to configure all the dependencies. What needs to be passed into that function:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;HttpClientTestingModule&lt;/code&gt;, which configures HttpClientTestingBackend used by HttpClient.&lt;/li&gt;
&lt;li&gt;Mock of &lt;code&gt;OAuthService&lt;/code&gt;.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;AuthService&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="kd"&gt;let&lt;/span&gt; &lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AuthService&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="na"&gt;router&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="kd"&gt;let&lt;/span&gt; &lt;span class="na"&gt;httpMock&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HttpTestingController&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="na"&gt;oAuthService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;OAuthService&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nf"&gt;beforeEach&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;TestBed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configureTestingModule&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;HttpClientTestingModule&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="na"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;provide&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;OAuthService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;useValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;configure&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="na"&gt;loadDiscoveryDocumentAndTryLogin&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="na"&gt;tokenValidationHandler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;JwksValidationHandler&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="nx"&gt;router&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;TestBed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;inject&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="nx"&gt;httpMock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;TestBed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;HttpTestingController&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;oAuthService&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;TestBed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;OAuthService&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;service&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;TestBed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;AuthService&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;At least two tests need to be added. Both of them would contain &lt;code&gt;spyOn&lt;/code&gt; functions to detect when a specific method gets called on an instance of a specific class:&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="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;AuthService&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="kd"&gt;let&lt;/span&gt; &lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AuthService&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="na"&gt;router&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="kd"&gt;let&lt;/span&gt; &lt;span class="na"&gt;httpMock&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HttpTestingController&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="na"&gt;oAuthService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;OAuthService&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nf"&gt;beforeEach&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{...});&lt;/span&gt;

    &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should configure SSO on initialization &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="c1"&gt;// Arrange&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;configureSpy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;spyOn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;oAuthService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;configure&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;tokenValidationHandlerSpy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;spyOnProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;oAuthService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tokenValidationHandler&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;set&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;loadDiscoveryDocumentAndTryLoginSpy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;spyOn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;oAuthService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;loadDiscoveryDocumentAndTryLogin&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="na"&gt;httpClientSpy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HttpClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jasmine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createSpyObj&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;post&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

        &lt;span class="c1"&gt;// Act&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AuthService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;httpClientSpy&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="nx"&gt;oAuthService&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Assert&lt;/span&gt;
        &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;configureSpy&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toHaveBeenCalledWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;authConfig&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tokenValidationHandlerSpy&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;JwksValidationHandler&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
        &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;loadDiscoveryDocumentAndTryLoginSpy&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toHaveBeenCalled&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should send a POST request to the server and navigate to "/dashboard" on success&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="c1"&gt;// Arrange&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;navigateSpy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;spyOn&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;navigate&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Act&lt;/span&gt;
        &lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loginUserAndRedirect&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="c1"&gt;// Assert&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;httpMock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;expectOne&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://localhost:4200/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;flush&lt;/span&gt;&lt;span class="p"&gt;({});&lt;/span&gt; &lt;span class="c1"&gt;// Simulate a successful response&lt;/span&gt;

        &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;navigateSpy&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toHaveBeenCalledWith&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/dashboard&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;Now let's replace the mock of OAuthService in TestBed with NG-Mocks &lt;code&gt;MockProvider&lt;/code&gt; helper 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="nf"&gt;beforeEach&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;TestBed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configureTestingModule&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;HttpClientTestingModule&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="na"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;MockProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;OAuthService&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="p"&gt;});&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;MockProvider(OAuthService)&lt;/code&gt; creates empty dummies as methods of OAuthService which is totally ok for implemented tests.&lt;/p&gt;

&lt;h3&gt;
  
  
  Spy all methods of services using ngMocks.autoSpy
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://ng-mocks.sudo.eu/extra/auto-spy"&gt;ngMocks.autoSpy&lt;/a&gt; is an awesome feature if you want to get rid of all spied methods in your tests. In order to use it in the specific test, it needs to be added on top of the &lt;code&gt;.spec&lt;/code&gt; file:&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;ngMocks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;autoSpy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;jasmine&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;AuthService&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="c1"&gt;//...&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;After that tests need to be updated:&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;ngMocks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;autoSpy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;jasmine&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;AuthService&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="kd"&gt;let&lt;/span&gt; &lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AuthService&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="na"&gt;router&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="kd"&gt;let&lt;/span&gt; &lt;span class="na"&gt;httpMock&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HttpTestingController&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="na"&gt;oAuthService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;OAuthService&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nf"&gt;beforeEach&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;TestBed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configureTestingModule&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="na"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;HttpClientTestingModule&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="na"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;MockProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;OAuthService&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nc"&gt;MockProvider&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="p"&gt;});&lt;/span&gt;

        &lt;span class="nx"&gt;router&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;TestBed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;inject&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="nx"&gt;httpMock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;TestBed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;HttpTestingController&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;oAuthService&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;TestBed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;OAuthService&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;service&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;TestBed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;AuthService&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should configure SSO on initialization &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="c1"&gt;// Assert&lt;/span&gt;
        &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;oAuthService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;configure&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toHaveBeenCalledWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;authConfig&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;oAuthService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tokenValidationHandler&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;JwksValidationHandler&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
        &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;oAuthService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loadDiscoveryDocumentAndTryLogin&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toHaveBeenCalled&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should send a POST request to the server and navigate to "/dashboard" on success&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="c1"&gt;// Act&lt;/span&gt;
        &lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loginUserAndRedirect&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="c1"&gt;// Assert&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;httpMock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;expectOne&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://localhost:4200/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;flush&lt;/span&gt;&lt;span class="p"&gt;({});&lt;/span&gt; &lt;span class="c1"&gt;// Simulate a successful response&lt;/span&gt;

        &lt;span class="nf"&gt;expect&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="nx"&gt;navigate&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toHaveBeenCalledWith&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/dashboard&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;As you can see, now it's a bit less code in the tests.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make tests more maintainable
&lt;/h2&gt;

&lt;p&gt;One more exciting thing from NG-Mocks is a proposal on &lt;a href="https://ng-mocks.sudo.eu/extra/how-to-write-tests"&gt;how to make tests more maintainable&lt;/a&gt;. The idea is to write tests without scoped variables. Basically, specific arrangements need to be applied to specific tests so that each test is self-sufficient and does not rely on scoped variables:&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;ngMocks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;autoSpy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;jasmine&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;AuthService&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="kd"&gt;let&lt;/span&gt; &lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AuthService&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nf"&gt;beforeEach&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;TestBed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configureTestingModule&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="na"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;HttpClientTestingModule&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="na"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;MockProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;OAuthService&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nc"&gt;MockProvider&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="p"&gt;});&lt;/span&gt;

        &lt;span class="nx"&gt;service&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;TestBed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;AuthService&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should configure SSO on initialization &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="c1"&gt;// Arrange&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;oAuthService&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;TestBed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;OAuthService&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Assert&lt;/span&gt;
        &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;oAuthService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;configure&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toHaveBeenCalledWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;authConfig&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;oAuthService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tokenValidationHandler&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;JwksValidationHandler&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
        &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;oAuthService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loadDiscoveryDocumentAndTryLogin&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toHaveBeenCalled&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should send a POST request to the server and navigate to "/dashboard" on success&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="c1"&gt;// Arrange&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;httpMock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;TestBed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;HttpTestingController&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="nx"&gt;TestBed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;inject&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="c1"&gt;// Act&lt;/span&gt;
        &lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loginUserAndRedirect&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="c1"&gt;// Assert&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;httpMock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;expectOne&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://localhost:4200/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;flush&lt;/span&gt;&lt;span class="p"&gt;({});&lt;/span&gt; &lt;span class="c1"&gt;// Simulate a successful response&lt;/span&gt;

        &lt;span class="nf"&gt;expect&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="nx"&gt;navigate&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toHaveBeenCalledWith&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/dashboard&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;As you can see now arrangements are applied to specific tests. That should make them more maintainable as we add other tests into that &lt;code&gt;.spec&lt;/code&gt; file.&lt;/p&gt;

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

&lt;p&gt;I hope NG-Mocks will help you with unit test development and improve your overall developer experience as you create Angular applications. Happy coding!&lt;/p&gt;

</description>
      <category>frontend</category>
      <category>angular</category>
      <category>unittests</category>
    </item>
    <item>
      <title>Generate unit tests using CodiumAI</title>
      <dc:creator>iO</dc:creator>
      <pubDate>Wed, 27 Sep 2023 00:00:00 +0000</pubDate>
      <link>https://dev.to/iodigital-com/generate-unit-tests-using-codiumai-387b</link>
      <guid>https://dev.to/iodigital-com/generate-unit-tests-using-codiumai-387b</guid>
      <description>&lt;h2&gt;
  
  
  What is Unit testing
&lt;/h2&gt;

&lt;p&gt;Unit testing ensures that pieces of code work the way you expect them to. Performing unit tests is designed to be simple, generally, the tests are written in the form of functions that will determine whether a returned value equals the value you were expecting.&lt;/p&gt;

&lt;h2&gt;
  
  
  CodiumAI for unit tests generation
&lt;/h2&gt;

&lt;p&gt;I've recently tried unit tests generation with &lt;a href="https://www.codium.ai/"&gt;CodiumAI&lt;/a&gt;. It's an extension for VS Code and Intellij Idea that is free to use for independent developers. It supports JavaScript, TypeScript and a variety of other programming languages. Let me show you how it works.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to install CodiumAI in VS Code
&lt;/h3&gt;

&lt;p&gt;First of all, you need to install it. CodiumAI supports VS Code. Open VS Code Extensions section, search for "CodiumAI" and install it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xDQ6ie6O--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://techhub.iodigital.com/articles/codiumai-for-unit-tests/codium-ai-install-vscode.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xDQ6ie6O--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://techhub.iodigital.com/articles/codiumai-for-unit-tests/codium-ai-install-vscode.webp" alt="How to install CodiumAI in VS Code" width="346" height="334"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  How to install CodiumAI in IntelliJ Idea
&lt;/h3&gt;

&lt;p&gt;To install CodiumAI in IntelliJ Idea, go to Settings -&amp;gt; Plugins -&amp;gt; search for "CodiumAI", install it and then restart IntelliJ Idea:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--w5eXnC3Z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://techhub.iodigital.com/articles/codiumai-for-unit-tests/codium-ai-install-intellij.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--w5eXnC3Z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://techhub.iodigital.com/articles/codiumai-for-unit-tests/codium-ai-install-intellij.webp" alt="How to install CodiumAI in IntelliJ Idea" width="586" height="213"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  How to generate unit tests
&lt;/h3&gt;

&lt;p&gt;Here's an example of TypeScript code that will be used for unit tests generation:&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="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;userName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&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;class&lt;/span&gt; &lt;span class="nc"&gt;AuthService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;searchUsersByName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="nx"&gt;userName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userName&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userName&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;
  &lt;span class="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;Tests generation with CodiumAI works similarly in VS Code and IntelliJ Idea. Here's how to do it in VS Code:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--PHyiG0Mr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://techhub.iodigital.com/articles/codiumai-for-unit-tests/codiumai-how-to-generate-tests-vscode.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PHyiG0Mr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://techhub.iodigital.com/articles/codiumai-for-unit-tests/codiumai-how-to-generate-tests-vscode.gif" alt="How to generate unit tests with CodiumAI in VS Code" width="800" height="514"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And here's how you can do the same thing in IntelliJ Idea:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--VHvOuR8u--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://techhub.iodigital.com/articles/codiumai-for-unit-tests/codiumai-how-to-generate-tests-intellij.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--VHvOuR8u--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://techhub.iodigital.com/articles/codiumai-for-unit-tests/codiumai-how-to-generate-tests-intellij.gif" alt="How to generate unit tests with CodiumAI in IntelliJ Idea" width="800" height="514"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Starting from here I will use VS Code only for tests generation. It works similarly in IntelliJ Idea.&lt;/p&gt;

&lt;h3&gt;
  
  
  Generate custom unit test
&lt;/h3&gt;

&lt;p&gt;Let's say we need a custom unit test that would make our test users have name and surname separated by space as part of their "userName" string. Example: "Jane Doe". So this test:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;returns an array of users, users should have names that contain name and surname separated by space&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;We can generate that custom test using the Behaviours Coverage section:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--FJyX1TpT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://techhub.iodigital.com/articles/codiumai-for-unit-tests/codiumai-how-to-generate-custom-test.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--FJyX1TpT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://techhub.iodigital.com/articles/codiumai-for-unit-tests/codiumai-how-to-generate-custom-test.gif" alt="How to generate custom unit test with CodiumAI" width="800" height="566"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, a custom unit test is generated and added to the list of tests.&lt;/p&gt;

&lt;h3&gt;
  
  
  Run all unit tests
&lt;/h3&gt;

&lt;p&gt;Time to run the tests!&lt;/p&gt;

&lt;p&gt;That might require you to have one of the testing libraries (Jest, Mocha, Karma Jasmine, etc.) installed and configured in your project. The only available Test Framework from the Configuration section that worked without any configuration with my simple code example is "React Testing Library". Here's how it works:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2Qi0vuVc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://techhub.iodigital.com/articles/codiumai-for-unit-tests/codiumai-how-to-run-tests.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2Qi0vuVc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://techhub.iodigital.com/articles/codiumai-for-unit-tests/codiumai-how-to-run-tests.gif" alt="How to run all unit tests with CodiumAI" width="800" height="566"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Fix the code with CodiumAI suggestions
&lt;/h3&gt;

&lt;p&gt;You might find one of the tests broken, due to edge case. CodiumAI would suggest you to fix the code. Here's one of CodiumAI's suggestions that could fix several edge cases:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--KRq7Oemm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://techhub.iodigital.com/articles/codiumai-for-unit-tests/codiumai-code-suggestion.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KRq7Oemm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://techhub.iodigital.com/articles/codiumai-for-unit-tests/codiumai-code-suggestion.webp" alt="Check CodiumAI code suggestions" width="800" height="673"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now it's time to apply the code suggestion:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--U_GYa9cn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://techhub.iodigital.com/articles/codiumai-for-unit-tests/codiumai-how-to-apply-code-suggestion.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--U_GYa9cn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://techhub.iodigital.com/articles/codiumai-for-unit-tests/codiumai-how-to-apply-code-suggestion.gif" alt="How to apply CodiumAI code suggestion" width="800" height="566"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Suggestion is applied, and all unit tests are generated. Now you can copy-paste unit tests into your project.&lt;/p&gt;

&lt;p&gt;That was the last feature that I wanted to share with you. &lt;a href="https://github.com/Codium-ai/codiumai-vscode-release"&gt;There are many others that you can try as well&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  CodiumAI is not perfect
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;You should always double-check the tests CodiumAI generates&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's the warning from the CodiumAI team. They've made an amazing tool to make you more productive as you develop unit tests. But don't forget to check what AI generated for you. Happy coding!&lt;/p&gt;

</description>
      <category>frontend</category>
      <category>unittests</category>
      <category>vscode</category>
      <category>intellij</category>
    </item>
    <item>
      <title>ECMAScript Explicit Resource Management early implementation in TypeScript 5.2</title>
      <dc:creator>iO</dc:creator>
      <pubDate>Mon, 18 Sep 2023 09:18:02 +0000</pubDate>
      <link>https://dev.to/iodigital-com/ecmascript-explicit-resource-management-early-implementation-in-typescript-52-3bl5</link>
      <guid>https://dev.to/iodigital-com/ecmascript-explicit-resource-management-early-implementation-in-typescript-52-3bl5</guid>
      <description>&lt;p&gt;This article on "ECMAScript Explicit Resource Management Early Implementation in TypeScript 5.2" is for developers who are interested in managing resources explicitly in their JavaScript programs. It explains how this feature works and how it can simplify resource management code.&lt;/p&gt;

&lt;p&gt;Explicit Resource Management indicates a system whereby the lifetime of a “resource” is managed explicitly by the user. This can be done either imperatively (by directly calling a method like &lt;code&gt;Symbol.dispose&lt;/code&gt;) or declaratively (through a block-scoped declaration like &lt;code&gt;using&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;A "Resource" is something that has a specific lifespan. When that lifespan is over, the program needs to perform a specific action, like closing a file or freeing up memory, so that the program can continue running smoothly. Examples of resources include file handles and network sockets.&lt;/p&gt;

&lt;p&gt;At the moment of writing, the ECMAScript &lt;a href="https://github.com/tc39/proposal-explicit-resource-management"&gt;proposal&lt;/a&gt; for Explicit Resource Management is at &lt;a href="https://tc39.es/process-document/"&gt;stage 3&lt;/a&gt; which means it will be launched within a few months. However, the TypeScript community has already implemented it in TypeScript &lt;a href="https://devblogs.microsoft.com/typescript/announcing-typescript-5-2-beta/#using-declarations-and-explicit-resource-management"&gt;version 5.2&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This implementation will add a feature to the JavaScript language similar to other languages such as C# &lt;code&gt;using&lt;/code&gt; syntax [&lt;a href="https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/statements#1314-the-using-statement"&gt;link&lt;/a&gt;], Python with syntax [&lt;a href="https://docs.python.org/3/reference/compound_stmts.html#the-with-statement"&gt;link&lt;/a&gt;], or try [&lt;a href="https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html"&gt;link&lt;/a&gt;] in Java.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// C# example of the 'using' statement&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;OpenRead&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;“&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="err"&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;// use the file&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;// file.dispose() method has been called now to release resources.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In general, without using this new feature, a conventional free-up pattern is by using the &lt;code&gt;try/finally&lt;/code&gt; syntax:&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="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;obj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;someResource&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;release&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;// or any other clean-up method provided by the resource&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now this can be written in this way in TypeScript 5.2:&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;using&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;someResource&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the program's execution goes outside of the block that the &lt;code&gt;obj&lt;/code&gt; has been defined, the object will no longer be needed and the &lt;code&gt;dispose&lt;/code&gt; method will be called.&lt;/p&gt;

&lt;p&gt;TypeScript introduced an interface called &lt;code&gt;Disposable&lt;/code&gt; that can be used to implement the &lt;code&gt;using&lt;/code&gt; statement. To do this, your class should implement the &lt;code&gt;Disposable&lt;/code&gt; interface, which requires the class to have a &lt;code&gt;[Symbol.dispose]&lt;/code&gt; method. This method is responsible for freeing up any resources used by the class when it is no longer needed. By implementing the &lt;code&gt;Disposable&lt;/code&gt; interface, the TypeScript compiler knows how to translate the &lt;code&gt;using&lt;/code&gt; syntax into a &lt;code&gt;try/finally&lt;/code&gt; block that the current JavaScript engine can understand.&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="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyResource&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;Disposable&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;Symbol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dispose&lt;/span&gt;&lt;span class="p"&gt;]()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// clean-up logic&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;using&lt;/span&gt; &lt;span class="nx"&gt;obj&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;MyResource&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or, if you have a &lt;strong&gt;function&lt;/strong&gt; instead of a &lt;strong&gt;class&lt;/strong&gt;, your function should return an object that has the &lt;code&gt;Symbol.dispose&lt;/code&gt; method:&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;myResource&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;Disposable&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;Symbol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dispose&lt;/span&gt;&lt;span class="p"&gt;](){&lt;/span&gt; &lt;span class="cm"&gt;/* clean-up logic */&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;using&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;myResource&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Good to know that after compilation, the above code will become something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class MyResource {
  [Symbol.dispose]() {
    // clean-up logic
  }
}
var obj
const env_1 = { stack: [], error: void 0, hasError: false }
try {
  obj = __addDisposableResource(env_1, MyResource(), false)
} catch (e_1) {
  env_1.error = e_1
  env_1.hasError = true
} finally {
  __disposeResources(env_1)
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see here the TypeScript compiler has compiled the &lt;code&gt;using&lt;/code&gt; statement into the traditional &lt;code&gt;try/finally&lt;/code&gt; pattern that I mentioned before and of course, with some simple functions to manage the allocated resources(&lt;code&gt;__addDisposableResource&lt;/code&gt; and &lt;code&gt;__disposeResources&lt;/code&gt; will be generated by the compiler in the output file).&lt;/p&gt;

&lt;h2&gt;
  
  
  Nested scopes and using
&lt;/h2&gt;

&lt;p&gt;Objects can be created in the nested scopes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function work() {
    using a = resource();
    {
        using b = resource();
    }
}

work();
// b.dispose()
// a.dispose()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this case, after leaving the scope, the &lt;code&gt;dispose&lt;/code&gt; methods will be called respectively.&lt;/p&gt;

&lt;h2&gt;
  
  
  DisposableStack class
&lt;/h2&gt;

&lt;p&gt;Implementing the &lt;code&gt;Symbol.dispose&lt;/code&gt; method can be a good pattern to use when you have a class or function that needs to be disposed of at some point. However, there are a few potential problems to consider. Firstly, implementing the &lt;code&gt;dispose&lt;/code&gt; method can add unnecessary abstraction to your code in some cases, which can make it more difficult to read and understand. Secondly, many existing libraries and modules may not have implemented the &lt;code&gt;dispose&lt;/code&gt; method yet, in which case you will need to create a wrapper around them and implement the clean-up code (dispose) yourself. Keep these considerations in mind when deciding whether or not to implement the &lt;code&gt;dispose&lt;/code&gt; method in your code.&lt;/p&gt;

&lt;p&gt;Here is an example from the TypeScript blog:&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="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TempFile&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;Disposable&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&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="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&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="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;handle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;openSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;w+&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;// other methods&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;Symbol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dispose&lt;/span&gt;&lt;span class="p"&gt;]()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Close the file and delete it.&lt;/span&gt;
        &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;closeSync&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="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;unlinkSync&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="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;doSomeWork&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;using&lt;/span&gt; &lt;span class="nx"&gt;file&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;TempFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;path-to-file&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This class has been implemented (with all of its sophistication that it might have later) to only have a &lt;code&gt;Symbol.dispose&lt;/code&gt; method that would be called by the engine. However, TypeScript 5.2 introduces the &lt;code&gt;DisposableStack&lt;/code&gt; class, which makes it simpler to use Explicit Resource Management.&lt;/p&gt;

&lt;p&gt;Here is how:&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;doSomeWork&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;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;path-to-file&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;file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;openSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;w+&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;using&lt;/span&gt; &lt;span class="nx"&gt;cleanup&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;DisposableStack&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;cleanup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;defer&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;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;closeSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;unlinkSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// use file...&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’ve created an instance of &lt;code&gt;DisposableStack&lt;/code&gt; right after opening the file; It has the defer method that accepts a callback function which is suitable for clean-up that will be invoked at the moment of disposing the &lt;code&gt;cleanup&lt;/code&gt; object.&lt;br&gt;
After the above code is compiled, the resulting JavaScript code will include a &lt;code&gt;finally&lt;/code&gt; block. This &lt;code&gt;finally&lt;/code&gt; block will call the callback function that was passed to the defer method. This ensures that the callback function is always executed, even if an error occurs in the try block.&lt;/p&gt;
&lt;h2&gt;
  
  
  Async dispose method
&lt;/h2&gt;

&lt;p&gt;Sometimes for your &lt;strong&gt;dispose&lt;/strong&gt; logic you might need to use asynchronous operations using &lt;code&gt;async/await&lt;/code&gt;. In this case, you can simply use the &lt;code&gt;await&lt;/code&gt; before the &lt;code&gt;using&lt;/code&gt; keyword:&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="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;using&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OpenFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this case, in the &lt;code&gt;OpenFile&lt;/code&gt; function or class, you should implement the dispose method using &lt;code&gt;Symbol.asyncDispose&lt;/code&gt; like below:&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="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;OpenFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;AsyncDisposable&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;file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;Symbol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;asyncDispose&lt;/span&gt;&lt;span class="p"&gt;]()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;anAsyncOperation&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;h2&gt;
  
  
  Polyfills
&lt;/h2&gt;

&lt;p&gt;Because this feature is so recent, most runtimes will not support it natively. To use it, you will need runtime polyfills for the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Symbol.dispose&lt;/li&gt;
&lt;li&gt;Symbol.asyncDispose&lt;/li&gt;
&lt;li&gt;DisposableStack&lt;/li&gt;
&lt;li&gt;AsyncDisposableStack&lt;/li&gt;
&lt;li&gt;SuppressedError&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;Symbol.dispose&lt;/code&gt; and &lt;code&gt;Symbol.asyncDispose&lt;/code&gt; can be polyfilled like this:&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="nb"&gt;Symbol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dispose&lt;/span&gt; &lt;span class="o"&gt;??=&lt;/span&gt; &lt;span class="nc"&gt;Symbol&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Symbol.dispose&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;Symbol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;asyncDispose&lt;/span&gt; &lt;span class="o"&gt;??=&lt;/span&gt; &lt;span class="nc"&gt;Symbol&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Symbol.asyncDispose&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;The compilation target in the &lt;code&gt;tsconfig&lt;/code&gt; file should be es2022 or below and the library setting to either include &lt;code&gt;"esnext"&lt;/code&gt; or &lt;code&gt;"esnext.disposable"&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"compilerOptions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"es2022"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"lib"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"es2022"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"esnext.disposable"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dom"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For more information on this feature, take a look at the work on &lt;a href="https://github.com/microsoft/TypeScript/pull/54505"&gt;GitHub&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>javascript</category>
    </item>
    <item>
      <title>TIL: The CSS order Property Requires Each Grid/Flex Item to be ordered</title>
      <dc:creator>iO</dc:creator>
      <pubDate>Tue, 12 Sep 2023 00:00:00 +0000</pubDate>
      <link>https://dev.to/iodigital-com/til-the-css-order-property-requires-each-gridflex-item-to-be-ordered-35c3</link>
      <guid>https://dev.to/iodigital-com/til-the-css-order-property-requires-each-gridflex-item-to-be-ordered-35c3</guid>
      <description>&lt;p&gt;I was today years old when I learned something new about the CSS &lt;code&gt;order&lt;/code&gt; property. I didn't learn about the existence of the property today, mind you. I just learned about a constriction which, in my opinion, makes it a little bit less usable. You see... when you want to &lt;code&gt;order&lt;/code&gt; a &lt;code&gt;flex&lt;/code&gt; or &lt;code&gt;grid&lt;/code&gt; item, you have to &lt;code&gt;order&lt;/code&gt; all of them. You can't just &lt;code&gt;order&lt;/code&gt; one of them. Let me show you what I mean.&lt;/p&gt;

&lt;p&gt;According to its &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/order#formal_definition"&gt;formal definition on MDN&lt;/a&gt;, &lt;code&gt;order&lt;/code&gt;'s inital value is &lt;code&gt;0&lt;/code&gt; (zero).&lt;/p&gt;

&lt;p&gt;When you &lt;code&gt;order&lt;/code&gt; an item, it will be visually ordered according to its value compared to its siblings's &lt;code&gt;order&lt;/code&gt; value. Any item with the same &lt;code&gt;order&lt;/code&gt; value will be visually ordered by its place in the DOM. Have a look at this example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
  &lt;span class="nc"&gt;.flex-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="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;class=&lt;/span&gt;&lt;span class="s"&gt;"flex-container"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"flex-item"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Item 1&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- order: 0 // default --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"flex-item"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Item 2&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- order: 0 // default --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"flex-item"&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"order: 1"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Item 3&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- order: 1 // explicitly assigned --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;&amp;lt;!-- Expected visual result --&amp;gt;&lt;/span&gt;
Item 1 Item 3 Item 2

&lt;span class="c"&gt;&amp;lt;!-- Actual visual result --&amp;gt;&lt;/span&gt;
Item 1 Item 2 Item 3

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

&lt;/div&gt;



&lt;p&gt;You would expect &lt;code&gt;Item 2&lt;/code&gt; and &lt;code&gt;Item 3&lt;/code&gt; to be visually swapped. However, they are not swapped, despite &lt;code&gt;Item 3&lt;/code&gt; having an &lt;code&gt;order: 1&lt;/code&gt;. Sort of like zero-index Arrays, 1 comes after 0. If you want to actually &lt;code&gt;order&lt;/code&gt; one item, you have to &lt;code&gt;order&lt;/code&gt; all of them. This is true for both &lt;code&gt;flex&lt;/code&gt; and &lt;code&gt;grid&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To order any element, you have to order all of them. This is not a big problem, as you usually generate lists of items with a loop (like &lt;code&gt;v-for&lt;/code&gt; in Vue or &lt;code&gt;*ngFor&lt;/code&gt; in Angular or &lt;code&gt;Array.prototype.map&lt;/code&gt; in JSX). You can easily retrieve the current iteration index and use that for &lt;code&gt;order&lt;/code&gt;. It's just weird to me that it is necessary. I would expect &lt;code&gt;order&lt;/code&gt; to have a default value of &lt;code&gt;auto&lt;/code&gt;, making the visual order default to the DOM order.&lt;/p&gt;

&lt;p&gt;So, to make sure that &lt;code&gt;Item 3&lt;/code&gt; is visually ordered &lt;em&gt;before&lt;/em&gt; &lt;code&gt;Item 2&lt;/code&gt;, you have to explicitly set &lt;code&gt;order: 2&lt;/code&gt; on &lt;code&gt;Item 2&lt;/code&gt; (or any value higher than &lt;code&gt;1&lt;/code&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"flex-container"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"flex-item"&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"order: 1"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Item 1&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"flex-item"&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"order: 2"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Item 2&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"flex-item"&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"order: 1"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Item 3&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;&amp;lt;!-- Visual result --&amp;gt;&lt;/span&gt;
Item 1 Item 3 Item 2

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

&lt;/div&gt;



&lt;p&gt;No big deal, but something to keep in mind when you want to use the &lt;code&gt;order&lt;/code&gt; property.&lt;/p&gt;

</description>
      <category>css</category>
      <category>flex</category>
      <category>grid</category>
    </item>
    <item>
      <title>Consistent CSS spacing is hard, but it does not have to be</title>
      <dc:creator>iO</dc:creator>
      <pubDate>Tue, 15 Aug 2023 00:00:00 +0000</pubDate>
      <link>https://dev.to/iodigital-com/consistent-css-spacing-is-hard-but-it-does-not-have-to-be-e95</link>
      <guid>https://dev.to/iodigital-com/consistent-css-spacing-is-hard-but-it-does-not-have-to-be-e95</guid>
      <description>&lt;p&gt;I've been around for a while, and it has been rewarding to witness CSS develop into a powerful styling tool. Many of the complexities we used to face are now being solved by single CSS properties (tip of the hat to box-shadow), while newly added capabilities give us powers far beyond our wildest dreams. We can whip out sub grids, container queries, and page transitions without breaking a sweat, so we can finally focus on the finer design details.&lt;/p&gt;

&lt;p&gt;Not all of our past problems have been laid to rest though, which is what prompted me to write this article. One of the oldest and most common sets of CSS properties are still causing major headaches, and our projects suffer the consequences. &lt;strong&gt;Spacing is hard&lt;/strong&gt; , and the repercussions (i.e. increased maintenance complexity and time/money spent on bugs) are grim.&lt;/p&gt;

&lt;p&gt;Before I dig into two simple guidelines that will alleviate the most pressing spacing concerns, let's find out why CSS spacing is such a minefield.&lt;/p&gt;

&lt;h2&gt;
  
  
  Whack-a-mole problems
&lt;/h2&gt;

&lt;p&gt;When CSS spacing goes wrong, it creates a &lt;a href="https://idioms.thefreedictionary.com/whack-a-mole"&gt;whack-a-mole&lt;/a&gt; situation. For those who don't know, whack-a-mole is an old arcade game in which moles randomly rise up from holes in the ground. Players need to hit them with a hammer, doing so will cause the mole to disappear, while another one pops up in a different location.&lt;/p&gt;

&lt;p&gt;In development terms: squashing one bug will invariably introduce a new one. The client reports an issue and the following code dive will leave a slightly confused developer pondering. He'll make a change and all will appear dandy, but unknowingly he created a new bug. When the new bug is discovered another ticket is created, resulting in increased frustration of all parties involved. And that cycle repeats itself. Ad infinitum. In other words: whack-a-mole.&lt;/p&gt;

&lt;p&gt;There is no single reason why spacing is so vulnerable to this type of slip-up, but there are some obvious causes why we keep tripping up:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Developers don't possess the needed design DNA to spot spacing issues.&lt;/li&gt;
&lt;li&gt;Spacing is everywhere, so the opportunities to mess up are plentiful.&lt;/li&gt;
&lt;li&gt;It's easy to mend spacing problems with crappy code, and people looking at the resulting layout will be none the wiser.&lt;/li&gt;
&lt;li&gt;Beware of the cascade. A trivial rule that adds 12px of vertical padding to a plain paragraph can ruin an entire project.&lt;/li&gt;
&lt;li&gt;There are several properties that create spacing, and a handful of context-specific hacks on top of that. Picking the right solution isn't always obvious.&lt;/li&gt;
&lt;li&gt;The setup of most projects is inadequate to assess layout impact, meaning developers are working in the dark (and just hoping for the best).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;CSS is free-form in the sense that there are always various ways to solve a problem. No two solutions are ever truly equal though, so let's run through the most common spacing options we have at our disposal.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using the right tool for the job
&lt;/h2&gt;

&lt;p&gt;There are various ways to create space, each with its own strengths and weaknesses, but it generally comes down to these three:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;padding&lt;/strong&gt; : creates space within an element. Works on pretty much everything, but top/bottom paddings are ineffective on inline elements.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;gap&lt;/strong&gt; : creates consistent spacing between the direct children of an element. It only works with grid and flexbox.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;margin&lt;/strong&gt; : creates space around elements. Works on pretty much everything, but top/bottom margins are ineffective on inline elements.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I am a big fan of gap, but its one big limitation is that it can't be overruled for specific children. It is perfect for creating a fixed grid, yet it is rigid, and changing one specific space is hacky. This severely limits its use.&lt;/p&gt;

&lt;p&gt;Paddings are useful for creating space between an element's edge and its contents. They come in handy when an element has a clear visual definition (think backgrounds, borders, box-shadow, ...), when you want to expand the interactive area of an element (hover, click, ...), or when you want to reserve space for absolutely positioned elements.&lt;/p&gt;

&lt;p&gt;Margin is the most versatile, and the go-to property when you need flexible space between elements. You should only switch out margin to a better-fitting property when it adds extra value. We are of course talking CSS and there will be specific use cases that require craftier solutions. As a starting point though, margin is your best bet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two Simple guidelines
&lt;/h2&gt;

&lt;p&gt;It is very difficult to recover from a spacing setup mid-crumble, and usually, the only way out is to rework the entire deal from the ground up. So how can we shield ourselves from these types of reputation-damaging solutions? Well, if you stick to the following guidelines, you are certain to end up with far fewer problems than before.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. A space is defined by a single CSS value
&lt;/h3&gt;

&lt;p&gt;DRY and/or the single-responsibility principle, I'm not quite sure what fits best here, but the guideline should be clear enough. If you need to create space, make sure it is defined and can be changed from a single property value in your CSS. That means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;no collapsing or adjoining margins&lt;/li&gt;
&lt;li&gt;no adjoining margins and paddings&lt;/li&gt;
&lt;li&gt;no adjoining paddings&lt;/li&gt;
&lt;li&gt;no borders in the same color as the background (or other, equally ugly hacks)&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Just none of it. When you see this happening, stop yourself and take an extra minute to figure out a better solution (and if you can't find one, flag to your PM that the CSS needs a more profound looking at).&lt;/p&gt;

&lt;h3&gt;
  
  
  2. The parent controls the layout of its children
&lt;/h3&gt;

&lt;p&gt;This guideline may sound a bit more abstract, but it's not that difficult to grasp. As we construct pages from reusable building blocks, our CSS needs to mimic that setup. That's why &lt;strong&gt;a component should never come with built-in spacing&lt;/strong&gt;. There is no way to predict in which context a component will be used, so we really shouldn't be making any assumptions on the spacing it needs.&lt;/p&gt;

&lt;p&gt;It's the parent element that decides the position of a component. That's the only one who knows how its children should be arranged. Layout systems like grid and flexbox allow us to do exactly that (gap goes on the parent element), if you're using margins then the child selector is your friend. And if you're using utility class-based frameworks, the burden shifts to the templating logic, which should make sure no excess spacing exists (for example by removing spacing classes on the first/last child).&lt;/p&gt;

&lt;p&gt;It's a simple enough guideline, but it's also an easy one to sin against, not in the least because component visualization tools (StoryBook and the like) give few indications of a component's factual boundaries. One thing that helped me tremendously was to include crop marks (the red crosses in the image below). They are relatively unobtrusive, yet a quick glance will reveal if unwanted spacing exists.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--iVU8dmj4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://techhub.iodigital.com/articles/consistent-css-spacing-is-hard/cropmark.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--iVU8dmj4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://techhub.iodigital.com/articles/consistent-css-spacing-is-hard/cropmark.jpg" alt="Example of crop marks" width="800" height="268"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;And that's all there is to it really. As long as you make sure that components don't bear excess spacing and that a space can be changed from a single value in the CSS, there's not a lot that can go wrong. Working with a robust component visualization tool (rather than working directly on templates) will provide extra protection against mishaps, but let's focus on prevention first.&lt;/p&gt;

&lt;p&gt;With all the tools we have, it's a little embarrassing to have to face clients about something as basic and prevalent as spacing. There really is no excuse to have this be a problem in 2023, but all too often I come across projects that hide a convoluted layout mess, so there is clearly a need to address this. With a little care and focus, I'm sure we can make this a problem of the past.&lt;/p&gt;

</description>
      <category>frontend</category>
      <category>css</category>
    </item>
  </channel>
</rss>
