<?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: Ilia Mikhailov</title>
    <description>The latest articles on DEV Community by Ilia Mikhailov (@codechips).</description>
    <link>https://dev.to/codechips</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F307616%2F70f57415-a4d6-4f69-814e-db0ae5ec9e1f.jpg</url>
      <title>DEV Community: Ilia Mikhailov</title>
      <link>https://dev.to/codechips</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/codechips"/>
    <language>en</language>
    <item>
      <title>Tailwind CSS and SvelteKit - The Easy Way</title>
      <dc:creator>Ilia Mikhailov</dc:creator>
      <pubDate>Tue, 26 Oct 2021 00:00:00 +0000</pubDate>
      <link>https://dev.to/codechips/tailwind-css-and-sveltekit-the-easy-way-16j3</link>
      <guid>https://dev.to/codechips/tailwind-css-and-sveltekit-the-easy-way-16j3</guid>
      <description>&lt;p&gt;Long story short, I needed to integrate Tailwind CSS into a new SvelteKit project and searched around on the Internet on how to do it. The first option is to use &lt;a href="https://github.com/svelte-add/tailwindcss"&gt;svelte-add&lt;/a&gt;, but it won't work unless you start off with a fresh project. The other common way to do it is some variation of my old &lt;a href="https://codechips.me/sapper-with-postcss-and-tailwind/"&gt;Sapper and Tailwind solution&lt;/a&gt;. While it's still valid today turns out there is a &lt;strong&gt;much simpler way&lt;/strong&gt; to do it.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This post was originally published at &lt;a href="https://codechips.me"&gt;codechips.me&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Create new SvelteKit project
&lt;/h2&gt;

&lt;p&gt;When you create a new SvelteKit project with &lt;code&gt;npm init svelte@next my-app&lt;/code&gt; command you have a few options to choose from. The choices you make will affect how easy it will be to integrate Tailwind CSS. For example, here are the choices that I made during my project creation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Jq_bj9J7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/codechips/image/upload/v1635261893/posts/sveltekit-project-setup_dgubvu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Jq_bj9J7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/codechips/image/upload/v1635261893/posts/sveltekit-project-setup_dgubvu.png" alt="SvelteKit Project Setup" width="802" height="231"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see in the screenshot above I chose TypeScript support. In order to get TypeScript working in Svelte you have to use &lt;a href="https://github.com/sveltejs/svelte-preprocess"&gt;svelte-preprocess&lt;/a&gt; library. That library is also the key to get Tailwind working in our project. Its job is to support and process many different languages in Svelte templates and it also has built-in support for PostCSS that Tailwind is based on.&lt;/p&gt;

&lt;p&gt;Don't worry if you didn't choose TypeScript as an option in your project. You can install &lt;code&gt;svelte-preprocess&lt;/code&gt; separately and implement it in your &lt;code&gt;svelte.config.js&lt;/code&gt; 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="c1"&gt;// svelte.config.js&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;preprocess&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;svelte-preprocess&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cm"&gt;/** @type {import('@sveltejs/kit').Config} */&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="c1"&gt;// Consult https://github.com/sveltejs/svelte-preprocess&lt;/span&gt;
  &lt;span class="c1"&gt;// for more information about preprocessors&lt;/span&gt;
  &lt;span class="na"&gt;preprocess&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;preprocess&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;

  &lt;span class="na"&gt;kit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// hydrate the &amp;lt;div id="svelte"&amp;gt; element in src/app.html&lt;/span&gt;
    &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#svelte&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="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that we have PostCSS support in place let's move on and install and configure Tailwind CSS and other required libraries.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install Tailwind CSS and friends
&lt;/h2&gt;

&lt;p&gt;First things first, we need to install Tailwind and supporting NPM packages and Tailwind configuration file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ npm add -D tailwindcss autoprefixer postcss-load-config
$ npx tailwindcss init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For this to work we also need to create PostCSS configuration in our project directory.&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;// postcss.config.js&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="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;autoprefixer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
    &lt;span class="na"&gt;tailwindcss&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;We are almost ready to go, but if you start you project you will get an exception that has the following error message in it somewhere.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Instead rename tailwind.config.js to end in .cjs, 
change the requiring code to use import(), 
or remove "type" : "module" from [...]/package.json.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What Node basically says is that it expects a different type of module. The solution is simple. Change extensions for Tailwind and PostCSS configs from &lt;code&gt;.js&lt;/code&gt; to &lt;code&gt;.cjs&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In order for Tailwind to know what CSS it needs to purge during production builds you have to add purge patterns to your Tailwind config.&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;// tailwind.config.cjs&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="c1"&gt;// add this section&lt;/span&gt;
  &lt;span class="na"&gt;purge&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="s1"&gt;./src/**/*.html&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;./src/**/*.svelte&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;darkMode&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="c1"&gt;// or 'media' or 'class'&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;extend&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;variants&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;extend&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;plugins&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;We now have a correct Tailwind and PostCSS setup in place. Let's use it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Import your CSS
&lt;/h2&gt;

&lt;p&gt;The last step is to create a Tailwind CSS file and import it in the project. Create an &lt;code&gt;app.css&lt;/code&gt; file under &lt;code&gt;src&lt;/code&gt; directory, if it's not there already, and add the following to it.&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="k"&gt;@tailwind&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;@tailwind&lt;/span&gt; &lt;span class="n"&gt;components&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;@tailwind&lt;/span&gt; &lt;span class="n"&gt;utilities&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;@layer&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;h2&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;h3&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;h4&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="err"&gt;@apply&lt;/span&gt; &lt;span class="err"&gt;font-bold;&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 step is to require that file in your template. Here I added it to my main layout file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;// __layout.svelte

&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"ts"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../app.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Header&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;$lib/components/Header.svelte&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Login&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;$lib/components/Login.svelte&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;auth&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;$lib/auth&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;main&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;Header&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;{$auth.company?.name}&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;"max-w-4xl mx-auto py-8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    {#if $auth.loading}
      &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;"text-gray-600"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Loading ...&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    {:else if $auth.user}
      &lt;span class="nt"&gt;&amp;lt;slot&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    {:else}
      &lt;span class="nt"&gt;&amp;lt;Login&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    {/if}
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/main&amp;gt;&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;If you start the application now the Tailwind CSS should kick in.&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 &lt;/span&gt;&lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"postcss"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nc"&gt;.content&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="err"&gt;@apply&lt;/span&gt; &lt;span class="err"&gt;text-lg&lt;/span&gt; &lt;span class="err"&gt;text-gray-600;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also use Tailwind directives in your style tags. I usually add &lt;code&gt;lang="postcss"&lt;/code&gt; to get rid of editor warnings, but you can safely omit it if you want as it has no effect on the functionality.&lt;/p&gt;

&lt;p&gt;That's all there is to it. Tailwind CSS integration in SvelteKit in &lt;strong&gt;3 easy steps&lt;/strong&gt;. If I now counted correctly that is.&lt;/p&gt;

</description>
      <category>sveltekit</category>
      <category>svelte</category>
      <category>webdev</category>
      <category>tailwindcss</category>
    </item>
    <item>
      <title>Sneaky painful frontend burnout</title>
      <dc:creator>Ilia Mikhailov</dc:creator>
      <pubDate>Tue, 28 Sep 2021 00:00:00 +0000</pubDate>
      <link>https://dev.to/codechips/sneaky-painful-frontend-burnout-51f7</link>
      <guid>https://dev.to/codechips/sneaky-painful-frontend-burnout-51f7</guid>
      <description>&lt;p&gt;I've recently noticed an alarming trend. There is a high churn of frontend developers due to the burnout. It's a shame, but it totally makes sense if you take a deeper look at the problem. It's not something you normally notice. All burnouts are sneaky, but this one is special. It comes creeping very slowly and before you notice it's too late. This article is a personal reflection from the interviews conducted with people who decided to leave the frontend field due to the burnout.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Originally published on &lt;a href="https://codechips.me"&gt;codechips.me&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Rough history of web development
&lt;/h2&gt;

&lt;p&gt;Things were simpler back in the days. &lt;a href="https://jquery.com/"&gt;jQuery&lt;/a&gt; was king and we relied on it to get things done. And we did get things done too. Browser wars made the frontend world accelerate at incredible speed. There were many new cool APIs we could use, but they were often browser specific. This created a lot of inconsistencies and forced us to use polyfills. &lt;a href="https://modernizr.com/"&gt;Modernizr&lt;/a&gt; was a popular library that we relied on to detect what was possible for us to do in the browser.&lt;/p&gt;

&lt;p&gt;The speed of browser advancements propelled the development of new tools. &lt;a href="https://bower.io/"&gt;Bower&lt;/a&gt; was a popular, but short-lived package manager for the web that many developers relied on. We used different task runners such as &lt;a href="https://gruntjs.com/"&gt;Grunt&lt;/a&gt; and &lt;a href="https://gulpjs.com/"&gt;Gulp&lt;/a&gt; to get things done. The frontend ecosystem was getting complex, but we could still wrap our heads around it.&lt;/p&gt;

&lt;p&gt;We were getting things done with the tools and libraries we had, but it wasn't enough. We wanted more and better tools. &lt;a href="https://webpack.js.org/"&gt;Webpack&lt;/a&gt; was gaining traction and &lt;a href="https://browserify.org/"&gt;Browserify&lt;/a&gt; was released and this is where I personally lost track of everything. I think it was around 2014 where things got blurry and made it impossible to keep track of all the news. This is also the year when I had my first frontend burnout and decided to move back to backend development. It became too much. I craved simplicity.&lt;/p&gt;

&lt;p&gt;Let's fast forward to today and see if things have improved. The popular frameworks of the past are merely ghosts of their former glory. Today, React is the undisputed king followed by many other modern frameworks such as &lt;a href="https://svelte.dev/"&gt;Svelte&lt;/a&gt;, &lt;a href="https://vuejs.org/"&gt;Vue&lt;/a&gt; and &lt;a href="https://angular.io/"&gt;Angular&lt;/a&gt;. &lt;a href="https://www.typescriptlang.org/"&gt;TypeScript&lt;/a&gt; has become a de-facto standard language of web development. For sure, frameworks and languages have advanced for the better, but did they actually make our life simpler or allowed us to reduce time to market?&lt;/p&gt;

&lt;h2&gt;
  
  
  npm install broke the Internet
&lt;/h2&gt;

&lt;p&gt;My opinion is that &lt;code&gt;npm install&lt;/code&gt; broke the Internet. There were a few different attempts to bring package management to Node.js and NPM won. Today it's &lt;strong&gt;the standard&lt;/strong&gt; of JS package management, but in the beginning it was actually for managing packages for Node.js only, hence the name - &lt;strong&gt;N&lt;/strong&gt; ode &lt;strong&gt;P&lt;/strong&gt; ackage &lt;strong&gt;M&lt;/strong&gt; anager.&lt;/p&gt;

&lt;p&gt;Not that long after developers realized that they could also publish frontend libraries to NPM. I think it was &lt;a href="https://bower.io/"&gt;Bower&lt;/a&gt; package manager that started the trend. Sometimes later, Bower quickly became out of fashion when most of developers switched to different JS bundlers. Fast forward to today and think of one frontend project that doesn't use a bundler and downloads half of the Internet when you run &lt;code&gt;npm install&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That's the norm today. Why is it like this? First, NPM's recommendation to package creation was to create small focused packages that do one thing and do it well and I was a big proponent of this myself telling all other developers to think this way. Second, it's often easier to find and install a package from NPM than to write it yourself. Is it a good thing? I am not sure. Remember the leftpad incident? Also, if you rely on some NPM package and find a bug in it, how much effort is required to fix it versus if you would have written the functionality yourself?&lt;/p&gt;

&lt;p&gt;It's easy to add new packages to your project and if it's easy we tend to do it. We have too much freedom of choice. It's also a little too easy to publish packages on the NPM and everyone wants to scratch their own itch. I am not saying it's bad. On the contrary, sharing knowledge and contributing to open source is good, but does it really justify itself as NPM package? Maybe a simple Github gist is enough? Copy, paste, adjust.&lt;/p&gt;

&lt;p&gt;Here is a thought experiment. Would you be able to build the same app with the same level of interactivity if you could only add your dependencies as scripts in the document head? How cautious would you be?&lt;/p&gt;

&lt;h2&gt;
  
  
  Enter TypeScript
&lt;/h2&gt;

&lt;p&gt;JavaScript is a cool language that mixes OOP with FP. Pick and choose. It's also a messy language. Actually not messy, but maybe the right word would be - &lt;em&gt;flexible&lt;/em&gt;, and if you know your way around the language and all its quirks you will do just fine. Many existing websites and apps were written using plain JS.&lt;/p&gt;

&lt;p&gt;When TypeScript entered the scene (almost) everyone cheered and thought that it will make all of their JS problems go away. Sure, it might have solved some of the existing problems, better IDE autocomplete comes to mind, but what people don't realize is that by adopting TypeScript they just made their developer lives more complex. If you ask me, TypeScript is a weird language with a fake type system that gives you fake confidence. I too do use TypeScript for some projects, but it feels like the only thing I do is try to keep the TypeScript compiler happy.&lt;/p&gt;

&lt;p&gt;TypeScript is rarely frictionless, especially its setup. I bet there are very few developers that throw together a working &lt;code&gt;tsconfig.json&lt;/code&gt; on the first try. It comes with a cost and it's something you have to think about. Because it's so wide-spread today, it's hard not to use TypeScript when building open source libraries. I mean, you don't want people to think you are a weirdo, right?&lt;/p&gt;

&lt;p&gt;Adopting TypeScript is not enough. There is a whole JS surrounding ecosystem that you have to adopt to as well. Because JS is such a loose language you have to solve linting and code formatting. Should you adopt &lt;a href="https://eslint.org/"&gt;ESlint&lt;/a&gt; or &lt;a href="https://standardjs.com/"&gt;StandardJS&lt;/a&gt;? Is there room for &lt;a href="https://prettier.io/"&gt;Prettier&lt;/a&gt;? If you go with ESlint what plugins do you need? Should you adopt Airbnb style or is there something else, something better and newer? You might want to introduce automatic linting too so you can't commit bad code. &lt;a href="https://typicode.github.io/husky/#/"&gt;Husky&lt;/a&gt; might help you with it.&lt;/p&gt;

&lt;p&gt;The tooling ecosystem surrounding JS adds additional complexity layer to your project. Now, I am sure that you can reach the &lt;em&gt;perfect&lt;/em&gt; setup for your project, but how many many hours of frustration, searching and reading documentation did it take you to get there?&lt;/p&gt;

&lt;h2&gt;
  
  
  Where are we at?
&lt;/h2&gt;

&lt;p&gt;It's clear that the frontend world is getting more and more complex and those people who tell you it's still young and we are still trying to find good solutions to existing problems are ignorant. Two decades is a long time for the dust to settle, but if you keep adding more wind all the time your sight of view will only get shorter. The wind is added by a lot of new frameworks and libraries, the flexibility and shortness of the JavaScript as a language and NPM. It's too easy to publish and consume NPM packages today and everyone is doing it. The flexibility and the possibility is what got us here. I understand that innovation is hard, it's blood, sweat and tears, but what if we have been running in the wrong direction this whole time? The direction of complexity instead of simplicity.&lt;/p&gt;

&lt;p&gt;In fact it's so bad that I've heard that some of the developers have developed the green field project anxiety. There are too many choices and they rather take on maintenance projects than make the tough decisions. React fatigue and overall frontend fatigue is real. How do I know that the tech stack I am betting on will still be relevant in two years? SSR or SPA, routing, state management, style libraries. It's easy to end up in the state of analysis paralysis and it's not a nice place to be in.&lt;/p&gt;

&lt;p&gt;We also tend to get things backwards. Developers think that in order to get things done you have to use [&lt;em&gt;insert some cool framework name here&lt;/em&gt;]. I've witnessed this myself. I have seen code schools teaching newbies without any IT experience how to build apps with React without teaching them the basics of HTML and how the web works. That's crazy, but also sad. It's sad how much money people pay in hope to get a job after they finish the coding course.&lt;/p&gt;

&lt;p&gt;It's not the fault of the people who taught the course. It's &lt;em&gt;Economics 101&lt;/em&gt;, demand vs supply. If someone can make money out of you they will. React should be the last thing you learn because React is super-complex, but there is a surge for React developers so let's skip the essentials and concentrate on the needs of the market.&lt;/p&gt;

&lt;p&gt;Choosing boring technology for the project is boring. I myself is guilty of this too. Luckily I can figure most of the things out because I have a lot of experience under my belt, but it can imagine how frustrating it can be for a newbie. Heck, sometimes even I don't figure stuff out. Not because I can't, but because it's hard and I don't want to invest my time in it, spend my mental energy. To me it's a clear sign of how complex current frontend stack is.&lt;/p&gt;

&lt;p&gt;This leads me into the main topic of the article - the dreaded &lt;strong&gt;frontend burnout&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The signs of burnout
&lt;/h2&gt;

&lt;p&gt;This burnout type is sneaky. You don't usually notice it because it doesn't really feel as a burnout, but more like a never-ending mild frustration at first.&lt;/p&gt;

&lt;p&gt;It's a slow burnout. It's like drinking coffee from your favorite coffee cup that has a small, but sharp crack in it just at the right place and you scratch your lip every time you take a sip. First it's annoying, but then you get used to it, only to realize months later that you have had enough, and you throw that cup into the wall with anger.&lt;/p&gt;

&lt;p&gt;When you reach this moment it's usually too late. That's why you have to pay close attention to the following signs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bringing your work home.&lt;/strong&gt; Not physically, but mentally. If you come home with the constant nagging feeling that you didn't finish what you planned to accomplish today. The feeling of mild frustration.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Yak shaving.&lt;/strong&gt; If you feel that the only thing you do is fight with the tools and project related things and it leads to a lot of time spent tweaking, solving, dependency management, but no actual coding. Frustration builds up.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mentally taxing.&lt;/strong&gt; If you come home mentally drained and the only thing you have energy left to do is to watch Netflix. You have no power to do anything else.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lost opportunity cost.&lt;/strong&gt; If you start doubting if building UIs and web sites was the right career choice for you. You ask yourself if things would be different if you programmed an operating system or backend APIs, but you are building interactive UIs and it's just as complex.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Negative feedback loop.&lt;/strong&gt; If you feel that things are getting worse at work every day and you are not getting anything done.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Work/life balance.&lt;/strong&gt; If you feel that work bleeds into your private life and work/life boundaries become fuzzy. It can be as simple as thinking about the unfinished ticket in the evening or fail to fall asleep because you are thinking about work. This is especially common if you work remotely.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Performance drop.&lt;/strong&gt; If you feel tired and lethargic when you come to work and can't get anything done. You have hard time concentrating on the task at hand and procrastinate. Nothing feels exciting anymore.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Framework envy.&lt;/strong&gt; If you are thinking that things would be easier, more productive or fun if only you used another framework or technology in the project.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Quitting.&lt;/strong&gt; If you are thinking about switching jobs. This is a common fallacy. People often think it's their current employer that is the problem, but oftentimes it's not.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Denial.&lt;/strong&gt; If people close to you tell that you have changed lately or you are not present mentally, and you blame it on the tough intense period at work, it's definitely a sign. It's called &lt;em&gt;self-denial&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;These are just a few signs that you are nearing the point of no return and it's only a matter of time before the burnout hits you. There are also always physical symptoms associated with those feelings. The most common are: lack of energy, irritation, agitation and sleeping trouble. Pay close attention to your body, not only to your mind.&lt;/p&gt;

&lt;p&gt;If you can relate to any of those signs it can mean that you've caught this early and it might not be too late yet. Recognizing and accepting is the first and most important step.&lt;/p&gt;

&lt;p&gt;My best advise here would be to take a break and go out for long walks in the nature. They help clear your mind plus you get the physical exercise as a bonus. Also, make sure to book some social events with friends or family even if you feel that you have to force yourself. Next step is have an honest talk with your manager and explain how you feel. It might be hard to do, but you will be glad you did it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What can we do?
&lt;/h2&gt;

&lt;p&gt;There are things in my opinion we can do to reverse this trend, and they are exciting too, but it's a topic for another upcoming article.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tune in into your feelings
&lt;/h2&gt;

&lt;p&gt;I fully understand, and accept, that the frontend world is turbulent at the moment, and it will be a bumpy ride before we get to somewhat stable state, but please be careful. Watch out for the signals yourself, watch your friends and colleagues. Pay close attention to how you feel and your body. Burnout is never worth it.&lt;/p&gt;

&lt;p&gt;Think objectively. Development is fun, but sometimes it can be too fun. Do you really need to use this frontend framework? Do you really need to use SSR? Is SEO a requirement? Remember that complexity increases with every choice you make. It might not be obvious at first when you get everything setup and running, but it will be obvious later when you find yourself in the hole you that have been digging yourself and it's too deep to climb up from.&lt;/p&gt;

&lt;p&gt;We, as programmers, often tend to complicate things. It's in our DNA. We like a good challenge, to solve complex problems and we are really good at it too. The problem is that the complexity often steals our time. Time that is better spend building features instead that bring real value to the users.&lt;/p&gt;

&lt;p&gt;Next time you are about to start a new project always ask yourself: &lt;strong&gt;What is the simplest solution I can get away with?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It's totally fine if you don't agree with me on everything. I just wanted to get it off my chest.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>career</category>
      <category>javascript</category>
      <category>typescript</category>
    </item>
    <item>
      <title>How to setup Vim for Svelte development</title>
      <dc:creator>Ilia Mikhailov</dc:creator>
      <pubDate>Thu, 10 Dec 2020 00:00:00 +0000</pubDate>
      <link>https://dev.to/codechips/how-to-setup-vim-for-svelte-development-1gkj</link>
      <guid>https://dev.to/codechips/how-to-setup-vim-for-svelte-development-1gkj</guid>
      <description>&lt;p&gt;I like Vim. I like its simplicity, its ugliness. There is something appealing about its GUI constraints too.&lt;/p&gt;

&lt;p&gt;Let's be honest. Trend sensitivity in tech is a big problem. Developers are jumping from editor to editor whenever something new, hot and trendy comes out. Always searching for the Holy Grail. Sublime, Atom, VSCode. What comes next?&lt;/p&gt;

&lt;p&gt;Vim is a very old editor. It's 25 years old. That's ancient in the tech world. What can possibly justify this piece of software's existence? Something must be good if it still lives, right? What are the benefits?&lt;/p&gt;

&lt;p&gt;One of Vim's powers is its simplicity and extensibility. It's a very extensible editor. It's also scriptable. It has macros and tons of other useful features. It's small, doesn't hog your CPU and make your computer fans go crazy. It often comes prepackaged with almost every Unix-like operating system.&lt;/p&gt;

&lt;p&gt;It's also a powerful editor for Javascript, TypesScript and Svelte development.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Uc94sgB---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/codechips/image/upload/v1607621821/posts/vim-svelte-typescript_xbx7si.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Uc94sgB---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/codechips/image/upload/v1607621821/posts/vim-svelte-typescript_xbx7si.png" alt="Vim with Svelte and TypeScript" width="880" height="512"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Neovim
&lt;/h2&gt;

&lt;p&gt;Neovim is the modern fork of Vim. Its goal is to clean and extend Vim with modern features. Personally, I don't use Neovim. I tried it, but I can't honestly tell what makes it better than the original Vim. The gains are not obvious to me. I am sticking to orginal Vim for now, currently version 8.2.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ever Heard of Emacs?
&lt;/h2&gt;

&lt;p&gt;You might say &lt;em&gt;"But what about Emacs?"&lt;/em&gt; I tried Emacs too for a while after getting inspired from watching &lt;a href="http://emacsrocks.com/"&gt;Emacs Rocks&lt;/a&gt; videos. Tried only to realize that I was spending most of my working time configuring Emacs instead of doing the actual work. Also, I already have an operating system, thank you.&lt;/p&gt;

&lt;p&gt;Oh, and you know, all those people who just &lt;strong&gt;have to&lt;/strong&gt; crack a joke about exiting Vim, why don't you ask them to try and exit Emacs next time? Look who is laughing now!&lt;/p&gt;

&lt;h2&gt;
  
  
  Side note: Vim + Tmux = Power
&lt;/h2&gt;

&lt;p&gt;I always use Tmux and Vim together. What it &lt;a href="https://github.com/tmux/tmux/wiki"&gt;Tmux&lt;/a&gt;? I would describe it as a tiling window manager for the terminal. You have to tweak it a bit to you liking, but once you master it, windows will start flying.&lt;/p&gt;

&lt;p&gt;This is out of scope for the article. I just wanted to mention it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Svelte Syntax Highlighting
&lt;/h2&gt;

&lt;p&gt;First things first, we have to configure Vim syntax highlighting for Svelte, Javascript and TypeScript.&lt;/p&gt;

&lt;p&gt;For Svelte, &lt;a href="https://github.com/evanleck/vim-svelte"&gt;vim-svelte&lt;/a&gt; is the plugin to use. It provides indentation and syntax highlighting for Svelte components. There is also &lt;a href="https://github.com/leafOfTree/vim-svelte-plugin"&gt;vim-svelte-plugin&lt;/a&gt; that does the same thing.&lt;/p&gt;

&lt;p&gt;For Javascript language highlighting, you have a few different options. The most popular seem to be &lt;a href="https://github.com/pangloss/vim-javascript"&gt;vim-javascript&lt;/a&gt; and this is what I use.&lt;/p&gt;

&lt;p&gt;There is also &lt;a href="https://github.com/yuezk/vim-js"&gt;vim-js&lt;/a&gt; and &lt;a href="https://github.com/HerringtonDarkholme/yats.vim"&gt;jats&lt;/a&gt;. If you are feeling adventurous check out &lt;a href="https://github.com/sheerun/vim-polyglot"&gt;vim-polyglot&lt;/a&gt;, a plugin with more language syntax hightlights that you probably will ever need.&lt;/p&gt;

&lt;p&gt;When it comes to TypeScript, I settled on &lt;a href="https://github.com/HerringtonDarkholme/yats.vim"&gt;yats.vim&lt;/a&gt;. I find it the best, but &lt;a href="https://github.com/leafgarland/typescript-vim"&gt;typescript-vim&lt;/a&gt; is another good option.&lt;/p&gt;

&lt;p&gt;I am using &lt;a href="https://github.com/junegunn/vim-plug"&gt;vim-plug&lt;/a&gt; as my Vim plugin manager and if you do too, this is how to add them to Vim config file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Plug 'evanleck/vim-svelte'
Plug 'pangloss/vim-javascript'
Plug 'HerringtonDarkholme/yats.vim'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Syntax highlighting is a nice to have, but autocomplete is essential for a good coding session. Let's tackle that next.&lt;/p&gt;

&lt;h2&gt;
  
  
  Svelte Autocomplete with Coc.nvim - Conquer of Completion
&lt;/h2&gt;

&lt;p&gt;Honestly, how did I write code in Vim before this extension existed? Apparently I did, but I bet it was painful.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/neoclide/coc.nvim"&gt;Coc.nvim&lt;/a&gt; is a must-have if you are a Vim user. It's an &lt;em&gt;"Intellisense engine for Vim8 &amp;amp; Neovim, full language server protocol support as VSCode"&lt;/em&gt; according to their website.&lt;/p&gt;

&lt;p&gt;What does it mean? It means it supports the same language protocol standard as VScode, which in turn means you can easily implement an autocompletion for a language if that language has a language server. Crystal clear, right?&lt;/p&gt;

&lt;p&gt;Here is Svelte's &lt;a href="https://github.com/sveltejs/language-tools"&gt;language server&lt;/a&gt; for example.&lt;/p&gt;

&lt;p&gt;Installing Coc.nvim is straight forward. Add the line below to your &lt;code&gt;.vimrc&lt;/code&gt; in the right place and run &lt;code&gt;:PlugInstall&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;Plug 'neoclide/coc.nvim', {'branch': 'release'}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Coc.nvim has many extensions and we will install one for Svelte next, along with a few others we will need too.&lt;/p&gt;

&lt;h2&gt;
  
  
  coc-svelte
&lt;/h2&gt;

&lt;p&gt;If you are coding Svelte in Vim &lt;a href="https://github.com/codechips/coc-svelte"&gt;coc-svelte&lt;/a&gt; is a must. Unfortunately, the original extension's Svelte dependencies are lagging behind, so I keep my own fork of it where I try to keep upstream dependencies in sync.&lt;/p&gt;

&lt;p&gt;Add it to your &lt;code&gt;.vimrc&lt;/code&gt; with the line below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Plug 'codechips/coc-svelte', {'do': 'npm install'}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Beside Svelte extension we also need to install TypeScript Coc extension. It will help us with autocompletion for TypeScript and JavaScript. Install it in Vim by running &lt;code&gt;:CocInstall coc-tsserver&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;Coc.nvim autoupdates dependencies in the background, but you can always update them manually by running &lt;code&gt;:CocUpdate&lt;/code&gt; command.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up svelte.config.js
&lt;/h2&gt;

&lt;p&gt;If you are using TypeScript, SASS or PostCSS in your Svelte components you have to create a &lt;code&gt;svelte.config.js&lt;/code&gt; file in the root of your project. That file is needed by &lt;em&gt;coc.nvim&lt;/em&gt; in order to interpret those parts of the Svelte files correctly. Read my TypeScript and Svelte article on how to do that - &lt;a href="https://dev.to/how-to-use-typescript-with-svelte/#bonus:-svelte-editor-integration"&gt;How to use Typescript with Svelte&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Formatting Files with Prettier
&lt;/h2&gt;

&lt;p&gt;We also want our files to look nice and therefore we need to format them. There is a &lt;a href="https://github.com/neoclide/coc-prettier"&gt;coc-prettier&lt;/a&gt; extension that does that, but for some reason it breaks Svelte files for me instead. Sometimes it doubles the lines at the end of the file. For that reason I decided to go with &lt;a href="https://github.com/prettier/vim-prettier"&gt;vim-prettier&lt;/a&gt; extension instead.&lt;/p&gt;

&lt;p&gt;For Prettier extension to work properly it needs to find a &lt;code&gt;prettier&lt;/code&gt; CLI and Prettier configuration. There are multiple ways of configuring and setting it up, but I always do it locally for every of my projects. It's the easiest.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ npm add -D prettier prettier-plugin-svelte
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You also need to add Prettier vim plugin to your Vim config and tweak some of its settings.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Plug 'prettier/vim-prettier', { 'do': 'npm install' }

" Prettier Settings
let g:prettier#quickfix_enabled = 0
let g:prettier#autoformat_require_pragma = 0
au BufWritePre *.css,*.svelte,*.pcss,*.html,*.ts,*.js,*.json PrettierAsync
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One drawback of Prettier is that you have to install Prettier and prettier Svelte plugin in every Svelte project. It might be possible to use a global Prettier config, because it will search for Prettier configs up the tree.&lt;/p&gt;

&lt;p&gt;However, I strongly advice against doing it, because you might want to share same config with your team.&lt;/p&gt;

&lt;p&gt;My prettier config usually looks something 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;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="na"&gt;arrowParens&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;avoid&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;printWidth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;singleQuote&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;svelteBracketNewLine&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;svelteStrictMode&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="na"&gt;trailingComma&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;none&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;plugins&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="s1"&gt;prettier-plugin-svelte&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;Prettier uses &lt;a href="https://github.com/davidtheclark/cosmiconfig"&gt;cosmiconfig&lt;/a&gt;. That means you can use any name and format it &lt;a href="https://prettier.io/docs/en/configuration.html"&gt;supports&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuring Coc.nvim
&lt;/h2&gt;

&lt;p&gt;If you need to configure Coc.nvim and its extentions you can open the config file directly in Vim by running &lt;code&gt;:CocConfig&lt;/code&gt; command. You can also edit the &lt;code&gt;coc-settings.json&lt;/code&gt; file directly. You will find it in your &lt;code&gt;~/.vim&lt;/code&gt; folder or &lt;code&gt;~/.config/nvim&lt;/code&gt; if you are using Neovim.&lt;/p&gt;

&lt;p&gt;Here is what mine looks like.&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;"diagnostic.messageTarget"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"echo"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"signature.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;"echo"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"coc.preferences.hoverTarget"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"echo"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"javascript.suggestionActions.enabled"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"typescript.suggestionActions.enabled"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"prettier.printWidth"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"prettier.singleQuote"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&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;All those "echo" values force Coc to display diagnostic messages in Vim's command line instead of a popup. I don't like distracting popups.&lt;/p&gt;

&lt;p&gt;These are Coc.nvim settings in my &lt;code&gt;.vimrc&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;" COC
let g:coc_node_path = '$HOME/.nvm/versions/node/v12.16.3/bin/node'

nmap &amp;lt;leader&amp;gt;rn &amp;lt;Plug&amp;gt;(coc-rename)
nmap &amp;lt;silent&amp;gt; gd &amp;lt;Plug&amp;gt;(coc-definition)
nmap &amp;lt;silent&amp;gt; gy &amp;lt;Plug&amp;gt;(coc-type-definition)
nmap &amp;lt;silent&amp;gt; gi &amp;lt;Plug&amp;gt;(coc-implementation)
nmap &amp;lt;silent&amp;gt; gr &amp;lt;Plug&amp;gt;(coc-references)

set updatetime=300
set shortmess+=c " don't give |ins-completion-menu| messages.

" Use K to show documentation in preview window
nnoremap &amp;lt;silent&amp;gt; K :call &amp;lt;SID&amp;gt;show_documentation()&amp;lt;CR&amp;gt;

function! s:show_documentation()
  if (index(['vim','help'], &amp;amp;filetype) &amp;gt;= 0)
    execute 'h '.expand('&amp;lt;cword&amp;gt;')
  else
    call CocAction('doHover')
  endif
endfunction
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Maybe you can get some inspiration from reading them.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Few More Tips
&lt;/h2&gt;

&lt;p&gt;It's not sure that Vim will recognize that your Svelte file consists of different sections and languages (JavaScript, CSS) and might comment them out using HTML comments when you try to comment them out.&lt;/p&gt;

&lt;p&gt;It happened to me, and if it happens to you, here is how to solve it.&lt;/p&gt;

&lt;p&gt;Install the &lt;a href="https://github.com/Shougo/context_filetype.vim"&gt;context_filetype&lt;/a&gt; Vim plugin. It will help us set the filetype based on the file section.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Plug 'Shougo/context_filetype.vim'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add the following code to your Vim config.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if !exists('g:context_filetype#same_filetypes')
  let g:context_filetype#filetypes = {}
endif

let g:context_filetype#filetypes.svelte =
\ [
\   {'filetype' : 'javascript', 'start' : '&amp;amp;lt;script&amp;amp;gt;', 'end' : '&amp;amp;lt;/script&amp;amp;gt;'},
\   {
\     'filetype': 'typescript',
\     'start': '&amp;amp;lt;script\%( [^&amp;gt;]*\)\? \%(ts\|lang="\%(ts\|typescript\)"\)\%( [^&amp;gt;]*\)\?&amp;gt;',
\     'end': '&amp;lt;/script&amp;gt;',
\   },
\   {'filetype' : 'css', 'start' : '&amp;amp;lt;style \?.*&amp;gt;', 'end' : '&amp;amp;lt;/style&amp;amp;gt;'},
\ ]

let g:ft = ''
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now your comments should behave correctly.&lt;/p&gt;

&lt;p&gt;I am using &lt;a href="https://github.com/preservim/nerdcommenter"&gt;NERDCommenter&lt;/a&gt;, a Vim commenting plugin with the best punchline - &lt;em&gt;"Comment functions so powerful - no comment necessary."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;For it to work properly with Svelte I had to tweak its settings a bit.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;" NERDCommenter settings

let g:NERDSpaceDelims = 1
let g:NERDCompactSexyComs = 1
let g:NERDCustomDelimiters = { 'html': { 'left': '&amp;lt;!--', 'right': '--&amp;gt;' } }

" Align comment delimiters to the left instead of following code indentation
let g:NERDDefaultAlign = 'left'

fu! NERDCommenter_before()
  if (&amp;amp;ft == 'html') || (&amp;amp;ft == 'svelte')
    let g:ft = &amp;amp;ft
    let cfts = context_filetype#get_filetypes()
    if len(cfts) &amp;gt; 0
      if cfts[0] == 'svelte'
        let cft = 'html'
      elseif cfts[0] == 'scss'
        let cft = 'css'
      else
        let cft = cfts[0]
      endif
      exe 'setf ' . cft
    endif
  endif
endfu

fu! NERDCommenter_after()
  if (g:ft == 'html') || (g:ft == 'svelte')
    exec 'setf ' . g:ft
    let g:ft = ''
  endif
endfu
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are plenty of other useful tips and tricks, but I will stop here.&lt;/p&gt;

&lt;p&gt;If you stuck with me this far you should have a fully functional Vim setup ready for Svelte development.&lt;/p&gt;

&lt;h2&gt;
  
  
  Plugins Mentioned
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/evanleck/vim-svelte"&gt;https://github.com/evanleck/vim-svelte&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/leafOfTree/vim-svelte-plugin"&gt;https://github.com/leafOfTree/vim-svelte-plugin&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/pangloss/vim-javascript"&gt;https://github.com/pangloss/vim-javascript&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/yuezk/vim-js"&gt;https://github.com/yuezk/vim-js&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/junegunn/vim-plug"&gt;https://github.com/junegunn/vim-plug&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/leafgarland/typescript-vim"&gt;https://github.com/leafgarland/typescript-vim&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/HerringtonDarkholme/yats.vim"&gt;https://github.com/HerringtonDarkholme/yats.vim&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/sheerun/vim-polyglot"&gt;https://github.com/sheerun/vim-polyglot&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/neoclide/coc-prettier"&gt;https://github.com/neoclide/coc-prettier&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/prettier/vim-prettier"&gt;https://github.com/prettier/vim-prettier&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/preservim/nerdcommenter"&gt;https://github.com/preservim/nerdcommenter&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/Shougo/context_filetype.vim"&gt;https://github.com/Shougo/context_filetype.vim&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/davidtheclark/cosmiconfig"&gt;https://github.com/davidtheclark/cosmiconfig&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Words
&lt;/h2&gt;

&lt;p&gt;My goal has always been to shorten the time between an idea, a thought, and its final implementation on screen. I think I've come pretty close by now by using Vim as my main editor.&lt;/p&gt;

&lt;p&gt;However, learning Vim comes with a price. I can barely write anything outside Vim now. Short texts, no problem. Longer texts, forget it. The muscle memory is strong!&lt;/p&gt;

&lt;p&gt;One big reason I like Vim is that it's never in my way. I don't like being distracted. It's just me, a black computer screen and my thoughts.&lt;/p&gt;

&lt;p&gt;My final advice:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Stop jumping around, always looking for something better, and invest your time and energy into something that has proven its value by now - Vim.&lt;/li&gt;
&lt;li&gt;Very few people can master Vim fully, but many will increase their productivity from learning just a bit of Vim and by just start using it in their daily work. Try to stick to it and miracles will happen!&lt;/li&gt;
&lt;li&gt;Never copy someone else's config. Start with sensible defaults and build it up as you go.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you want to learn Vim, &lt;a href="http://vimcasts.org/"&gt;Vimcasts&lt;/a&gt; is a good starting point. Godspeed.&lt;/p&gt;

</description>
      <category>vim</category>
      <category>svelte</category>
      <category>productivity</category>
      <category>beginners</category>
    </item>
    <item>
      <title>RxJS beginner learning resources</title>
      <dc:creator>Ilia Mikhailov</dc:creator>
      <pubDate>Sat, 05 Dec 2020 00:00:00 +0000</pubDate>
      <link>https://dev.to/codechips/rxjs-beginner-learning-resources-4b5e</link>
      <guid>https://dev.to/codechips/rxjs-beginner-learning-resources-4b5e</guid>
      <description>&lt;p&gt;RxJS is such a wonderful technology. Seriously. It's too bad that not many people outside Angular use it. But I totally get it. The entry bar is really high. Some say it's even higher than Vim's.&lt;/p&gt;

&lt;p&gt;I learned Vim and I learned &lt;a href="https://rxjs.dev"&gt;RxJS&lt;/a&gt;. I didn't say it was easy. Was it worth it? 100%. The key is being persistent and not give up. If I could learn so can you!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--apa1r4YA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/codechips/image/upload/v1607185401/posts/rxjs-beginner-learning-resources_nbd7cw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--apa1r4YA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/codechips/image/upload/v1607185401/posts/rxjs-beginner-learning-resources_nbd7cw.png" alt="RxJS Beginner Learning Resources" width="880" height="440"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I've compiled a list of some of the best RxJS resources for people who are just getting started on their wonderful journey to become RxJS masters.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is RxJS?
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Observables, multicast, share, subscriptions, hot, cold, async, streams, forkJoin, switchMap, concat&lt;/em&gt;. Oh my! Those terms alone can scare one off. But don't be afraid! Once you follow this guide everything will start making sense.&lt;/p&gt;

&lt;p&gt;The way I like to think about RxJS is that it's a library that helps us process our data through a &lt;strong&gt;data-processing pipeline&lt;/strong&gt; we define.&lt;/p&gt;

&lt;p&gt;When you hear people talk about RxJS you often hear the word &lt;em&gt;streams&lt;/em&gt;. Well, it kind of makes sense. The data flows through the processing pipeline just like water flows through a pipe.&lt;/p&gt;

&lt;p&gt;RxJS also contains the word &lt;em&gt;reactive&lt;/em&gt; in it. Let's talk about it and what reactivity means in this context. I like to think of it as the data pipeline only activates (reacts) only when we tell it to. What do I mean by that? Let me explain.&lt;/p&gt;

&lt;p&gt;Reactivity means that we can trigger our data processing pipeline by various inputs or events that we define in the pipeline. But there is a catch. The pipeline is not activated, even when triggered by our defined events, &lt;em&gt;unless&lt;/em&gt; we tell it that we are interested by subscribing to it. No subscribers, no reactivity, no data.&lt;/p&gt;

&lt;p&gt;This works as a kind of pull model, even though the data is pushed through the pipeline. Let me explain. When we subscribe to our pipeline (pull), we activate the pipeline. It then activates the pipeline from the bottom all the way up by saying "&lt;em&gt;Hey, we have a subscriber! Let's start working!&lt;/em&gt;" All the events listeners and triggers will get activated and they will start processing (pushing) data through our pipeline all the way down to us when something is triggered. When we unsubscribe, the pipeline will become inactive again, because there are no subscribers.&lt;/p&gt;

&lt;p&gt;Does it make sense? It's reactive because it's lazy. Nothing happens until you subscribe. Only then it starts reacting (or working).&lt;/p&gt;

&lt;p&gt;RxJS is also a declarative framework. This is another concept that can be hard to understand. With imperative (normal) programming you describe exactly what you want to happen, step by step. You give precise instructions. With declarative programming, on the other hand, you write code that describes what you want to do, but not necessarily how to do it. You say what you want, but you don't specify exactly how. It's compiler's job to figure it out. You give the compiler commands instead of telling it exactly what to do. These commands consists of RxJS operators chained together. The implementation details are abstracted away from you. You specify the desired outcome and compiler will figure it out for you.&lt;/p&gt;

&lt;p&gt;This is good, because you are forced to work on the higher level of abstraction. The implementation details are already in place for you. Less code to write. You just have to define the desired outcome in your program and RxJS will take care of it.&lt;/p&gt;

&lt;p&gt;Another hurdle, and maybe the hardest to jump over, is thinking in streams. Thinking in streams is super-hard and will take you some time to learn. But it's really cool once you grok it. When you finally reach that "&lt;em&gt;Aha!&lt;/em&gt;" moment you will for sure get a rush. Suddenly, it will all make sense and you will realize how to merge, delay, filter, split and do lots of other cool things with streams.&lt;/p&gt;

&lt;p&gt;One essential thing when learning RxJS is to take it slow and be patient. You head will hurt, for sure. Try not to get frustrated. It will all click together soon if you are persistent. Later, you will wonder why you didn't learn RxJS earlier and will be telling all your friends how cool it is and you will start prompting it in your circles.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to use this guide
&lt;/h2&gt;

&lt;p&gt;I've compiled a list of resources that helped me learn RxJS. RxJS is built into Angular, that's why many articles on the internet are Angular based. But you can ignore all Angluar stuff and concentrate only on the juicy RxJS parts.&lt;/p&gt;

&lt;p&gt;The guide is broken down into different sections. Start at the top, it will help you learn the core concepts. Later, feel free to jump around to the articles that catch your attention.&lt;/p&gt;

&lt;p&gt;When you read an article and learn a new concept, it's not guaranteed to be crystal clear at first. Come back to it sometimes later and re-read it.&lt;/p&gt;

&lt;p&gt;After you learn a new concept or operator, try to play around with it to get that hands-on feeling. Spend 20-30 minutes on it. It will be time well invested.&lt;/p&gt;

&lt;p&gt;Also, many of the articles included are on Medium. Sorry about that.&lt;/p&gt;

&lt;h3&gt;
  
  
  Note on Svelte and RxJS
&lt;/h3&gt;

&lt;p&gt;I am a Svelte fan, so I also threw in a couple of Svelte related articles for other Svelte fans out there. One thing about Svelte and RxJS combo is that they go very well together. I like to say that RxJS is Svelte's &lt;strong&gt;stores on steroids&lt;/strong&gt;. You get &lt;strong&gt;200% reactivity&lt;/strong&gt; if you use RxJS.&lt;/p&gt;

&lt;p&gt;Another cool thing is that you don't have to use &lt;code&gt;onMount&lt;/code&gt;, when fetching data for example. Why? Because RxJS is lazy (reactive). Since Svelte views RxJS pipelines as Svelte stores, Svelte compiler manages subscriptions for us automatically. Now, how cool is that?!&lt;/p&gt;

&lt;h2&gt;
  
  
  Basics
&lt;/h2&gt;

&lt;p&gt;The hardest part is getting started. RxJS has many operators, but you only need to learn a handful of them to be productive. Once you learn them, others will start making sense.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://gist.github.com/staltz/868e7e9bc2a7b8c1f754"&gt;The introduction to Reactive Programming you've been missing&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Classic introduction to Reactive Programming. This should be your starting point. It's a little dated, but explains the concepts well and teaches you how to think in streams.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://codechips.me/classic-frp-tutorial-with-svelte-rxjs-6/"&gt;Recreating a classic FRP tutorial with Svelte and RxJS&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;My own take on the tutorial above with Svelte and newer version of RxJS. Plus I went a bit further than the original article by removing code duplication.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://softchris.github.io/books/rxjs/"&gt;Rxjs Ultimate Book&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Good and short introduction to RxJS in form of free online book. Highly recommended!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://codechips.me/if-svelte-and-rxjs-had-a-baby/"&gt;If Svelte and RxJS had a baby&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I explain why Svelte and RxJS is such a nice combo with a few simple examples.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/better-programming/getting-started-with-rxjs-1b6260dd184b"&gt;Getting Started With RxJS&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Short article that gives a digestible overview of RxJS and concepts with a few code examples.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.strongbrew.io/the-sip-principle/"&gt;Thinking reactive with the SIP principle&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nice article that teaches you to think reactively with RxJS. I like to think of RxJS operators as Lego pieces that you assemble into something larger.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://rangle.io/blog/rxjs-where-is-the-if-else-operator/"&gt;RxJS, where is the If-Else Operator&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Coming from imperative background you might wonder where the &lt;code&gt;if&lt;/code&gt; statement is. Well, there is no &lt;code&gt;if&lt;/code&gt; statement sort of. Instead you use operators like &lt;code&gt;map&lt;/code&gt; and &lt;code&gt;filter&lt;/code&gt; to achieve the desired branching logic.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/@luukgruijs/understanding-hot-vs-cold-observables-62d04cf92e03"&gt;Understanding hot vs cold Observables&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hot and cold observables. You will hear it a lot when learning RxJS and will most likely get burned by it sometimes. This short article explains the concepts very well.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ncjamieson.com/understanding-publish-and-share/"&gt;RxJS: Understanding the publish and share Operators&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This article explains in-depth how to turn cold observarbles into hot. Heavy reading, but an excellent reference.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://itnext.io/understanding-rxjs-subjects-386605ad2bdb"&gt;Understanding RxJS Subjects&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;RxJS subjects is another concept that you must understand. They are really useful. This article explains subjects on the higher level.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://levelup.gitconnected.com/rxjs-subjects-explained-with-examples-78ae7b9edfc"&gt;RxJS Subjects Explained with Examples&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Good code examples of RxJS subjects. A follow up article to the one above.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.angular-university.io/rxjs-higher-order-mapping/"&gt;Comprehensive Guide to Higher-Order RxJs Mapping Operators&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Mapping operators are the core of RxJS and there are quite a few of them. This article explains them well.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/@luukgruijs/understanding-rxjs-map-mergemap-switchmap-and-concatmap-833fc1fb09ff"&gt;Understanding RxJS map, mergeMap, switchMap and concatMap&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Another excellent article on various RxJS mapping operators. A must read!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.angular-university.io/rxjs-error-handling/"&gt;RxJs Error Handling: Complete Practical Guide&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You will get errors and exceptions when working with RxJS and you need to know how to handle them. This in-depth article explains how to deal with errors the RxJS way.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://fireship.io/lessons/rxjs-basic-pro-tips/"&gt;Top Ten RxJS Concepts&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Little dated, but still very good RxJS concepts overview from Fireship. With complementary video too!&lt;/p&gt;

&lt;h2&gt;
  
  
  Intermediate
&lt;/h2&gt;

&lt;p&gt;Once you get down the basics your imperative mind will still struggle to translate it to declarative thinking. You need to revisit the concepts and examine them closer, more in-depth.&lt;/p&gt;

&lt;p&gt;After you learn the basics, you only need to learn a handful of operators. Like, really learn them. Especially different mapping operators. Higher-order observables can be tough to grasp.&lt;/p&gt;

&lt;p&gt;Below is a collection of intermediate resources. They all require basic RxJS knowledge. Without it they will be either overwhelming or will just not make any sense to you.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://rangleio.ghost.io/thinking-in-nested-streams-with-rxjs/"&gt;Thinking in nested streams with RxJS&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Learn how to work with higher-order observables aka nested streams.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/weekly-webtips/rxjs-transformation-operators-in-examples-part-1-9c9fb7b4e705"&gt;RxJS. Transformation Operators in Examples (part 1)&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Very good breakdown of the transformation operators such as different buffer and concat operators. Clear code examples.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/weekly-webtips/rxjs-transformation-operators-in-examples-part-2-9d0f09bdbe6d"&gt;RxJS. Transformation Operators in Examples (part 2)&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Second part of the transformation operators. This time various merge, scan, group and window operators. Excellent code examples!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ultimatecourses.com/blog/rxjs-forkjoin-combine-observables"&gt;Combining Observables with forkJoin in RxJS&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;ForkJoin is RxJS version of &lt;code&gt;Promise.all&lt;/code&gt;. It's really useful to have when you have to deal with parallel HTTP requests for example.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.angular-university.io/rxjs-switchmap-operator/"&gt;Deep Dive Into The RxJs switchMap Operator - How Does it Work? A Less Well-Known Use Case (selector functions)&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;SwitchMap is one operator that you will use often. This is a nice breakdown of how it works using HTTP requests examples.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/@damianczapiewski/rxjs-merge-vs-mergeall-vs-mergemap-7d8f40fc4756"&gt;RxJS: merge() vs. mergeAll() vs. mergeMap()&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Merge is also one of the frequently used operators. Make sure you understand all the different variations of it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://itnext.io/the-magic-of-rxjs-sharing-operators-and-their-differences-3a03d699d255"&gt;The magic of RXJS sharing operators and their differences&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Explains the sharing operators in detail. Those cold vs hot observable concepts.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://netbasal.com/creating-custom-operators-in-rxjs-32f052d69457"&gt;Creating Custom Operators in RxJS&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Learn how to create custom observables in RxJS. Helps you understand and solidify your RxJS observable concepts.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://netbasal.com/getting-to-know-the-defer-observable-in-rxjs-a16f092d8c09"&gt;Getting to Know the Defer Observable in RxJS&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Defer operator is really handy. It might not be something you will use often, but it's still very important operator to know.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://indepth.dev/create-a-taponce-custom-rxjs-operator"&gt;Create a tapOnce custom Rxjs Operator&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Continuing on the topic of custom RxJS operators. Here is a very good article explaining how to create a custom &lt;code&gt;tapOnce&lt;/code&gt; operator. You will be using &lt;code&gt;tap&lt;/code&gt; operator a lot when you need to debug your pipelines and see what data flows through them.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ckjaersig.dk/2020/05/showing-a-loading-spinner-delayed-with-rxjs/"&gt;Showing a loading spinner delayed with rxjs&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Clean example of how to show a loading spinner while waiting for something. I am sure you will want to show a loading spinner in your apps.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/daviddalbusco/debounce-with-vanilla-javascript-or-rxjs-280c"&gt;Debounce with vanilla JavaScript or RxJS&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Examples of debounce using plain JS and RxJS. Which one is better? You be the judge.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/laurosilvacom/thinking-reactively-with-rxjs"&gt;Thinking Reactively with RxJS&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A text transcript and source code of the paid RxJS course on &lt;a href="http://Egghead.io"&gt;Egghead.io&lt;/a&gt;. Covers lots of ground!&lt;/p&gt;

&lt;h2&gt;
  
  
  Advanced
&lt;/h2&gt;

&lt;p&gt;Below are some advanced topics and tips. Take a look at them when you are really sure you understand the core concepts.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ncjamieson.com/understanding-expand/"&gt;RxJS: Understanding Expand&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Learn the &lt;code&gt;expand&lt;/code&gt; operator with guitar delay pedal example. Very cool!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.strongbrew.io/rx-example-of-the-day-part-1/"&gt;RxJS examples of the day (part 1)&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Good examples of how solve problems in the most effective ways when using RxJS. Lots of learning opportunities.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/javascript-everyday/rxjs-iif-is-not-the-same-as-defer-with-ternary-operator-7cb012903fe7"&gt;RxJS: iif is not the same as defer with ternary operator&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Comparison between &lt;code&gt;iif&lt;/code&gt; and &lt;code&gt;defer&lt;/code&gt; and when to use what.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.strongbrew.io/building-a-safe-autocomplete-operator-with-rxjs/"&gt;Building a safe autocomplete operator in RxJS&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;RxJS is really handy for autocomplete. Learn how to build a custom autocomplete operator.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.strongbrew.io/thinking-reactively-in-angular-and-rxjs/"&gt;Thinking reactively in Angular and RXJS&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Learn how to think reactively by building a calendar app.&lt;/p&gt;

&lt;h2&gt;
  
  
  Other Interesting Resources
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.learnrxjs.io/"&gt;Learn RxJS&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Best RxJS reference with good examples. My goto place when I need to look up an operator.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.rxjs-fruits.com/"&gt;RxJS-fruits&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Fun game to learn RxJS. You have to code your way through.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://rxmarbles.com/"&gt;RxJS Marbles&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;RxJS is usually explained with the help of marble diagrams. This is a good interactive reference to many RxJS operators.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://thinkrx.io/"&gt;ThinkRx&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Another interactive tool slash reference that will help you understand operators with code examples and marble diagrams.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://reactive.how/"&gt;reactive.how&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Lots of good resources and short interactive animations comparing different operators together. Site feels a bit messy, but the information is really good.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/firebase-developers/rxjs-firebase-101-68a84c37f85e"&gt;RxJS &amp;amp; Firebase 101&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Firebase has really nice RxJS bindings. This article explains the basics.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/dailyjs/introducing-bloc-pattern-with-react-and-rxjs-40109665bb2"&gt;Introducing BLoC Pattern with React and RxJS&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;BLoC pattern originated in Dart language, but can be used in other frameworks too. Here is a simple example with RxJS in React.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=dzhavat.rxjs-cheatsheet"&gt;RxJS Cheatsheet VS Code extension&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Handy VSCode extension. View RxJS operator documentation in-place.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/AlexAegis/svelte-minesweeper"&gt;https://github.com/AlexAegis/svelte-minesweeper&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Cool classic Minesweeper clone built in Svelte, RxJS and TypeScript. Lots of learning opportunities by studying the code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Videos
&lt;/h2&gt;

&lt;p&gt;If videos are your thing, here are some of the best ones.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=uQ1zhJHclvs"&gt;You will learn RxJS&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Essential talk that explains observables by building observables. If you plan on watching one talk only, make it this one.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=PhggNGsSQyg"&gt;Learn RxJS in 60 Minutes for Beginners&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A nice crash course that covers the basics of RxJS. Covers lots of ground.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=_q-HL9YX_pk"&gt;Mastering the Subject: Communication Options in RxJS | Dan Wahlin&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A really good explanation of RxJS subjects and how you can use them to communicate between different application components.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=vS1-jzngpmw"&gt;The Magic of RxJS - Natalia Tepluhina | JSHeroes 2019&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Shows how you can use RxJS to build a Pong game. Heavy code, but very inspirational talk!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=Z76QlSpYcck"&gt;Data Composition with RxJS | Deborah Kurata&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Really good talk that explains how you can use RxJS to fetch data and do cross-component communication. A must watch!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=-4cwkHNguXE"&gt;Thinking Reactively: Most Difficult | Mike Pearson&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Learn how to think reactively by building a typeahead search. Very good talk for RxJS newbies.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=KlmABSriUbw"&gt;Understanding RxJS Error Handling&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Excellent talk on different exception handling strategies in RxJS.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=ldRdjc-60PM"&gt;Why Should You Care About RxJS Higher-order Mapping Operators?&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Explains higher-order RxJS mapping operators with clear examples that all can understand.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=rUZ9CjcaCEw"&gt;I switched a map and you'll never guess what happened next&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Fun interactive talk that explains mapping operators with the help of hiring agency example.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=XKfhGntZROQ"&gt;RxJS Advanced Patterns – Operate Heavily Dynamic UI’s | Michael Hladky&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Advanced concepts talk that goes very deep. Expect to scrub a lot in order to understand everything.&lt;/p&gt;

&lt;h2&gt;
  
  
  Personal tips
&lt;/h2&gt;

&lt;p&gt;Here are some tips from me that can help you on your RxJS learning journey.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Don't give up! If will be hard, but if you give it time, it will all come together soon. Things will click, I promise!&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;tap&lt;/code&gt; operator for debugging your pipelines. Inject &lt;code&gt;tap(console.log)&lt;/code&gt; anywhere in your pipeline to view the data flowing through it.&lt;/li&gt;
&lt;li&gt;You will probably start by composing very large pipelines. It's OK. Later, try to write small single-purpose operators instead and learn how to combine them. View them as Lego bricks.&lt;/li&gt;
&lt;li&gt;Learn &lt;code&gt;of&lt;/code&gt;, &lt;code&gt;from&lt;/code&gt;, &lt;code&gt;merge&lt;/code&gt;, &lt;code&gt;BehaviourSubject&lt;/code&gt;, &lt;code&gt;combineLatest&lt;/code&gt;, &lt;code&gt;startWith&lt;/code&gt; and all the mapping operators. They will give you a solid base to stand on.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Exercises
&lt;/h2&gt;

&lt;p&gt;You can read articles and watch videos all you want, but in order to truly learn and understand you have to do some coding. Here are some good problems you can try to tackle with RxJS.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Timer App&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Try to create a simple timer app. You should be able to start, stop and reset the timer. Bonus points: be able to set a countdown time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Typeahead Search&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Try to implement a simple typeahead TV show search by using Episodate &lt;a href="https://www.episodate.com/api"&gt;API&lt;/a&gt;.&lt;/p&gt;

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

&lt;p&gt;RxJS is a wonderful technology, but it's not widely adopted yet. One of the main reasons might be the lack of good learning resources. Hopefully this resource will help your discover its super-powers.&lt;/p&gt;

&lt;p&gt;My main advice is to take it slow and don't give up! It will all click soon enough if you are persistent. Soon you will be wondering how you even could do something without RxJS and will mutter for yourself how easy you could have solved the problem if only this project used RxJS.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>rxjs</category>
      <category>javascript</category>
      <category>svelte</category>
    </item>
    <item>
      <title>How to send transactional HTML emails with Mailgun, Handlebars and Firebase</title>
      <dc:creator>Ilia Mikhailov</dc:creator>
      <pubDate>Wed, 25 Nov 2020 00:00:00 +0000</pubDate>
      <link>https://dev.to/codechips/how-to-send-transactional-html-emails-with-mailgun-handlebars-and-firebase-195b</link>
      <guid>https://dev.to/codechips/how-to-send-transactional-html-emails-with-mailgun-handlebars-and-firebase-195b</guid>
      <description>&lt;p&gt;If you are building any serious app chances are big that you have to send some emails. I needed to send transactional emails for a web app I am building, so I reached for the trusted Mailgun.&lt;/p&gt;

&lt;p&gt;The app itself is based on Firebase and Firebase Functions was an obvious choice for sending emails. What was not so obvious is how to put everything together. I went through the pain and finally figured it out.&lt;/p&gt;

&lt;p&gt;Below are my notes on how I got everything to work with &lt;a href="https://www.typescriptlang.org/"&gt;TypeScript&lt;/a&gt; using &lt;a href="https://www.mailgun.com/"&gt;Mailgun&lt;/a&gt;, &lt;a href="https://handlebarsjs.com/"&gt;Handlebars&lt;/a&gt; templates for HTML emails and &lt;a href="https://firebase.google.com/docs/functions"&gt;Firebase Functions&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Firebase Functions Setup
&lt;/h2&gt;

&lt;p&gt;I assume here that you already have a Firebase project set up with the local Firebase emulator running. If not, you can follow my guide - &lt;a href="https://codechips.me/local-development-with-firebase-emulator-and-snowpack/"&gt;Smooth local Firebase development setup with Firebase emulator and Snowpack&lt;/a&gt;. It targets &lt;a href="https://svelte.dev/"&gt;Svelte&lt;/a&gt;, but you can skip those parts if you are using some other framework or technology.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mailgun Setup
&lt;/h2&gt;

&lt;p&gt;There are many transactional email providers, but I chose &lt;a href="https://www.mailgun.com/"&gt;Mailgun&lt;/a&gt;. Mostly because of their cool name and logo, and also because they have a generous free quota (5K emails/month) and a nice Javascript SDK.&lt;/p&gt;

&lt;p&gt;I won't go through setting and configuring Mailgun, DNS and all that jazz. They already have good documentation on how to do it. Instead, I will concentrate on wiring everything up in code.&lt;/p&gt;

&lt;p&gt;There are several Mailgun libraries on NPM, but we will go with the official one. It's written in Javascript and if you are using Typescript you have to install the types.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ npm add mailgun.js @types/mailgun-js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because Mailgun SDK is written in Javascript you have to add &lt;code&gt;esModuleInterop&lt;/code&gt; to your &lt;code&gt;tsconfig.json&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;"module"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"commonjs"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"noImplicitReturns"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"noUnusedLocals"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"esModuleInterop"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"outDir"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lib"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"sourceMap"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"strict"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&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;"es2017"&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;"compileOnSave"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"include"&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="s2"&gt;"src"&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;All right! We are ready to roll!&lt;/p&gt;

&lt;h2&gt;
  
  
  Writing the email sender function
&lt;/h2&gt;

&lt;p&gt;To keep code clean we will keep the email sending logic in a separate file. Create a new &lt;code&gt;email.ts&lt;/code&gt; file in the &lt;code&gt;src&lt;/code&gt; directory.&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="c1"&gt;// email.ts&lt;/span&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;functions&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;firebase-functions&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Mailgun&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;mailgun-js&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;apiKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mailgun-api-key&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;domain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mg.example.com&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;mg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Mailgun&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;domain&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;send&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;subscribers&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="o"&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class="nx"&gt;mg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;send&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Mailgun Test &amp;lt;noreply@mg.example.com&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;subscribers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Mailgun test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;text&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! How are you today?&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;html&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;h1&amp;gt;Hello!&amp;lt;/h1&amp;gt;&amp;lt;p&amp;gt;How are you today?&amp;lt;/p&amp;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;So far, so good. With this code we can send an email to a bunch of subscribers whose email addresses are passed in as a list of strings.&lt;/p&gt;

&lt;h2&gt;
  
  
  Firebase Remote Config
&lt;/h2&gt;

&lt;p&gt;Mailgun SDK requires an API key to send email. While you can hardcode it in your code, as we did in the example above, you should never do it. Instead, we can leverage Firebase Remote Config for this and keep our Maingun API key securely stored in it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ firebase functions:config:get &amp;gt; .runtimeconfig.json

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

&lt;/div&gt;



&lt;p&gt;If you already don't have anything there you will get and empty JSON file. Let's add our Mailgun API key to it.&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;"mg"&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;"key"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"your-mailgun-api-key"&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;&lt;strong&gt;NOTE&lt;/strong&gt;: to set this value in production later you can use the command below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ firebase functions:config:set mg.key="your-mailgun-api-key"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also, make sure to keep all your config keys lowercase or Firebase will complain!&lt;/p&gt;

&lt;p&gt;Now, when the Firebase emulator starts it will pickup the local config. This means that we can get the key from the functions config.&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="c1"&gt;// email.ts&lt;/span&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;functions&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;firebase-functions&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// ...&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;apiKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;functions&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="nx"&gt;mg&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our basic email function is now done. Let's continue with setting up our Handlebars templates.&lt;/p&gt;

&lt;h2&gt;
  
  
  HTML email templates with Handlebars
&lt;/h2&gt;

&lt;p&gt;HTML email design is hard. It's like going back to the 90s again. Tables and all. Luckily there are plenty of apps that can help you with that. Maybe you even have an old copy of Adobe Dreamweaver laying around somewhere? If you a feeling adventurous.&lt;/p&gt;

&lt;p&gt;Jokes aside, if you want to learn more about HTML email design I can highly recommend reading &lt;a href="https://www.smashingmagazine.com/2017/01/introduction-building-sending-html-email-for-web-developers/"&gt;An Introduction To Building And Sending HTML Email For Web Developers&lt;/a&gt; on Smashing Magazine.&lt;/p&gt;

&lt;p&gt;But, we are aiming for the MVP here as this article's focus is on the technical implementation and not on email design.&lt;/p&gt;

&lt;p&gt;My goal was to keep the email templates separate from code and Handlebars is a good fit for that. With that said, it was not as straight forward as I first thought.&lt;/p&gt;

&lt;p&gt;Handlebars.js is one of the templating languages that allows you to precompile your templates, so you can later import them in code. It took me a while to get it right, but here is how you do it.&lt;/p&gt;

&lt;p&gt;First, let's install &lt;a href="https://handlebarsjs.com/"&gt;Handlebars&lt;/a&gt; and &lt;a href="https://github.com/mysticatea/npm-run-all"&gt;npm-run-all&lt;/a&gt; utility package that we will use for the precompile step.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ npm add handlebars npm-run-all
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next we need to create two Handlebars templates. One for html emails and one for text emails.&lt;/p&gt;

&lt;p&gt;Create an &lt;code&gt;emails&lt;/code&gt; folder in the root of the project and then create two files in it - &lt;code&gt;html.handlebars&lt;/code&gt; and &lt;code&gt;text.handlebars&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;{{!-- html.handlebars --}}

&lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;{{title}}&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;{{body}}&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;All the best, John&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Text template is needed for email clients that can't display HTML. I am looking at you &lt;a href="http://www.mutt.org/"&gt;Mutt&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;{{!-- text.handlebars --}}

{{title}}

{{body}}

All the best, John
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that we have created the templates we need to precompile them with this command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ npx handlebars emails/ -f src/templates.js -c handlebars/runtime
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It compiles the templates into &lt;code&gt;templates.js&lt;/code&gt; module and also includes the reference to &lt;code&gt;handlesbars/runtime&lt;/code&gt; module that is needed for some reason.&lt;/p&gt;

&lt;p&gt;Now you can import &lt;code&gt;templates.js&lt;/code&gt; in your code and both &lt;code&gt;text&lt;/code&gt; and &lt;code&gt;html&lt;/code&gt; templates will be available.&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="c1"&gt;// email.ts&lt;/span&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;functions&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;firebase-functions&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Mailgun&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;mailgun-js&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="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;Handlebars&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;handlebars/runtime&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./templates&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;apiKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;functions&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="nx"&gt;mg&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;domain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mg.example.com&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;mg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Mailgun&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;domain&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// import our email and text precompiles templates&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Handlebars&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;templates&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;html&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;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Handlebars&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;templates&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text&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;send&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;subscribers&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="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;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Mailgun Test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;How are you?&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;mg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;send&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Mailgun Test &amp;lt;noreply@mg.example.com&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;subscribers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;subject&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;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;text&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="na"&gt;html&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;html&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="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;Our email send function is now complete, but let's streamline our development environment a bit, so that Handlebars templates are precompiled automatically before we build our functions.&lt;/p&gt;

&lt;p&gt;Change the &lt;code&gt;scripts&lt;/code&gt; section in functions' &lt;code&gt;package.json&lt;/code&gt; to this.&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="nl"&gt;"scripts"&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;"build"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"run-s build:templates build:tsc"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"build:tsc"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tsc"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"build:templates"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"handlebars emails/ -f src/templates.js -c handlebars/runtime"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"serve"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npm run build &amp;amp;&amp;amp; firebase serve --only functions"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"shell"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npm run build &amp;amp;&amp;amp; firebase functions:shell"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npm run shell"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"deploy"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"firebase deploy --only functions"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"logs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"firebase functions:log"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When we now execute &lt;code&gt;npm run build&lt;/code&gt; Handlebars will compile the templates first before we compile function. This is done with &lt;code&gt;run-s&lt;/code&gt; (s is for serial) which is a part of the &lt;code&gt;npm-run-all&lt;/code&gt; utility package.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wiring everything together
&lt;/h2&gt;

&lt;p&gt;Now that we have all the different parts done, let's wire them all together.&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="c1"&gt;// functions/src/index.ts&lt;/span&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;functions&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;firebase-functions&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;send&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;./email&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;sendEmail&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;functions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;https&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onCall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;data&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;subscribers&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&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="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;subscribers&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;ok&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="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&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;ok&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="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&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;That's it! Now you can send transactional emails with Mailgun through Firebase Functions. I kept the example minimal in order to keep things simple, but you can fetch data from Firestore or send the email in the Firebase trigger functions. Imagination is the limit!&lt;/p&gt;

&lt;h2&gt;
  
  
  BONUS: Firebase Functions Emulator Startup
&lt;/h2&gt;

&lt;p&gt;If you what to start both Firebase emulator and function compilation watch, when you start the project, change the &lt;code&gt;scripts&lt;/code&gt; section of your main project's  &lt;code&gt;package.json&lt;/code&gt; to this.&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="nl"&gt;"scripts"&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;"build"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"run-p build:*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"build:functions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cd functions &amp;amp;&amp;amp; npm run build"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"firebase:deploy:functions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"firebase deploy --only functions"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"firebase:start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"firebase emulators:start --only functions"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"run-p watch:* firebase:start"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"watch:functions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cd functions &amp;amp;&amp;amp; npm run watch"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Libraries Mentioned
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/mailgun/mailgun-js"&gt;https://github.com/mailgun/mailgun-js&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.npmjs.com/package/@types/mailgun-js"&gt;https://www.npmjs.com/package/@types/mailgun-js&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/handlebars-lang/handlebars.js"&gt;https://github.com/handlebars-lang/handlebars.js&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/mysticatea/npm-run-all"&gt;https://github.com/mysticatea/npm-run-all&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;This is just one way to send transactional emails from Firebase functions. To keep templates separate from code we had to precompile our email templates as Firebase functions can't deal with file system very well. I am sure there are other templating languages that support precompilation, but Handlebars is good enough for the task.&lt;/p&gt;

&lt;p&gt;You can find complete example code on Github.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/codechips/firebase-functions-mailgun-handlebars-example"&gt;https://github.com/codechips/firebase-functions-mailgun-handlebars-example&lt;/a&gt;&lt;/p&gt;

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

</description>
      <category>firebase</category>
      <category>webdev</category>
      <category>mailgun</category>
    </item>
    <item>
      <title>Transforming an object to array in JavaScript</title>
      <dc:creator>Ilia Mikhailov</dc:creator>
      <pubDate>Mon, 09 Nov 2020 00:00:00 +0000</pubDate>
      <link>https://dev.to/codechips/transforming-an-object-to-array-in-javascript-1c4e</link>
      <guid>https://dev.to/codechips/transforming-an-object-to-array-in-javascript-1c4e</guid>
      <description>&lt;p&gt;When working with objects in JavaScript sometimes it's convenient to transform them to arrays. In this short post I will show you how to do it in plain JavaScript and also with Rambda.js.&lt;/p&gt;

&lt;p&gt;Let's say that we have a nested JavaScript object that looks like this. If you work with Firebase Firestore, or some of Google Cloud Platform's or AWS API, you might have stumbled on structures like these.&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;obj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;one&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;services&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="s1"&gt;service-foo&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;slug&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;foo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;public&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;readonly&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;service-bar&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;slug&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;public&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="na"&gt;readonly&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;users&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="s1"&gt;john&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;jane&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="s1"&gt;service-baz&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;slug&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;baz&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;public&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="na"&gt;readonly&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="na"&gt;users&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="s1"&gt;bo&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;bill&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;brooke&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;Say we want to display the &lt;code&gt;services&lt;/code&gt; property as a list on the page. While it's possible to loop over the object itself, it's more convenient to loop over a list that looks 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="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;service-foo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;foo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;public&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;readonly&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;users&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="s1"&gt;frank&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;finn&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;fernando&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="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;service-bar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;public&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="na"&gt;readonly&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;users&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="s1"&gt;john&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;jane&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="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;service-baz&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;baz&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;public&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="na"&gt;readonly&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="na"&gt;users&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="s1"&gt;bo&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;bill&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;brooke&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;How can we transform our our &lt;code&gt;services&lt;/code&gt; property object to an array?&lt;/p&gt;

&lt;h2&gt;
  
  
  Transforming with plain JavaScript
&lt;/h2&gt;

&lt;p&gt;One obvious way is to do it in plain JavaScript. 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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;transform&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&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;id&lt;/span&gt;&lt;span class="p"&gt;,&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="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}));&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;trasform&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="nx"&gt;services&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We map over object's keys and construct a new object consisting of the object's key and its properties with the help of the ES spread operator.&lt;/p&gt;

&lt;h2&gt;
  
  
  Transforming object with Rambda.js
&lt;/h2&gt;

&lt;p&gt;I was curious how to achieve the same results with &lt;a href="https://ramdajs.com/"&gt;Rambda&lt;/a&gt; - a functional programming library for JavaScript.&lt;/p&gt;

&lt;p&gt;Turns out there are two ways to do it.&lt;/p&gt;

&lt;p&gt;First one is to use Ramda's &lt;a href="https://ramdajs.com/docs/#pipe"&gt;pipe&lt;/a&gt; operator.&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;R&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&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;rambda&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;transform&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;R&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;R&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toPairs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;R&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(([&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt; &lt;span class="p"&gt;}))&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;trasform&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="nx"&gt;services&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Second is to use Ramda's &lt;a href="https://ramdajs.com/docs/#compose"&gt;compose&lt;/a&gt; operator.&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;R&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&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;rambda&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;transform&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;R&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;compose&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;R&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(([&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt; &lt;span class="p"&gt;})),&lt;/span&gt;
  &lt;span class="nx"&gt;R&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toPairs&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;trasform&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="nx"&gt;services&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both results in the same output. It's only a matter of taste. If you are coming form an FP background, &lt;code&gt;compose&lt;/code&gt; will look more natural to you.&lt;/p&gt;

&lt;p&gt;Let's break down Rambda's &lt;a href="https://ramdajs.com/docs/#toPairs"&gt;toPairs&lt;/a&gt; operator. Maybe a better name for it would be &lt;code&gt;toTuple&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;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;R&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toPairs&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="nx"&gt;services&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;service-foo&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;slug&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;foo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;public&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;readonly&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;users&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;Array&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;service-bar&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;slug&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;public&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="na"&gt;readonly&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;users&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;Array&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;service-baz&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;slug&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;baz&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;public&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="na"&gt;readonly&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="na"&gt;users&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;Array&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;The &lt;code&gt;toPairs&lt;/code&gt; operator loops over object's keys and returns key and property a list with two items in it - &lt;code&gt;[key, prop]&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In functional programming you often work with lists as they are easier to manipulate.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rambda - pipe vs compose
&lt;/h2&gt;

&lt;p&gt;What the difference when it comes to the two composition operators in Rambda - &lt;code&gt;pipe&lt;/code&gt; and &lt;code&gt;compose&lt;/code&gt;? It's subtle. In my opinion it's a matter of taste.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;pipe&lt;/code&gt; operator feels most intuitive to developers. It works like a pipe in Unix (&lt;code&gt;ls -l | grep js&lt;/code&gt;), passing the results from one function to another in the chain from left to right. First function can take multiple arguments, but the rest of the functions in the chain can only have one argument as input. It reminds me a lot about the proposed &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Pipeline_operator"&gt;pipeline&lt;/a&gt; operator in JavaScript.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;compose&lt;/code&gt; operator does the opposite of pipe. It performs its operations right to left.&lt;/p&gt;

&lt;p&gt;I find it intuitive to think of it as the Russian Doll pattern - &lt;code&gt;f4(f3(f2(f1([1,2,3]))))&lt;/code&gt;, where each function is called inside out.&lt;/p&gt;

&lt;p&gt;There are probably a few more ways to transform an object to array. Let me know then so I can learn something new.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>rambda</category>
      <category>tips</category>
      <category>devtips</category>
    </item>
    <item>
      <title>Snowpack for Svelte development revisited</title>
      <dc:creator>Ilia Mikhailov</dc:creator>
      <pubDate>Fri, 30 Oct 2020 00:00:00 +0000</pubDate>
      <link>https://dev.to/codechips/snowpack-for-svelte-development-revisited-43h9</link>
      <guid>https://dev.to/codechips/snowpack-for-svelte-development-revisited-43h9</guid>
      <description>&lt;p&gt;As I am writing this I realize that I have no good introduction. This is another post about finding the best bundler for Svelte and this time it's Snowpack's turn to be evaluated.&lt;/p&gt;

&lt;p&gt;Building a good bundler is not easy. Building a fast bundler is almost impossible. Mad props to those who dare.&lt;/p&gt;

&lt;p&gt;I discovered &lt;a href="https://www.snowpack.dev/"&gt;Snowpack&lt;/a&gt; before version 2 was officially released and was very excited about it. Today I decided to revisit it to see how and if things have changed since.&lt;/p&gt;

&lt;p&gt;My goal is to compare different bundlers for Svelte development in order to find the best one. I've already tested &lt;a href="https://codechips.me/svelte-with-vitejs-typescript-tailwind/"&gt;Vite&lt;/a&gt;, &lt;a href="https://codechips.me/svelte-postcss-and-typescript-with-svite/"&gt;Svite&lt;/a&gt; and now it's Snowpack's turn.&lt;/p&gt;

&lt;h2&gt;
  
  
  Requirements
&lt;/h2&gt;

&lt;p&gt;For the purpose of evaluation I created a simple web app. Its functionality is simple. You press a button and it fetches a random Kanye West tweet from &lt;a href="https://kanye.rest/"&gt;Kanye as a Service&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Wav44z-V--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/codechips/image/upload/v1598252607/kanye-says-app_rup4n6.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Wav44z-V--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/codechips/image/upload/v1598252607/kanye-says-app_rup4n6.webp" alt="Kanye Says app screenshot" width="880" height="592"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;While simple, the application has interesting parts.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Svelte components are written in Typescript&lt;/strong&gt;. I want to see if transpiling and type checking works correctly for TS.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;External Svelte library&lt;/strong&gt;. Not all bundlers support libraries written in Svelte efficiently.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;External library dependency&lt;/strong&gt;. I want to see if a bundler supports tree shaking when bundling for production.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;External Assets&lt;/strong&gt;. It should be possible to import SVG, PNG, JSON and other external assets in our code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PostCSS with TailwindCSS&lt;/strong&gt;. A good bundler should make it easy to work with SASS and PostCSS.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Business components in Typescript&lt;/strong&gt;. Typescript is here to stay. A good bundler should support it out-of-the-box.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I also have a list of my own requirements when it comes to bundlers.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It must be fast&lt;/li&gt;
&lt;li&gt;It must support Typescript&lt;/li&gt;
&lt;li&gt;It must support PostCSS&lt;/li&gt;
&lt;li&gt;It must produce small and efficient bundles&lt;/li&gt;
&lt;li&gt;It must produce correct sourcemaps for debugging&lt;/li&gt;
&lt;li&gt;It should support HMR (Hot Module Replacement)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's see if Snowpack can satisfy all of them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bootstrapping the app
&lt;/h2&gt;

&lt;p&gt;Snowpack comes with many official framework templates that you can use as base for your apps. Since the app is written in TypeScript we will use &lt;a href="https://github.com/snowpackjs/snowpack/tree/master/create-snowpack-app/app-template-svelte-typescript"&gt;@snowpack/app-template-svelte-typescript&lt;/a&gt;. It's not listed on the website, but in Github.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ npx create-snowpack-app svelte-snowpack-typescript \
  --template @snowpack/app-template-svelte-typescript --use-pnpm
$ cd svelte-snowpack-typescript
# add test app's dependencies
$ pnpm add -D date-fns svelte-inline-svg
$ pnpm start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I like how Snowpack lets me specify the package manager. &lt;em&gt;pnpm&lt;/em&gt;, the package manger I often use, is not in the main documentation, but I found the option somewhere in Github.&lt;/p&gt;

&lt;p&gt;Dev server starts instantly and Snowpack is nice enough to open the browser for me. I appreciate it, but I don't like it or need it.&lt;/p&gt;

&lt;p&gt;Another thing I like about Snowpack is that it uses npm &lt;code&gt;start&lt;/code&gt; script and not &lt;code&gt;dev&lt;/code&gt; to start everything up. The &lt;code&gt;npm run dev&lt;/code&gt; command is so 2015, right?&lt;/p&gt;

&lt;p&gt;I also noticed that app template's config file has changed from JSON to JS. I like it because it makes it easy to comment and do other advanced JS gymnastics.&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;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="na"&gt;mount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;public&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="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/_dist_&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;plugins&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="s1"&gt;@snowpack/plugin-svelte&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;@snowpack/plugin-dotenv&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;@snowpack/plugin-typescript&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="s1"&gt;@snowpack/plugin-run-script&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;cmd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;svelte-check --output human&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;watch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;$1 --watch&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;stream&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="na"&gt;install&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="cm"&gt;/* ... */&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;installOptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cm"&gt;/* ... */&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;devOptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// don't open browser&lt;/span&gt;
    &lt;span class="na"&gt;open&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;none&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="c1"&gt;// don't clear the output&lt;/span&gt;
    &lt;span class="na"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;stream&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;buildOptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cm"&gt;/* ... */&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;proxy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cm"&gt;/* ... */&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;alias&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cm"&gt;/* ... */&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So far so good.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's in the box?
&lt;/h2&gt;

&lt;p&gt;Let's see what files Svelte template generates for us.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ls -1
babel.config.json
jest.config.js
jest.setup.js
LICENSE
node_modules
package.json
pnpm-lock.yaml
public
README.md
snowpack.config.js
src
svelte.config.js
tsconfig.json
types
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Not bad. I don't know why Babel config has to be explicit and I am usually not writing any unit tests, so jest has no use for me. But other that that it looks nice and clean.&lt;/p&gt;

&lt;p&gt;The app entry file comes with an explicit &lt;em&gt;HMR&lt;/em&gt; section. I like that it's explicit and how it tells you where to find more information. Good!&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;App&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;./App.svelte&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Hot Module Replacement (HMR) - Remove this snippet to remove HMR.&lt;/span&gt;
&lt;span class="c1"&gt;// Learn more: https://www.snowpack.dev/#hot-module-replacement&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;import&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hot&lt;/span&gt;&lt;span class="p"&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;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;accept&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;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hot&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$destroy&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;By now I've copied the code from my test web app, added missing dependencies and tried to start the dev server. Things don't quite work yet.&lt;/p&gt;

&lt;h2&gt;
  
  
  External Svelte libraries
&lt;/h2&gt;

&lt;p&gt;This is where I hit the first road block. Looks like Snowpack uses &lt;code&gt;pkg.module&lt;/code&gt; file if it exists. The &lt;a href="https://github.com/robinscholz/svelte-inline-svg"&gt;svelte-inline-svg&lt;/a&gt; library I am using had the &lt;code&gt;module&lt;/code&gt; property defined, but the file was missing in the NPM package. Showpack borked on this. I wish that it could have fallbacks in place and try with something else if the file is missing. The author of the SVG library has fixed it after I filed a bug report.&lt;/p&gt;

&lt;h2&gt;
  
  
  PostCSS + TailwindCSS
&lt;/h2&gt;

&lt;p&gt;Snowpack has no PostCSS support out-of-the box, because in Snowpack it's all about plugins and build scripts, but it offers instructions on the main documentation page on how to enable PostCSS. But turns out there is some ambiguity. The main documentation webpage tells &lt;a href="https://www.snowpack.dev/#postcss"&gt;one thing&lt;/a&gt; while Github tells &lt;a href="https://github.com/snowpackjs/snowpack/tree/master/plugins/plugin-postcss"&gt;another&lt;/a&gt;. Which one to trust?&lt;/p&gt;

&lt;p&gt;Alright, let's go with the second one and use Snowpack's &lt;a href="https://github.com/snowpackjs/snowpack/tree/master/plugins/plugin-postcss"&gt;plugin-postcss&lt;/a&gt;. Not sure I understand why I have to explicitly install &lt;code&gt;postcss&lt;/code&gt; and &lt;code&gt;postcss-cli&lt;/code&gt;. Wouldn't it be better if they were baked into the Snowpack's PostCSS plugin?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ pnpm add @snowpack/plugin-postcss postcss postcss-cli
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we need to add the plugin to &lt;code&gt;snowpack.config.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="nx"&gt;plugins&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@snowpack/plugin-postcss&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;Talking about plugins. Snowpack has quite strange plugin configuration. You can either supply plugin as string, or as an array if plugin needs some arguments, where first item is the name of the plugin and second is an object of the arguments that will be passed to the plugin.&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@snowpack/plugin-run-script&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;cmd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;svelte-check --output human&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;watch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;$1 --watch&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;stream&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;Let's install and setup Tailwind and friends.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ pnpm add -D tailwindcss postcss-preset-env
$ pnpx tailwind init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace Tailwind configuration file with the following contents.&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;// tailwind.config.js&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="na"&gt;future&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;removeDeprecatedGapUtilities&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;purgeLayersByDefault&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="na"&gt;experimental&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;uniformColorPalette&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;extendedFontSizeScale&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="na"&gt;purge&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;content&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="s1"&gt;./src/**/*.svelte&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;./public/*.html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;whitelistPatterns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;/svelte-/&lt;/span&gt;&lt;span class="p"&gt;],&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;extend&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;variants&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
  &lt;span class="na"&gt;plugins&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;And &lt;code&gt;postcss.config.js&lt;/code&gt; with this contents.&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;// postcss.config.js&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tailwind&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&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;tailwindcss&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;purge&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&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;@fullhuman/postcss-purgecss&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;cssnano&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&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;cssnano&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;presetEnv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&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;postcss-preset-env&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)({&lt;/span&gt; &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&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;plugins&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NODE_ENV&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;production&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;tailwind&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;presetEnv&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;cssnano&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;tailwind&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;presetEnv&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;plugins&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Environment Variables
&lt;/h2&gt;

&lt;p&gt;Svelte template comes with &lt;code&gt;dotenv&lt;/code&gt; pre-packaged as Snowpack &lt;a href="https://github.com/snowpackjs/snowpack/tree/master/plugins/plugin-dotenv"&gt;plugin&lt;/a&gt;. This is nice as it doesn't require a working &lt;code&gt;dotenv&lt;/code&gt; environment in the shell.&lt;/p&gt;

&lt;p&gt;The ES2020 &lt;code&gt;import.meta.env&lt;/code&gt; works out of the box, but not in my Vim editor.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ytQXNaV0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/codechips/image/upload/v1604053708/posts/snowpack-revisited/snowpack-ts-editor-error_n5rbmn.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ytQXNaV0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/codechips/image/upload/v1604053708/posts/snowpack-revisited/snowpack-ts-editor-error_n5rbmn.webp" alt="svelte + ts error in vim" width="880" height="336"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;VS Code works fine and you get autocompletion too. I figured that &lt;code&gt;coc-svelte&lt;/code&gt; plugin I am using is way behind VS Codes Svelte extension. As soon as I forked and updated dependencies, Vim stopped complaining.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: If you are using Vim, coc.nvim and coc-svelte extension, you can use mine. It's more up-to-date than the original one &lt;a href="https://github.com/codechips/coc-svelte"&gt;@codechips/coc-svelte&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You have to prefix all env variables with &lt;code&gt;SNOWPACK_&lt;/code&gt; if you want them to be accessible in the app. Most bundlers today follow this convention in order to avoid environment variable clashes.&lt;/p&gt;

&lt;p&gt;For my app I had to create an &lt;code&gt;.env.local&lt;/code&gt; file so that &lt;code&gt;dotnenv&lt;/code&gt; could pick it up, but any &lt;code&gt;dotenv&lt;/code&gt; convention works.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# .env.local
SNOWPACK_PUBLIC_KANYE_API=https://api.kanye.rest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Snowpack and svelte.config.js
&lt;/h2&gt;

&lt;p&gt;As I understand &lt;code&gt;svelte.config.js&lt;/code&gt; file is mostly only used by code editors for autocompletion. Not with Snowpack. Snowpack is one of the few bundlers that relies on that file to exist. It's used by its Svelte plugin. Good thing that it comes with the template.&lt;/p&gt;

&lt;h2&gt;
  
  
  Snowpack, Svelte and TypesScript
&lt;/h2&gt;

&lt;p&gt;TypeScript is nice, but getting TypeScript configuration right is usually a game of trial and error for me. Snowpack's Svelte template has a dependency on the &lt;code&gt;@tsconfig/svelte&lt;/code&gt; NPM package, but from what I can see it's not used anywhere?&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;svelte-inline-svg&lt;/code&gt; module does not come with any TypeScript declaration. We need to fake it locally.&lt;/p&gt;

&lt;p&gt;Create &lt;code&gt;svelte-inline-svg.d.ts&lt;/code&gt; in the &lt;code&gt;types&lt;/code&gt; directory with the following contents.&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="kr"&gt;declare&lt;/span&gt; &lt;span class="kr"&gt;module&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;svelte-inline-svg&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;By the way, I really like how the &lt;code&gt;svelte-check&lt;/code&gt; tool, which comes already injected into the build pipeline, warns me about it and provides instruction on what to do.&lt;/p&gt;

&lt;p&gt;I also like that Snowpack has a dedicated &lt;code&gt;types&lt;/code&gt; directory for your own TypeScript definitions. Somehow, I always struggle to configure type paths. Snowpack is the first bundler that makes this clear for me.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building
&lt;/h2&gt;

&lt;p&gt;The test app is now finally working! Lets build it for production.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ pnpm build
&amp;gt; snowpack build
...

[@snowpack/plugin-typescript] src/index.ts(2,17): error TS2307: Cannot find module './App.svelte' or its corresponding type declarations.
[@snowpack/plugin-typescript] Error: Command failed with exit code 2: tsc --noEmit
src/index.ts(2,17): error TS2307: Cannot find module './App.svelte' or its corresponding type declarations.
 ERROR  Command failed with exit code 1.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Oops. Something is wrong, but what? I actually never figured out what, so I recreated the whole project from scratch, which helped. Who told you that this was going to be easy, right?&lt;/p&gt;

&lt;p&gt;When building Snowpack shows you a nice stats table of all the build artifacts.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9d87_vce--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/codechips/image/upload/v1604053708/posts/snowpack-revisited/snowpack-build-stats_msatng.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9d87_vce--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/codechips/image/upload/v1604053708/posts/snowpack-revisited/snowpack-build-stats_msatng.webp" alt="Snowpack stats table" width="844" height="470"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But what's in the build?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;tree -h build
build
├── [4.0K]  _dist_
│   ├── [1.4K]  App.js
│   ├── [4.0K]  assets
│   │   ├── [ 899]  usa.svg
│   │   └── [  40]  usa.svg.proxy.js
│   ├── [ 14K]  index.css
│   ├── [ 15K]  index.css.proxy.js
│   ├── [ 334]  index.js
│   ├── [1.5K]  Time.js
│   ├── [ 539]  timer.js
│   ├── [ 721]  Wisdom.css
│   ├── [1.0K]  Wisdom.css.proxy.js
│   └── [5.9K]  Wisdom.js
├── [1.1K]  favicon.ico
├── [ 882]  index.html
├── [1.1K]  logo.svg
├── [  67]  robots.txt
├── [4.0K]  __snowpack__
│   └── [ 126]  env.js
└── [4.0K]  web_modules
    ├── [4.0K]  common
    │   └── [ 16K]  index-2f302f93.js
    ├── [ 82K]  date-fns.js
    ├── [ 273]  import-map.json
    ├── [4.0K]  svelte
    │   ├── [ 448]  internal.js
    │   ├── [2.0K]  store.js
    │   └── [ 308]  transition.js
    ├── [4.9K]  svelte-inline-svg.js
    └── [  59]  svelte.js

6 directories, 24 files
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You have &lt;code&gt;__dist__&lt;/code&gt; folder with all your application files, &lt;code&gt;__snowpack__&lt;/code&gt; folder with the env variables, and &lt;code&gt;web_modules&lt;/code&gt; folder with all the dependencies.&lt;/p&gt;

&lt;p&gt;Snowpack bundles your assets (CSS, SVG) files as JS files, the &lt;code&gt;.proxy.js&lt;/code&gt; ones. I wonder why it also puts raw SVG and CSS file in the build directory if they are not used in production.&lt;/p&gt;

&lt;p&gt;The dependencies are not bundled, but server to browser as ES modules. After all, Snowpack is an ESM bundler.&lt;/p&gt;

&lt;p&gt;This has its advantages and drawbacks. The development is super-fast as only the files changes need to be recompiled and reloaded. The files and dependencies are not optimized and served as is, which leads to many more requests if you have many dependencies and also to larger downloads as you need to download the whole module even if you only use one function in it.&lt;/p&gt;

&lt;p&gt;You might need to only do it once on the first load, then the files might be cached, but I am not the expert on this.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bundling a Snowpack project
&lt;/h2&gt;

&lt;p&gt;Ah! The ultimate test for a bundler. How efficient bundles a bundler can produce. Snowpack doesn't have bundler support built-in, but there are some plugins that we can use.&lt;/p&gt;

&lt;p&gt;Some time while ago Snowpack released an optimization plugin. It's not a bundler plugin, but more of a minifier. It helps you minify CSS, HTML and JS. From what I have read, this plugin uses ESBuild's minifying functionality and from what I understand it's almost on par with Rollup's.&lt;/p&gt;

&lt;p&gt;Enough talking, let's test!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ pnpm add -D @snowpack/plugin-optimize
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We need to add it to the list of plugins in &lt;code&gt;snowpack.config.js&lt;/code&gt; and do a new build.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Z7aV7q1N--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/codechips/image/upload/v1604053708/posts/snowpack-revisited/snowpack-build-optimize_mrcbl8.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Z7aV7q1N--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/codechips/image/upload/v1604053708/posts/snowpack-revisited/snowpack-build-optimize_mrcbl8.webp" alt="Snowpack stats table after optimization" width="841" height="488"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Strange. I see no difference in the stats table output.&lt;/p&gt;

&lt;p&gt;The terminal output is saying that file size didn't change, but if I look at the actual file on disk it's actually 21K and not 83K as Snowpack tells me.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ tree -h build
build
├── [4.0K]  _dist_
│   ├── [ 768]  App.js
│   ├── [4.0K]  assets
│   │   ├── [ 899]  usa.svg
│   │   └── [  40]  usa.svg.proxy.js
│   ├── [5.0K]  index.css
│   ├── [ 14K]  index.css.proxy.js
│   ├── [ 251]  index.js
│   ├── [ 860]  Time.js
│   ├── [ 305]  timer.js
│   ├── [ 554]  Wisdom.css
│   ├── [ 893]  Wisdom.css.proxy.js
│   └── [2.7K]  Wisdom.js
├── [1.1K]  favicon.ico
├── [ 416]  index.html
├── [1.1K]  logo.svg
├── [  67]  robots.txt
├── [4.0K]  __snowpack__
│   └── [ 126]  env.js
└── [4.0K]  web_modules
    ├── [4.0K]  common
    │   └── [6.1K]  index-2f302f93.js
    ├── [ 21K]  date-fns.js
    ├── [ 273]  import-map.json
    ├── [4.0K]  svelte
    │   ├── [ 421]  internal.js
    │   ├── [ 564]  store.js
    │   └── [ 217]  transition.js
    ├── [4.8K]  svelte-inline-svg.js
    └── [  54]  svelte.js

6 directories, 24 files
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Weird. I don't know if it's cached somewhere or if the numbers are showing something else.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing @snowpack/plugin-webpack
&lt;/h2&gt;

&lt;p&gt;Looks like Snowpack's plugin-optimize does some light optimization, but Showpack also has a real official bundler plugin - &lt;a href="https://github.com/snowpackjs/snowpack/tree/master/plugins/plugin-webpack"&gt;plugin-snowpack&lt;/a&gt;. It actually use to have an official Parcel plugin, but looks like it has been removed.&lt;/p&gt;

&lt;p&gt;Last time I tried Snowpack's Parcel and Snowpack plugin none of them worked. Has anything changed? Let's try Webpack plugin.&lt;/p&gt;

&lt;p&gt;Nope. Still getting the same "'babel-loader' not found" error from it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Entry module not found: Error: Can't resolve 'babel-loader'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It installed it, but it didn't help. I give up. Don't feel like I want do go down the rabbit hole.&lt;/p&gt;

&lt;p&gt;As of Rollup, no official Rollup plugin exists (yet), but Svelte crew promised that it's coming soon.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building an SWC compiler plugin for Snowpack
&lt;/h2&gt;

&lt;p&gt;Snowpack is all about plugins and they have great documentation on how to build your own. They actually encourage people to build them.&lt;/p&gt;

&lt;p&gt;I decided to test and build an SWC compiler plugin for TypeScript files to see how hard it would be. Just for fun.&lt;/p&gt;

&lt;p&gt;Let's get dirty. Install the &lt;a href="https://swc.rs"&gt;SWC compiler&lt;/a&gt; and create a &lt;code&gt;plugins&lt;/code&gt; directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ pnpm add -D @swc/core &amp;amp;&amp;amp; mkdir plugins
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the new plugins directory create a file named &lt;code&gt;plugin-swc.js&lt;/code&gt; with the following contents.&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;// plugins/plugin-swc.js&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;swc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&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;@swc/core&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;fs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&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;fs&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="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;snowpackConfig&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// read options from the main Snowpack config file&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;useSourceMaps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;snowpackConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;buildOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sourceMaps&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;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;snowpack-swc&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;input&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="s1"&gt;.ts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="na"&gt;output&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="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="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;load&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;filePath&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// read the TypeScript file&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;contents&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;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;promises&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;readFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;utf-8&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="c1"&gt;// transform it with SWC compiler&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;output&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;swc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;sourceMaps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;useSourceMaps&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;isModule&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;jsc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;syntax&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;typescript&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;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;esnext&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;

      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.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="na"&gt;code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;output&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;code&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="nx"&gt;output&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="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;Now, add it to the Snowpack config.&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="nx"&gt;plugins&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="s1"&gt;@snowpack/plugin-svelte&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;@snowpack/plugin-dotenv&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;@snowpack/plugin-typescript&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;@snowpack/plugin-optimize&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;@snowpack/plugin-postcss&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;./plugins/snowpack-swc.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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@snowpack/plugin-run-script&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;cmd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;svelte-check --output human&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;watch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;$1 --watch&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;stream&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;Start the dev server and be amazed. Your TypeScript files are now being transpiled by the SWC compiler.&lt;/p&gt;

&lt;p&gt;Well, I am not actually 100% sure about that, because Snowpack comes with &lt;a href="https://github.com/snowpackjs/snowpack/tree/master/snowpack/src/plugins"&gt;ESBuild plugin&lt;/a&gt; baked-in and I don't know if my SWC plugin overrides it or not.&lt;/p&gt;

&lt;p&gt;Anyhow, this was for demonstration purpose only to show how easy it is to build a plugin in Snowpack with only a few lines of code.&lt;/p&gt;

&lt;p&gt;If you look at Snowpack's official plugins, most of them are really simple to understand and only a few hundred lines long.&lt;/p&gt;

&lt;p&gt;I really like this plugin approach. It makes it very easy to build your own and developers have already started building &lt;a href="https://www.npmjs.com/search?q=keywords:snowpack%20plugin"&gt;all sorts of plugins&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Requirements Revisited
&lt;/h2&gt;

&lt;p&gt;Sorry for getting side-tracked. The test web app now actually works. Let us revisit the list of initial requirements and see how well Snowpack did.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;It must be fast&lt;/strong&gt;. Check. Snowpack is very fast to start.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It must support Typescript&lt;/strong&gt;. Check. Works out-of-the-box with the TypeScript plugin.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It must support PostCSS&lt;/strong&gt;. Check. Works with PostCSS plugin.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It must produce small and efficient bundles&lt;/strong&gt;. Nope. No bundler plugin works with Svelte.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It should be possible to import external Svelte libs&lt;/strong&gt;. Check.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It must produce correct sourcemaps for debugging&lt;/strong&gt;. Nope. All files are transpiled to JS.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It should support HMR (Hot Module Replacement)&lt;/strong&gt;. Check. Works great.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Plugins Mentioned
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/robinscholz/svelte-inline-svg"&gt;https://github.com/robinscholz/svelte-inline-svg&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/snowpackjs/snowpack/tree/master/plugins/plugin-optimize"&gt;https://github.com/snowpackjs/snowpack/tree/master/plugins/plugin-optimize&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/snowpackjs/snowpack/tree/master/plugins/plugin-webpack"&gt;https://github.com/snowpackjs/snowpack/tree/master/plugins/plugin-webpack&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/ParamagicDev/snowpack-plugin-rollup-bundle"&gt;https://github.com/ParamagicDev/snowpack-plugin-rollup-bundle&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Snowpack's underlying build pipeline design is very good. It feels thought through.&lt;/p&gt;

&lt;p&gt;There are a few parts that I like. I like that it uses &lt;code&gt;start&lt;/code&gt; script as the entry point to start the dev server. It always felt more intuitive to me than &lt;code&gt;npm run dev&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;When the dev server is disconnected the web app does not spam Dev Tools console log trying to reconnect to the dev server.&lt;/p&gt;

&lt;p&gt;SvelteKit, the newly announced Svelte framework that will replace Sapper, is betting on Snowpack. According to Svelte's crew they evaluated Vite.js as an alternative, but found it not to be framework agnostic enough.&lt;/p&gt;

&lt;p&gt;Snowpack does not have any official bundler plugin that works with Svelte, but as I understand the Svelte gang will work on making a Rollup plugin. Don't know if it will be only as a part of SvelteKit's build pipeline or an official framework-agnostic Snowpack Rollup plugin.&lt;/p&gt;

&lt;p&gt;But maybe Rollup plugin is not needed at all? From what I've read &lt;a href="https://github.com/evanw/esbuild/issues/50#issuecomment-639253432"&gt;esbuild has tree shaking functionality&lt;/a&gt; that is almost on par with Rollup's. I don't think that it's used in Snowpack yet and don't know either if there are any plans to implement it.&lt;/p&gt;

&lt;p&gt;As I wrote earlier, the official Webpack plugin didn't work for me, but since it's easy to build plugins in Snowpack and the documentation is good, I bet that we can expect a lot of new and interesting plugins from the developer community in the near future. Like this one for example, &lt;a href="https://www.syntaxsuccess.com/viewarticle/svelte-with-snowpack-and-closure-compiler"&gt;Svelte with Snowpack with Google Closure Compiler&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Overall, Snowpack is an interesting and future-glancing project with a helpful community and a fierce development speed. But somehow it feels like it's rushing forward a little too fast for its own good. A lot of features feel half-finished and documentation, although great, often lags behind or is outdated.&lt;/p&gt;

&lt;p&gt;Sorry Snowpack, I like you, but I will use another bundler for my Svelte projects for now. But don't be sad! I will come and visit in a few months. I am sure it will be a nice visit.&lt;/p&gt;

&lt;p&gt;You can find the final code here &lt;a href="https://github.com/codechips/svelte-typescript-setups"&gt;https://github.com/codechips/svelte-typescript-setups&lt;/a&gt;&lt;/p&gt;

</description>
      <category>svelte</category>
      <category>webdev</category>
      <category>snowpack</category>
      <category>bundler</category>
    </item>
    <item>
      <title>Managing Svelte UI state with Robot FSM</title>
      <dc:creator>Ilia Mikhailov</dc:creator>
      <pubDate>Sun, 11 Oct 2020 00:00:00 +0000</pubDate>
      <link>https://dev.to/codechips/managing-svelte-ui-state-with-robot-fsm-4j21</link>
      <guid>https://dev.to/codechips/managing-svelte-ui-state-with-robot-fsm-4j21</guid>
      <description>&lt;p&gt;Ever worked with building complex UIs? If yes, you probably know how challenging it can be to keep track all UI states. It's very easy to end up with a big hairy ball of all the state variables.&lt;/p&gt;

&lt;p&gt;If you are building semi-complex UIs Finite State Machines can help you deal with state. Learn what FSMs are and how you can use them with a simple example.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a Finite State Machine?
&lt;/h2&gt;

&lt;p&gt;Shortly, Finite State Machine or FSM is an old, but recently rediscovered pattern, to help us deal with complex state.&lt;/p&gt;

&lt;p&gt;The gist of FSM is that your application can only be in one state at a time. If you think about it, it actually makes sense. Just like in real life, you can only be in one state at at time. Unless you are a quark.&lt;/p&gt;

&lt;p&gt;I find Finite State Machines a great fit when working with complex UIs. Applications are getting more and more interactive and keeping track of UI state can quickly get challenging.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Robot FSM?
&lt;/h2&gt;

&lt;p&gt;There are currently two popular FSM libraries in JavaScript that I know of. &lt;a href="https://xstate.js.org/docs/"&gt;XState&lt;/a&gt; and &lt;a href="https://thisrobot.life/"&gt;Robot&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I would say that XState is the most popular. It has a lot of features and really great documentation. However, FSM can be intimidating to learn and &lt;em&gt;XState&lt;/em&gt; especially, because there you can achieve the same result by different ways.&lt;/p&gt;

&lt;p&gt;If you are interested in XState, I've written an article where I used XState as a state machine for Firebase Authentication - &lt;a href="https://codechips.me/firebase-authentication-with-xstate-and-svelte/"&gt;Firebase authentication with XState and Svelte&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For this example I decided to use &lt;a href="https://thisrobot.life/"&gt;Robot FSM library&lt;/a&gt;. First, it's much smaller than &lt;em&gt;XState&lt;/em&gt; - 1kb vs 13kb. Second, it's also easier to learn, because it has less bells and whistles.&lt;/p&gt;

&lt;p&gt;Robot doesn't have as good documentation as &lt;em&gt;XState&lt;/em&gt;, so you have to read between the lines and experiment a bit to understand it's concepts and how to work with it.&lt;/p&gt;

&lt;p&gt;But that's why I am here! Hopefully by the end of this article everything will click, you will know what an FSM is and understand where you can use them.&lt;/p&gt;

&lt;p&gt;I promise to keep things simple. Let's code!&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up project
&lt;/h2&gt;

&lt;p&gt;First, we need to create a new Svelte project. We will use &lt;a href="https://github.com/dominikg/svite"&gt;Svite&lt;/a&gt; with &lt;a href="https://pnpm.js.org/"&gt;pnpm&lt;/a&gt; package manager to save some disk space. I find this combination the fastest way to get going.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ npx svite@beta create -pm pnpm svelte-robot-fsm-example
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We will be building a panel menu with different sections. Think tabs component, only prettier.&lt;/p&gt;

&lt;p&gt;Next step is to create a few Svelte components.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating panel components
&lt;/h2&gt;

&lt;p&gt;We will create a simple panel component called &lt;code&gt;Section.svelte&lt;/code&gt;. It will take a &lt;code&gt;title&lt;/code&gt; and a &lt;code&gt;close&lt;/code&gt; function handler as parameters.&lt;/p&gt;

&lt;p&gt;Close handler will be wired up to the &lt;em&gt;Close&lt;/em&gt; button in the component. When clicked, it should hide the menu. We will also add Svelte's slide transition for some neat visual FX.&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="c"&gt;&amp;lt;!-- src/components/Section.svelte --&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;slide&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;svelte/transition&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;let&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;close&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="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
  &lt;span class="nc"&gt;.section&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;border-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="m"&gt;#65b6ca&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nc"&gt;.inner&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;relative&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1em&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="nb"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;max-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100ch&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;min-height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;200px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nt"&gt;h2&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;font-weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;700&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3em&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nc"&gt;.close&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;absolute&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2em&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;"section"&lt;/span&gt; &lt;span class="na"&gt;transition:slide&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;"inner"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;{title}&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;on:click=&lt;/span&gt;&lt;span class="s"&gt;{close}&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"close"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Close&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's setup three sections in our &lt;code&gt;App.svelte&lt;/code&gt;. Replace the file with the code below.&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="c"&gt;&amp;lt;!-- App.svelte --&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Section&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/Section.svelte&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
  &lt;span class="nc"&gt;.wrapper&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;sans-serif&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nc"&gt;.container&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1em&lt;/span&gt; &lt;span class="nb"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;max-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100ch&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nc"&gt;.panel&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#b9f0fe&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nc"&gt;.buttons&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.5em&lt;/span&gt; &lt;span class="m"&gt;1em&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;"wrapper"&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;"heading container"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Svelte with Robot FSM Example&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"panel"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;Section&lt;/span&gt; &lt;span class="na"&gt;title=&lt;/span&gt;&lt;span class="s"&gt;"One"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;Section&lt;/span&gt; &lt;span class="na"&gt;title=&lt;/span&gt;&lt;span class="s"&gt;"Two"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;Section&lt;/span&gt; &lt;span class="na"&gt;title=&lt;/span&gt;&lt;span class="s"&gt;"Three"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"buttons container"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;button&amp;gt;&lt;/span&gt;One&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;button&amp;gt;&lt;/span&gt;Two&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;button&amp;gt;&lt;/span&gt;Three&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you start the app (pnpm run dev), all of the three sections should be visible. That's not something we want. Let's add some interactivity logic next.&lt;/p&gt;

&lt;h2&gt;
  
  
  Doing it the normal way
&lt;/h2&gt;

&lt;p&gt;Adding interactivity to these panel sections is straight forward. For that we need to add three boolean variables, one for each section, and then manipulate their state.&lt;/p&gt;

&lt;p&gt;Here is the updated &lt;code&gt;App.svelte&lt;/code&gt; file with state variables and &lt;code&gt;on:click&lt;/code&gt; handlers.&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="c"&gt;&amp;lt;!-- App.svelte --&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Section&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/Section.svelte&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// open/close state variables. one for each section.&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;showOne&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;showTwo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;showThree&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&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;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
  &lt;span class="nc"&gt;.wrapper&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;sans-serif&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nc"&gt;.container&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1em&lt;/span&gt; &lt;span class="nb"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;max-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100ch&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nc"&gt;.panel&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#b9f0fe&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nc"&gt;.buttons&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.5em&lt;/span&gt; &lt;span class="m"&gt;1em&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;"wrapper"&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;"heading container"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Svelte with Robot FSM Example&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"panel"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    {#if showOne}
      &lt;span class="nt"&gt;&amp;lt;Section&lt;/span&gt; &lt;span class="na"&gt;title=&lt;/span&gt;&lt;span class="s"&gt;"One"&lt;/span&gt; &lt;span class="na"&gt;close=&lt;/span&gt;&lt;span class="s"&gt;{()&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; (showOne = false)} /&amp;gt;
    {/if}

    {#if showTwo}
      &lt;span class="nt"&gt;&amp;lt;Section&lt;/span&gt; &lt;span class="na"&gt;title=&lt;/span&gt;&lt;span class="s"&gt;"Two"&lt;/span&gt; &lt;span class="na"&gt;close=&lt;/span&gt;&lt;span class="s"&gt;{()&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; (showTwo = false)} /&amp;gt;
    {/if}

    {#if showThree}
      &lt;span class="nt"&gt;&amp;lt;Section&lt;/span&gt; &lt;span class="na"&gt;title=&lt;/span&gt;&lt;span class="s"&gt;"Three"&lt;/span&gt; &lt;span class="na"&gt;close=&lt;/span&gt;&lt;span class="s"&gt;{()&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; (showThree = false)} /&amp;gt;
    {/if}
  &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;"buttons container"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;on:click=&lt;/span&gt;&lt;span class="s"&gt;{()&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; (showOne = true)}&amp;gt;One&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;on:click=&lt;/span&gt;&lt;span class="s"&gt;{()&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; (showTwo = true)}&amp;gt;Two&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;on:click=&lt;/span&gt;&lt;span class="s"&gt;{()&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; (showThree = true)}&amp;gt;Three&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can see that we added basic &lt;code&gt;on:click&lt;/code&gt; handlers to buttons and we also pass in &lt;code&gt;close&lt;/code&gt; handlers that set state variable to &lt;code&gt;false&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you've done everything right you should now be able to open and close each panel. Good times!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_jIg0IGP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://res.cloudinary.com/codechips/image/upload/v1602447536/posts/svelte-robot-fsm/svelte-robot-fsm-v1_sakxtg.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_jIg0IGP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://res.cloudinary.com/codechips/image/upload/v1602447536/posts/svelte-robot-fsm/svelte-robot-fsm-v1_sakxtg.gif" alt="State based panel" width="880" height="907"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But what about the scenario that only one section should be visible at one time? Meaning if you open a new one, the currently visible section should close.&lt;/p&gt;

&lt;p&gt;It's doable, but things can get pretty hairy, because you have to track individual state of every section. That's something Finite State Machines can help us with.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let's bring in the Robot
&lt;/h2&gt;

&lt;p&gt;Let's add Robot FSM library to our project.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ pnpm add robot3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we need to create our Finite State Machine. We will name it &lt;code&gt;panelMachine&lt;/code&gt; as it will be responsible for keeping track of our panel's state.&lt;/p&gt;

&lt;p&gt;Add this code to &lt;code&gt;App.svelte&lt;/code&gt; and I will explain it in a second.&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="c"&gt;&amp;lt;!-- App.svelte --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&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;createMachine&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;transition&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;robot3&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;panelMachine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createMachine&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;closed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;showOne&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;one&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="nx"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;showTwo&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;two&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="nx"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;showThree&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;three&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;one&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;close&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;closed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
    &lt;span class="na"&gt;two&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;close&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;closed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
    &lt;span class="na"&gt;three&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;close&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;closed&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="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see we used Robot's &lt;code&gt;createMachine&lt;/code&gt; function to define a state machine. The machine consists of total four states - &lt;em&gt;closed&lt;/em&gt;, &lt;em&gt;one&lt;/em&gt;, &lt;em&gt;two&lt;/em&gt; and &lt;em&gt;three&lt;/em&gt;. Our first state is &lt;em&gt;closed&lt;/em&gt; and that will be the state machine will start with. If you look carefully you can see that our state machine is just a simple dictionary.&lt;/p&gt;

&lt;p&gt;Each dictionary value is then defined by a &lt;code&gt;state&lt;/code&gt; function. The &lt;a href="https://thisrobot.life/api/state.html"&gt;state function&lt;/a&gt; is used to define state in Robot FSM library. But what going on inside the state functions?&lt;/p&gt;

&lt;p&gt;State function is the meat of the Robot library. It's in there that you define your state and transitions to other states. Transitions? States? What?!&lt;/p&gt;

&lt;p&gt;Yes, I understand if you are confused and I promised to keep things simple for that reason. Let me explain.&lt;/p&gt;

&lt;p&gt;As I wrote earlier, an state machine can only be in one state at at time. When our panel FSM is started it starts in the &lt;code&gt;closed&lt;/code&gt; state, because it's first in the dictionary.&lt;/p&gt;

&lt;p&gt;If you imagine that you are in the &lt;code&gt;closed&lt;/code&gt; state you can see that inside it we have defined three &lt;code&gt;transition&lt;/code&gt; functions. Transition &lt;a href="https://thisrobot.life/api/transition.html"&gt;function&lt;/a&gt; dictates the states you can go to from the state FSM that is currently in. They are used to move from one state to another.&lt;/p&gt;

&lt;p&gt;Every transition function takes a &lt;em&gt;trigger&lt;/em&gt; event as first argument and the &lt;em&gt;destination&lt;/em&gt; state as second argument. You can name events however you like, but I like to prefix mine with verbs as I like to think of them not as events, but rather commands or actions. By sending an event to the state machine you command it to go to another state than it's currently in.&lt;/p&gt;

&lt;p&gt;One important concept to understand is that you can only move those states that you have setup transitions for. So, from our &lt;code&gt;closed&lt;/code&gt; state we can only go to states &lt;code&gt;one&lt;/code&gt;, &lt;code&gt;two&lt;/code&gt; or &lt;code&gt;three&lt;/code&gt;. If you send in some other event name than &lt;code&gt;showOne&lt;/code&gt;, &lt;code&gt;showTwo&lt;/code&gt; or &lt;code&gt;showThree&lt;/code&gt; our FSM will simply ignore them and just stay in the current state it is in.&lt;/p&gt;

&lt;p&gt;Similarly, you can only go to &lt;code&gt;closed&lt;/code&gt; state from other states by sending a &lt;code&gt;close&lt;/code&gt; event to our state machine. Are you with me so far?&lt;/p&gt;

&lt;h2&gt;
  
  
  Interpreting our state machine
&lt;/h2&gt;

&lt;p&gt;Alright, we have defined our state machine. Now we have to activate it by using &lt;code&gt;interpret&lt;/code&gt; function from the Robot library.&lt;br&gt;
The &lt;code&gt;interpret&lt;/code&gt; function takes a machine and wraps it into a service that can send events into the machine to change its states. A service does not mutate a machine. Instead it creates derived machines with the current state set.&lt;/p&gt;

&lt;p&gt;You can find the current state of the machine by the &lt;code&gt;machine.current&lt;/code&gt; property. If you wrap it in the service it will be located under &lt;code&gt;service.machine.current&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Here is the code that demonstrates how a machine service works.&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;// Wrap machine into a service. Every time state changes it's printed.&lt;/span&gt;
&lt;span class="c1"&gt;// A service has a `send` command that we can use to send it events.&lt;/span&gt;
&lt;span class="kd"&gt;const&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;interpret&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;panelMachine&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;=&amp;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="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;current state: &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;machine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&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="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;current state: &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;machine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;// current state: closed&lt;/span&gt;

&lt;span class="c1"&gt;// Transition from `closed` state to `one` [legal]&lt;/span&gt;
&lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;showOne&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// current state: one&lt;/span&gt;

&lt;span class="c1"&gt;// Transition from `one` to `two` [illegal]&lt;/span&gt;
&lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;showTwo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// current state: one&lt;/span&gt;

&lt;span class="c1"&gt;// Send non-existent event&lt;/span&gt;
&lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;foo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// current state: one&lt;/span&gt;

&lt;span class="c1"&gt;// Transition from `one` to `closed` [legal]&lt;/span&gt;
&lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;close&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// current state: closed&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see from the example above, if we send in a valid event, that we defined in our state transition in the current state we are in, we can switch to a different state in our state machine.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping our state machine service in a Svelte store
&lt;/h2&gt;

&lt;p&gt;Robot does not have Svelte support per default, but it's not hard to wire it up. For that we will can write a custom Svelte store.&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;createMachine&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;interpret&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;robot3&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;writable&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;svelte/store&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;useMachine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;machine&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;subscribe&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;set&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;writable&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;machine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;machine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;context&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;service&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;interpret&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;machine&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;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;set&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;machine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;context&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="nx"&gt;subscribe&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;send&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 we defined a &lt;code&gt;useMachine&lt;/code&gt; constructor function. It takes a Robot FSM as its argument and returns an array.  First value is our custom Svelte store with our machine's &lt;code&gt;context&lt;/code&gt; and &lt;code&gt;state&lt;/code&gt; as two properties. Second value is the service &lt;code&gt;send&lt;/code&gt; method. It will allow us to send events to our machine.&lt;/p&gt;

&lt;p&gt;Let's use it in our code.&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="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;panelState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;send&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useMachine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;panelMachine&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Using state machine
&lt;/h2&gt;

&lt;p&gt;We have everything we need. Next step we need to do is to replace our &lt;code&gt;on:click&lt;/code&gt; and &lt;code&gt;close&lt;/code&gt; handlers. We also need to use our new &lt;code&gt;panelService&lt;/code&gt; store for state checking instead of individual state variables.&lt;/p&gt;

&lt;p&gt;I won't bore you with all the code changes needed. Instead, here is the updated &lt;code&gt;App.svelte&lt;/code&gt; file in all its glory.&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="c"&gt;&amp;lt;!-- App.svelte --&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Section&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/Section.svelte&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;createMachine&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;interpret&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;robot3&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;writable&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;svelte/store&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// constructor function to interpret the machine and wrap&lt;/span&gt;
  &lt;span class="c1"&gt;// the FSM service into a custom svelte store.&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;useMachine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;machine&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;subscribe&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;set&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;writable&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;machine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;machine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// every time the state changes we will update our Svelte store&lt;/span&gt;
    &lt;span class="kd"&gt;const&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;interpret&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;machine&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="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;set&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;machine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;context&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="nx"&gt;subscribe&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;send&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="c1"&gt;// create our Robot FSM definition&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;panelMachine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createMachine&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;closed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;showOne&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;one&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="nx"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;showTwo&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;two&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="nx"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;showThree&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;three&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;one&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;close&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;closed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
    &lt;span class="na"&gt;two&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;close&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;closed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
    &lt;span class="na"&gt;three&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;close&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;closed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;panelState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;send&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useMachine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;panelMachine&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// close handler&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;close&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="nx"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;close&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// send in event to our FSM&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;toggle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;action&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;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;action&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;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
  &lt;span class="nc"&gt;.wrapper&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;sans-serif&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nc"&gt;.container&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1em&lt;/span&gt; &lt;span class="nb"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;max-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100ch&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nc"&gt;.panel&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#b9f0fe&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nc"&gt;.buttons&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.5em&lt;/span&gt; &lt;span class="m"&gt;1em&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;"wrapper"&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;"heading container"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Svelte with Robot FSM Example&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"panel"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    {#if $panelState.state === 'one'}
      &lt;span class="nt"&gt;&amp;lt;Section&lt;/span&gt; &lt;span class="na"&gt;title=&lt;/span&gt;&lt;span class="s"&gt;"One"&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;&lt;span class="na"&gt;close&lt;/span&gt;&lt;span class="err"&gt;}&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    {/if}

    {#if $panelState.state === 'two'}
      &lt;span class="nt"&gt;&amp;lt;Section&lt;/span&gt; &lt;span class="na"&gt;title=&lt;/span&gt;&lt;span class="s"&gt;"Two"&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;&lt;span class="na"&gt;close&lt;/span&gt;&lt;span class="err"&gt;}&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    {/if}

    {#if $panelState.state === 'three'}
      &lt;span class="nt"&gt;&amp;lt;Section&lt;/span&gt; &lt;span class="na"&gt;title=&lt;/span&gt;&lt;span class="s"&gt;"Three"&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;&lt;span class="na"&gt;close&lt;/span&gt;&lt;span class="err"&gt;}&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    {/if}
  &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;"buttons container"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;on:click=&lt;/span&gt;&lt;span class="s"&gt;{()&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; toggle('showOne')}&amp;gt;One&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;on:click=&lt;/span&gt;&lt;span class="s"&gt;{()&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; toggle('showTwo')}&amp;gt;Two&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;on:click=&lt;/span&gt;&lt;span class="s"&gt;{()&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; toggle('showThree')}&amp;gt;Three&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can see that we have replaced our individual state variables with our state machine. We have also create two handlers - &lt;code&gt;close&lt;/code&gt; and &lt;code&gt;toggle&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you start the app now, you will notice that we can only open one panel at a time. But I must say, it's not a correct behaviour. We cannot open other panel before we close the currently open one.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--T4LZklqg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://res.cloudinary.com/codechips/image/upload/v1602447536/posts/svelte-robot-fsm/svelte-robot-fsm-v2_jxmogo.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--T4LZklqg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://res.cloudinary.com/codechips/image/upload/v1602447536/posts/svelte-robot-fsm/svelte-robot-fsm-v2_jxmogo.gif" alt="Robot FSM based panel" width="880" height="427"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Why? If you look at our machine definition you can see that the only valid transition from a panel is to the &lt;code&gt;closed&lt;/code&gt; state. This means the only valid event we can sent to the open panel is &lt;code&gt;close&lt;/code&gt; event.&lt;/p&gt;

&lt;p&gt;We can actually use this knowledge to our advantage by changing our &lt;code&gt;toggle&lt;/code&gt; handler to the following.&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;toggle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;action&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;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;close&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;action&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;Before sending our show event, we first send the &lt;code&gt;close&lt;/code&gt; event so that our machine enters &lt;code&gt;closed&lt;/code&gt; state.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--QRyBkAaL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://res.cloudinary.com/codechips/image/upload/v1602447536/posts/svelte-robot-fsm/svelte-robot-fsm-v3_vtrmdg.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QRyBkAaL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://res.cloudinary.com/codechips/image/upload/v1602447536/posts/svelte-robot-fsm/svelte-robot-fsm-v3_vtrmdg.gif" alt="Robot FSM based panel with auto-switching" width="880" height="427"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I don't know about you, but I find it pretty elegant.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementing toggle functionality
&lt;/h2&gt;

&lt;p&gt;But what if we want our button not only to open, but also toggle a panel's state. Meaning if the panel is currently open clicking the same button should close it.&lt;/p&gt;

&lt;p&gt;For that we need add a guard that checks if the current state our machine matches the desired state. If it does, it just closes the panel.&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;toggle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// if current state matches desired panel state, close it&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;$panelState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;close&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="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;// else first close, then open desired state&lt;/span&gt;
    &lt;span class="nx"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;close&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We also need to provide the desired state in our buttons' &lt;code&gt;on:click&lt;/code&gt; handlers.&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;"buttons container"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;on:click=&lt;/span&gt;&lt;span class="s"&gt;{()&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; toggle('showOne', 'one')}&amp;gt;One&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;on:click=&lt;/span&gt;&lt;span class="s"&gt;{()&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; toggle('showTwo', 'two')}&amp;gt;Two&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;on:click=&lt;/span&gt;&lt;span class="s"&gt;{()&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; toggle('showThree', 'three')}&amp;gt;Three&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's take a look if the toggle works.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Z7tQXFz---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://res.cloudinary.com/codechips/image/upload/v1602448022/posts/svelte-robot-fsm/svelte-robot-fsm-v4_errgpl.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Z7tQXFz---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://res.cloudinary.com/codechips/image/upload/v1602448022/posts/svelte-robot-fsm/svelte-robot-fsm-v4_errgpl.gif" alt="Robot FSM based panel with toggle" width="880" height="427"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Yep. Works dandy fine! Mission accomplished.&lt;/p&gt;

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

&lt;p&gt;Finite State Machines are hard to understand if you haven't been exposed to them before. They also offer so much more than I showed in my example.&lt;/p&gt;

&lt;p&gt;In Robot there is also &lt;em&gt;context&lt;/em&gt; that lets you work with state, &lt;em&gt;guards&lt;/em&gt; that allows you to enter a state only if condition is met, &lt;em&gt;invoke&lt;/em&gt; that helps us work with promises and sub-machines, and &lt;em&gt;immediate&lt;/em&gt; transitions.&lt;/p&gt;

&lt;p&gt;XState has even more to offer. It's easy to get lost if you don't have a solid understanding of the basics first.&lt;/p&gt;

&lt;p&gt;I chose the simplest example I could think of on purpose that only shows how to setup state and transition from one state to another by sending events.&lt;/p&gt;

&lt;p&gt;If you want to test you skills try building a tabs component. The concepts are exactly the same and you can re-use a lot of the code we have written.&lt;/p&gt;

&lt;p&gt;When you understand the concepts and how to work with state, all other FSM concepts become easy. In the end, it's all about states, sending events and transitions.&lt;/p&gt;

&lt;p&gt;Start simple, experiment and I can guarantee that you will quickly start seeing places in your codebase where a state machine makes sense to use. Plus, as a bonus, you will get one more powerful tool in your toolbox.&lt;/p&gt;

&lt;p&gt;You can find the full code at &lt;a href="https://github.com/codechips/svelte-robot-fsm-example"&gt;https://github.com/codechips/svelte-robot-fsm-example&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thanks for reading! Now go and replace something in your codebase with an FSM FTW.&lt;/p&gt;

</description>
      <category>svelte</category>
      <category>webdev</category>
      <category>fsm</category>
    </item>
    <item>
      <title>Learn Fast, Earn Fast</title>
      <dc:creator>Ilia Mikhailov</dc:creator>
      <pubDate>Thu, 01 Oct 2020 00:00:00 +0000</pubDate>
      <link>https://dev.to/codechips/learn-fast-earn-fast-4dko</link>
      <guid>https://dev.to/codechips/learn-fast-earn-fast-4dko</guid>
      <description>&lt;p&gt;Life is all about continuous learning. You are always learning something new whether you like it or not. If you don't, you quickly become outdated. You don't learn, you don't earn.&lt;/p&gt;

&lt;p&gt;The world around us is spinning faster and faster. It's not only stressful enough just to keep up to date with the information, but also constantly learn something new.&lt;/p&gt;

&lt;p&gt;The Internet is a blessing, but also a curse. There is so much information out there! It's hard to find and process it all. My children don't see me reading many books at home nowadays, but only if they knew how much text consume on a normal day! Blogs, Slack, Twitter, emails, manuals, forums, work documents. These things add up!&lt;/p&gt;

&lt;p&gt;Everyone today is often competing on the same terms, but you can gain a competitive advantage over others by being a fast learner. This brings me to the core question of this essay: &lt;em&gt;What's the fastest way to learn&lt;/em&gt;?&lt;/p&gt;

&lt;h2&gt;
  
  
  Know your learning style
&lt;/h2&gt;

&lt;p&gt;Everyone is different, therefore there is no ultimate learning process. You have to find what works best just for you. Are you a visual, auditory, reading/writing or kinetic learner? You are often not only one type, but a mix of different combinations.&lt;/p&gt;

&lt;p&gt;Of course, you can learn by using any style - doing, watching, reading, listening, but in order to maximize your learning you have to know which one of those suits you best.&lt;/p&gt;

&lt;p&gt;I personally is definitely a reading/writing type, but I like to watch videos too in order to get a better understanding of the topic I am learning.&lt;/p&gt;

&lt;p&gt;Podcasts are not really my thing. I can't listen to podcasts because I need to concentrate while listening. I tried to do it in bed before sleeping, but that only made me fall asleep faster.&lt;/p&gt;

&lt;p&gt;How about watching videos? Coding videos are also not high on my list. I hate to scrub and pause in the video player, plus I want to be able to copy and try out the code while I am watching.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt; : I know there are platforms that show video and code side-by-side, but they are rare.&lt;/p&gt;

&lt;p&gt;Reading, on the other hand, is different. It's fast. You can easily skim to the juicy parts, plus copying and pasting text is much faster than typing it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Style vs Process
&lt;/h2&gt;

&lt;p&gt;We've talked about the learning styles, but what about the process? Which process should you follow in order to learn fast?&lt;/p&gt;

&lt;p&gt;There are a few different approaches. Some people like to dive heads down and &lt;a href="https://topfunky.com/2019/do-first-understand-later/"&gt;figure things on the way&lt;/a&gt;. Some like to understand what they are dealing with first.&lt;/p&gt;

&lt;p&gt;I am definitely the second type. I like to understand before I start.&lt;/p&gt;

&lt;p&gt;Not sure how and when I finally understood this, but I've now used it for many years with great success.&lt;/p&gt;

&lt;h2&gt;
  
  
  My learning process
&lt;/h2&gt;

&lt;p&gt;Below are the steps I normally follow in order to learn a topic as fast and as efficiently as possible. It's not rocket science, but works great for me.&lt;/p&gt;

&lt;p&gt;Maybe it will work for you too?&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Know what you need to learn
&lt;/h3&gt;

&lt;p&gt;Before learning something it's important to know what you want, or need, to learn. What's the minimum you need to know in order to reach your goal? What questions need answers?&lt;/p&gt;

&lt;p&gt;Often you already might have some idea or some kind of goal in you mind. Make &lt;strong&gt;sure&lt;/strong&gt; you know it beforehand. It will make judging sources of information easier.&lt;/p&gt;

&lt;p&gt;If you are not sure, go for a walk and think about it. Somehow things tend to clear up in motion.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Find quality information
&lt;/h3&gt;

&lt;p&gt;The first thing I do is to find as much useful slash interesting information on the topic as possible. It can be blog posts, Youtube videos, official documentation, code examples, Github repositories, Github issues, Stack Overflow threads. Anything goes here. As long as you think it can help you reach your goal.&lt;/p&gt;

&lt;p&gt;The important part in this step is to find as much information you &lt;em&gt;think&lt;/em&gt; might be useful as possible. Don't judge it at this stage. Just skim through it quickly, if something catches your interest, keep it and continue with your quest. Go with your gut feeling here. If something seems interesting or useful, save it, otherwise scrap it.&lt;/p&gt;

&lt;p&gt;I also often search for the bad or negative parts. Parts that people struggle with or complain about. Review posts are super useful here, even if they are only someones opinions and not necessary the truth. I try to get a sense if it worth it and if there are any existing problems or pitfalls.&lt;/p&gt;

&lt;p&gt;Following links I find in the articles is also something I do often. If some link catches my attention I open it in a new tab.&lt;/p&gt;

&lt;p&gt;When I feel I have enough potential sources of information I stop. At the end of this session I usually have 20-30 browser tabs open, 10-15 bookmarks and 5-10 Youtube videos added to my "Watch Later" list.&lt;/p&gt;

&lt;p&gt;This step usually takes me 30-60 minutes.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Overwhelm yourself
&lt;/h3&gt;

&lt;p&gt;During this phase I start to process the found information one by one. I don't go deep here. My goal is to find interesting bits of information and extract the core concepts. I usually read only a paragraph or two, maybe look some diagrams or glance briefly at some code examples.&lt;/p&gt;

&lt;p&gt;If I had to describe the level my concentration between 1 being glancing over text and 10 being fully emerged in some text, I would say it's around 3. I am fully concentrated, but not on the whole text. My goal at this stage is to only understand the concepts and not the details. I want to get a rough idea of what this is all about.&lt;/p&gt;

&lt;p&gt;When it comes to videos I usually play them at 1.5X speed and often forward to the parts I find interesting.&lt;/p&gt;

&lt;p&gt;I branch out to other related things I find on the way that I don't yet understand.&lt;/p&gt;

&lt;p&gt;I do this to the point where I am feeling completely lost and nothing makes sense any more.&lt;/p&gt;

&lt;p&gt;This phase usually takes me between 1-5 hours depending on the size of the subject in question.&lt;/p&gt;

&lt;p&gt;When my brain is completely fried with information I step away from the research phase and go and do something else. Something that doesn't require brain power.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Sleep on it
&lt;/h3&gt;

&lt;p&gt;This is a &lt;strong&gt;very important&lt;/strong&gt; step, don't skip it! It's called &lt;em&gt;Background Processing&lt;/em&gt;. You have overwhelmed your brain. Bombed it with information. Now you have to give your unconscious mind a chance to process and sort it all. Go to bed early and try to get 7-8 hours of good sleep.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Review yesterday
&lt;/h3&gt;

&lt;p&gt;Now it's time to take some notes. To try to recap what you learned and make your own understanding of the subject. I usually grab a pen and paper and try to write down core concepts using lists and keywords. Sometimes I draw a sloppy mind map. Remember, this is only for you. Nobody will read this!&lt;/p&gt;

&lt;p&gt;Here, I am using pen and paper. It allows me to draw doodles and other stuff and it's fast too. Plus, physically writing it down on paper has a totally different effect than typing it out on a computer. It feels more free form and your brain processes this differently. At least that's what I've noticed.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Distill everything
&lt;/h3&gt;

&lt;p&gt;You mind has already background-processed all the information. You helped it structure it further by your review. Things and concepts suddenly start to make sense. Look at you notes and rewrite them once again, but this time only picking out the core parts and concepts.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Synthesize important parts
&lt;/h3&gt;

&lt;p&gt;Now that you have an idea of the concepts and all the dots are connected, think through what is important. What concepts, terms, patterns have you learned? Try to identify them.&lt;/p&gt;

&lt;h3&gt;
  
  
  7. Play around
&lt;/h3&gt;

&lt;p&gt;It's time to have some fun. Try to do something very simple. Baby steps. There is no pressure at this stage. You are in &lt;em&gt;exploration&lt;/em&gt; mode.&lt;/p&gt;

&lt;p&gt;Once you get something going, try to expand it. Do some more, play around. View it as &lt;a href="https://en.wikipedia.org/wiki/Proof_of_concept"&gt;POC&lt;/a&gt;. You are only trying to do something here. There are no "musts."&lt;/p&gt;

&lt;p&gt;While playing around and getting stuck you will notice that you somehow you already know where to look for the solution to your problem. Either from the "overwhelm" session or you will somehow know how and where to search for the solution.&lt;/p&gt;

&lt;p&gt;Again, thanks to the power of your mind and its background-processing ability. It helped you distill and internalize the core concepts and connect all the dots.&lt;/p&gt;

&lt;h3&gt;
  
  
  8. Do the work
&lt;/h3&gt;

&lt;p&gt;It's now time to do the real work. Here I often use the outcome of the "play" phase as the base and somehow I already know where to look for answers I have.&lt;/p&gt;

&lt;h2&gt;
  
  
  Proof
&lt;/h2&gt;

&lt;p&gt;All these steps can take differently long time. It depends on the size of the topic. For example, I had to learn a lot of new things in the past few weeks. One of them was &lt;a href="https://nextjs.org/"&gt;Next.js&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;By following my learning process I went from knowing nothing about Next.js to building a fully functional app with authentication and other bells and whistles running in production in just 3 days.&lt;/p&gt;

&lt;p&gt;If I can do it so can you!&lt;/p&gt;

&lt;h2&gt;
  
  
  Useful links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://www.youtube.com/watch?v=f84n5oFoZBc"&gt;Hammock Driven Development&lt;/a&gt;&lt;/strong&gt; - one of my favorite talks on Youtube by Rich Hickey, creator of Clojure language. He talks about the background-processing and this is where I got the idea from.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://www.youtube.com/watch?v=DUlFxffjDFo"&gt;Hacking Your Head: Managing Information Overload&lt;/a&gt;&lt;/strong&gt; - a short talk by Jo Pearce on how to deal with Cognitive Load. It touches the same subjects I mention in the article, but with scientific proof.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Takeaways
&lt;/h2&gt;

&lt;p&gt;Developing an effective learning process is a &lt;strong&gt;must&lt;/strong&gt; if you want to stay ahead of the competition. But it's not only about learning fast, but also about learning smart.&lt;/p&gt;

&lt;p&gt;Experiment with different approaches. Evaluate. There is no universal approach to learning. Find out what works best just for you.&lt;/p&gt;

&lt;p&gt;The more I learn, the less I know, the less stressed I get. Because in the end it's not about knowing, but about understanding. Even if you don't know somehow you get a feeling that you have seen it before. You brain is good at making connections. By constantly learning you are building brain capital.&lt;/p&gt;

&lt;p&gt;By having developed my own learning process I always know that I can learn fast and effectively and that keeps me calm.&lt;/p&gt;

&lt;p&gt;Stay curious and always be learning!&lt;/p&gt;

</description>
      <category>career</category>
      <category>productivity</category>
      <category>learning</category>
      <category>effectiveness</category>
    </item>
    <item>
      <title>Tailwind UI dropdown menu - React vs Svelte</title>
      <dc:creator>Ilia Mikhailov</dc:creator>
      <pubDate>Mon, 21 Sep 2020 00:00:00 +0000</pubDate>
      <link>https://dev.to/codechips/tailwind-ui-dropdown-menu-react-vs-svelte-3970</link>
      <guid>https://dev.to/codechips/tailwind-ui-dropdown-menu-react-vs-svelte-3970</guid>
      <description>&lt;p&gt;The other day I was prototyping a new internal app at work in React using Next.js. To get it off the ground quickly I used Tailwind CSS. In my app I needed to create a simple dropdown menu and I looked at the Tailwind UI example on how they did it.&lt;/p&gt;

&lt;p&gt;Actually, creating a dropdown menu is not as simple as it sounds. First, you have to handle mouse clicks outside of it and close the menu if it's currently open. Second, you should support pressing &lt;code&gt;Escape&lt;/code&gt; key and close the menu if it's currently open. Third, you should add nice animation to the menu so it feels more alive.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--H_sf1SGj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://res.cloudinary.com/codechips/image/upload/v1600635707/dropdown-menu_evkubl.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--H_sf1SGj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://res.cloudinary.com/codechips/image/upload/v1600635707/dropdown-menu_evkubl.gif" alt="Tailwind UI dropdown meny example" width="880" height="511"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Implementing the menu in React wasn't quite as straight forward as I hoped for. The Tailwind styling itself is not a problem, but it took me some time to figure out how to handle "click away" or "click outside" functionality and handling the escape key. On top of that I had to research how to do CSS transitions in React. Turns out that creators of Tailwind created a helpful &lt;a href="https://github.com/tailwindlabs/tailwindui-react"&gt;transition library&lt;/a&gt; as React does not have the functionality built-in.&lt;/p&gt;

&lt;p&gt;Doing a &lt;a href="https://www.google.com/search?q=react+click+away+listener"&gt;Google search&lt;/a&gt; for "react click away listener" didn't really help. A search on NPM for "react click outside" and "react click away" returned way too many more results than I needed. Sure, there are plenty of React libraries, but I felt that there should be a much simpler way of handling that.&lt;/p&gt;

&lt;p&gt;Here is the Next.js (React + TypeScript) code I ended up with.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Link&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/link&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useEffect&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Transition&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;@tailwindui/react&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;Menu&lt;/span&gt; &lt;span class="o"&gt;=&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="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;show&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setShow&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;container&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useRef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handleOutsideClick&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;MouseEvent&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;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;contains&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;target&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;show&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;setShow&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="p"&gt;}&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;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="nx"&gt;handleOutsideClick&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;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;removeEventListener&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="nx"&gt;handleOutsideClick&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;show&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

  &lt;span class="nx"&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handleEscape&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;KeyboardEvent&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;show&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;if&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;key&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Escape&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;setShow&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="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;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;keyup&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;handleEscape&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;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;removeEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;keyup&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;handleEscape&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;show&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"relative"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;
        &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"menu focus:outline-none focus:shadow-solid "&lt;/span&gt;
        &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;setShow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;show&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;img&lt;/span&gt;
          &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"w-10 h-10 rounded-full"&lt;/span&gt;
          &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;picture&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="na"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Transition&lt;/span&gt;
        &lt;span class="na"&gt;show&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;show&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;enter&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"transition ease-out duration-100 transform"&lt;/span&gt;
        &lt;span class="na"&gt;enterFrom&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"opacity-0 scale-95"&lt;/span&gt;
        &lt;span class="na"&gt;enterTo&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"opacity-100 scale-100"&lt;/span&gt;
        &lt;span class="na"&gt;leave&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"transition ease-in duration-75 transform"&lt;/span&gt;
        &lt;span class="na"&gt;leaveFrom&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"opacity-100 scale-100"&lt;/span&gt;
        &lt;span class="na"&gt;leaveTo&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"opacity-0 scale-95"&lt;/span&gt;
      &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"origin-top-right absolute right-0 w-48 py-2 mt-1 bg-gray-800 rounded shadow-md"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Link&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"/profile"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"block px-4 py-2 hover:bg-green-500 hover:text-green-100"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
              Profile
            &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Link&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Link&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"/api/logout"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"block px-4 py-2 hover:bg-green-500 hover:text-green-100"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
              Logout
            &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Link&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Transition&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;Menu&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When I was done with React implementation, I thought to myself of how I would implement the same menu in Svelte. So I took some time to port it to Svelte.&lt;/p&gt;

&lt;p&gt;One of the many niceties about Svelte is that it has CSS transitions and animations built in. Here is my take on it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;onMount&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;svelte&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;scale&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;svelte/transition&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;let&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;show&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// menu state&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;menu&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// menu wrapper DOM reference&lt;/span&gt;

  &lt;span class="nx"&gt;onMount&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;handleOutsideClick&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="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;show&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;menu&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;contains&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;target&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;show&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&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;const&lt;/span&gt; &lt;span class="nx"&gt;handleEscape&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="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;show&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&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;key&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Escape&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;show&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="c1"&gt;// add events when element is added to the DOM&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;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="nx"&gt;handleOutsideClick&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="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&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;keyup&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;handleEscape&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="c1"&gt;// remove events when element is removed from the DOM&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;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;removeEventListener&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="nx"&gt;handleOutsideClick&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="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;removeEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;keyup&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;handleEscape&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="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"relative"&lt;/span&gt; &lt;span class="na"&gt;bind:this=&lt;/span&gt;&lt;span class="s"&gt;{menu}&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt;
      &lt;span class="na"&gt;on:click=&lt;/span&gt;&lt;span class="s"&gt;{()&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; (show = !show)}
      class="menu focus:outline-none focus:shadow-solid"
    &amp;gt;
      &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"w-10 h-10 rounded-full"&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;{user.picture}&lt;/span&gt; &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;{user.name}&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;

    {#if show}
      &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt;
        &lt;span class="na"&gt;in:scale=&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt; &lt;span class="na"&gt;duration:&lt;/span&gt; &lt;span class="err"&gt;100,&lt;/span&gt; &lt;span class="na"&gt;start:&lt;/span&gt; &lt;span class="err"&gt;0.95&lt;/span&gt; &lt;span class="err"&gt;}}&lt;/span&gt;
        &lt;span class="na"&gt;out:scale=&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt; &lt;span class="na"&gt;duration:&lt;/span&gt; &lt;span class="err"&gt;75,&lt;/span&gt; &lt;span class="na"&gt;start:&lt;/span&gt; &lt;span class="err"&gt;0.95&lt;/span&gt; &lt;span class="err"&gt;}}&lt;/span&gt;
        &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"origin-top-right absolute right-0 w-48 py-2 mt-1 bg-gray-800
          rounded shadow-md"&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt;
          &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/profile"&lt;/span&gt;
          &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"block px-4 py-2 hover:bg-green-500 hover:text-green-100"&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Profile&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt;
          &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/api/logout"&lt;/span&gt;
          &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"block px-4 py-2 hover:bg-green-500 hover:text-green-100"&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Logout&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    {/if}
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sure, the amount of code is little less in Svelte than in React, but what about the cognitive load? Which one is easier to read and understand? You be the judge.&lt;/p&gt;

</description>
      <category>svelte</category>
      <category>react</category>
      <category>ui</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Svelte, PostCSS and TypeScript with Svite bundler</title>
      <dc:creator>Ilia Mikhailov</dc:creator>
      <pubDate>Sun, 06 Sep 2020 00:00:00 +0000</pubDate>
      <link>https://dev.to/codechips/svelte-postcss-and-typescript-with-svite-bundler-ael</link>
      <guid>https://dev.to/codechips/svelte-postcss-and-typescript-with-svite-bundler-ael</guid>
      <description>&lt;p&gt;Svite has been on my radar for quite some time now and I finally found some time to play with it. I was surprised by the results.&lt;/p&gt;

&lt;p&gt;I am on the quest to find the easiest, fastest and most flexible bundler for Svelte development. I think I just did.&lt;/p&gt;

&lt;h2&gt;
  
  
  My requirements
&lt;/h2&gt;

&lt;p&gt;Testing different Svelte bundler setups is a weird hobby of mine. I get a kick out of connecting different plugins together, while trying to setup a smooth development workflow.&lt;/p&gt;

&lt;p&gt;The requirements are pretty straight forward.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It must be fast&lt;/li&gt;
&lt;li&gt;It must support Typescript&lt;/li&gt;
&lt;li&gt;It must support PostCSS&lt;/li&gt;
&lt;li&gt;It must produce small and efficient bundles&lt;/li&gt;
&lt;li&gt;It must produce correct sourcemaps for debugging&lt;/li&gt;
&lt;li&gt;It should support HMR (Hot Module Replacement)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  My testing application
&lt;/h2&gt;

&lt;p&gt;For my tests I created a simple Svelte app. Its functionality is simple. You press a button and it fetches a random Kanye West tweet from &lt;a href="https://kanye.rest/"&gt;Kanye as a Service&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Wav44z-V--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/codechips/image/upload/v1598252607/kanye-says-app_rup4n6.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Wav44z-V--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/codechips/image/upload/v1598252607/kanye-says-app_rup4n6.webp" alt="Kanye Says app screenshot" width="880" height="592"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;While simple, the application has some interesting parts.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Svelte components are written in Typescript&lt;/strong&gt;. I want to see if transpiling and type checking works correctly for TS.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;External Svelte library&lt;/strong&gt;. Not all bundlers support libraries written in Svelte efficiently.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;External library dependency&lt;/strong&gt;. I want to see if a bundler supports tree shaking when bundling for production.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;External Assets&lt;/strong&gt;. It should be possible to import SVG, PNG, JSON and other external assets in our code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PostCSS with TailwindCSS&lt;/strong&gt;. A good bundler should make it easy to work with SASS and PostCSS.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Business components in Typescript&lt;/strong&gt;. Typescript is here to stay. A good bundler should support it out-of-the-box.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Can &lt;a href="https://github.com/dominikg/svite"&gt;Svite&lt;/a&gt; handle the pressure? Let's see!&lt;/p&gt;

&lt;h2&gt;
  
  
  Short notes on Svite
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/dominikg/svite"&gt;Svite&lt;/a&gt; is a pure Svelte bundler built by &lt;a href="https://github.com/dominikg"&gt;dominikg&lt;/a&gt; on top of &lt;a href="https://github.com/vitejs/"&gt;Vite&lt;/a&gt;. I've already tested Vite with very &lt;a href="https://dev.to/svelte-with-vitejs-typescript-tailwind/"&gt;impressive results&lt;/a&gt;, and since Svite is almost Vite, I expect the results to be equal or better.&lt;/p&gt;

&lt;p&gt;What's great about Svite is that it has many different templates you can use when creating your app. Currently there are four:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;minimal&lt;/li&gt;
&lt;li&gt;routify-mdsvex&lt;/li&gt;
&lt;li&gt;postcss-tailwind&lt;/li&gt;
&lt;li&gt;svelte-preprocess-auto&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It has also a few other useful options you can use too.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating a new project
&lt;/h2&gt;

&lt;p&gt;Let's create an app by using postcss-tailwind template and adding TypeScript support.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ npx svite create -pm pnpm -ts -t postcss-tailwind -sc svelte-svite-typescript
$ cd svelte-svite-typescript
$ npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That was easy! Let's look at the files it produced.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ls -1
index.html
node_modules
package.json
pnpm-lock.yaml
postcss.config.js
public
src
svelte.config.js
tailwind.config.js
tsconfig.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And in the &lt;code&gt;src&lt;/code&gt; directory you have these files.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ls src
App.svelte  index.css  index.ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nice! Almost the same files I had to write by hand when testing Vite.&lt;/p&gt;

&lt;p&gt;Why almost? If you worked with Vite, you might notice that there is no &lt;code&gt;vite.config.js&lt;/code&gt;. Because Svite has build in support for Svelte, there is no need to have one.&lt;/p&gt;

&lt;h3&gt;
  
  
  App.svelte
&lt;/h3&gt;

&lt;p&gt;The file is small and with helpful comments in the right places.&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;lang=&lt;/span&gt;&lt;span class="s"&gt;"ts"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;world&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;postcss&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// edit world and save to see hmr update&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;h1&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;orangered&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c"&gt;/* change color an save to see hmr update */&lt;/span&gt;
    &lt;span class="c"&gt;/* you can also use css nesting
    &amp;amp; .world {
      font-size: 2rem;
    }
    */&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nc"&gt;.world&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="err"&gt;@apply&lt;/span&gt; &lt;span class="err"&gt;text-teal-500&lt;/span&gt; &lt;span class="err"&gt;italic;&lt;/span&gt; &lt;span class="c"&gt;/*  here's some tailwind apply */&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;h1&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"border border-current rounded p-4 m-4"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- tailwind classes in svelte template --&amp;gt;&lt;/span&gt;
  Hello
  &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"world"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;{world}&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  index.ts
&lt;/h3&gt;

&lt;p&gt;The main file is clean and imports our base Tailwind 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="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;App&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.svelte&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./index.css&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;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  package.json
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;package.json&lt;/code&gt; is clean and small.&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;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"svelte-svite-typescript"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0.0.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&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;"validate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"svelte-check &amp;amp;&amp;amp; tsc --noEmit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"dev"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"svite -ts"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"build"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"svite build -ts"&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;"dependencies"&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;"svelte"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"3.24.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"svelte-hmr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0.10.2"&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;"devDependencies"&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;"@tsconfig/svelte"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^1.0.10"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"postcss"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^7.0.32"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"postcss-import"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^12.0.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"postcss-load-config"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^2.1.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"postcss-preset-env"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^6.7.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"svelte-check"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^1.0.21"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"svelte-preprocess"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"4.1.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"svite"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^0.6.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"tailwindcss"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^1.7.3"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"tslib"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^2.0.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"typescript"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^3.9.7"&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;A few dependencies are outdated, but we can update them with &lt;code&gt;ncu -u &amp;amp;&amp;amp; pnpm install&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;When starting everything works as advertised. Impressive!&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing with Svite with my benchmark app
&lt;/h2&gt;

&lt;p&gt;I copied the files in &lt;code&gt;src&lt;/code&gt; directory from my Vite test setup and the app worked out of the box! I am not surprised since Svite is Vite abstracted.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building
&lt;/h2&gt;

&lt;p&gt;Let's compare Svite builds to Vite's.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Svite

[write] dist/_assets/index.ab6f11f2.js 32.04kb, brotli: 9.50kb
[write] dist/_assets/style.45ed9e6c.css 8.10kb, brotli: 1.81kb
[write] dist/_assets/usa.fcca7834.svg 0.88kb
[write] dist/index.html 0.40kb, brotli: 0.16kb
Build completed in 8.37s.

# Vite

[write] dist/_assets/index.0729edbe.js 32.04kb, brotli: 9.50kb
[write] dist/_assets/style.393fa0c4.css 6.39kb, brotli: 1.67kb
[write] dist/_assets/usa.6aae1c82.svg 0.88kb
[write] dist/index.html 0.41kb, brotli: 0.16kb
Build completed in 5.16s.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The results are almost identical in terms of output and speed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Results
&lt;/h2&gt;

&lt;p&gt;Let's re-visit our list of requirements.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;It must be fast&lt;/strong&gt;. Check. Vite's cold starts and reloads feel superfast.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It must support Typescript&lt;/strong&gt;. Check. Works out-of-the-box.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It must support PostCSS&lt;/strong&gt;. Check. Works out-of-the-box.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It must produce small and efficient bundles&lt;/strong&gt;. Check. Rollup is used for bundling.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It should be possible to import external Svelte libs&lt;/strong&gt;. Check.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It must produce correct sourcemaps for debugging&lt;/strong&gt;. So-so. Not very impressed, but ok.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It should support HMR (Hot Module Replacement)&lt;/strong&gt;. Check. Works great.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One annoying thing I found is that when you kill the dev server the whole app starts reloading itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Plugins and Libraries Mentioned
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/dominikg/svite"&gt;https://github.com/dominikg/svite&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/vitejs/vite"&gt;https://github.com/vitejs/vite&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Svite is a nice little abstraction of Vitejs specifically tailored for Svelte development. It's fast, small and doesn't get in your way. Everything just works.&lt;/p&gt;

&lt;p&gt;It feels thought though. For example, one thing I like is that Svite allows you to choose which package manager you want to use. It might be a small thing, but it's really nice that the author has thought of this.&lt;/p&gt;

&lt;p&gt;If you feel you need to use some other Vite plugins that Svite doesn't offer, you can always create &lt;code&gt;vite.config.js&lt;/code&gt; and wire them up there.&lt;/p&gt;

&lt;p&gt;I didn't bother to setting up other useful scripts this time, like continuous linting, but if you are interested check out &lt;code&gt;package.json&lt;/code&gt; in my Vite setup in the Github repo.&lt;/p&gt;

&lt;p&gt;Svite might just be the easiest way to get your new Svelte project off the ground. The documentation is excellent and you have many different boilerplate templates to choose from. Just make sure to read the docs for all the possible options!&lt;/p&gt;

&lt;p&gt;You can find the code here &lt;a href="https://github.com/codechips/svelte-typescript-setups"&gt;https://github.com/codechips/svelte-typescript-setups&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I will definitely use Svite in the future!&lt;/p&gt;

</description>
      <category>svelte</category>
      <category>webdev</category>
      <category>typescript</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Solid Sapper setup with PostCSS and Tailwind</title>
      <dc:creator>Ilia Mikhailov</dc:creator>
      <pubDate>Sun, 06 Sep 2020 00:00:00 +0000</pubDate>
      <link>https://dev.to/codechips/solid-sapper-setup-with-postcss-and-tailwind-1mbo</link>
      <guid>https://dev.to/codechips/solid-sapper-setup-with-postcss-and-tailwind-1mbo</guid>
      <description>&lt;p&gt;While it's straight forward to integrate PostCSS and Tailwind into plain Svelte projects, Sapper is a totally different beast. There are many moving parts. Rollup configuration is super complex, code is generated. It can be hard to grasp what's going on.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;I needed to integrate &lt;a href="https://postcss.org/"&gt;PostCSS&lt;/a&gt; together with &lt;a href="https://sapper.svelte.dev/"&gt;Sapper&lt;/a&gt;. Meanwhile it's not so hard to integrate plain &lt;a href="https://tailwindcss.com/"&gt;Tailwind CSS&lt;/a&gt; into Sapper, turns out integrating PostCSS together with TailwindCSS requires a little more work. After trying a few different approaches I finally landed on something that works for me.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why PostCSS?
&lt;/h2&gt;

&lt;p&gt;Plain CSS can take you far, but I often prefer to use Tailwind CSS. I find it really nice to work with declarative CSS instead of writing everything from scratch. I like Tailwind as it is, but I often use a few other PostCSS plugins too that help me work with Tailwind CSS more efficiently. Maybe a better word would be "augment" and not "help."&lt;/p&gt;

&lt;h2&gt;
  
  
  How Sapper manages CSS
&lt;/h2&gt;

&lt;p&gt;Sapper has an internal router built-in. Which is helpful. The router intercepts all link clicks and fetches each page individually when you visit it. When you click on the link that leads to another page in your app, Sapper will fetch the page in the background and replace the content in your Sapper app.&lt;/p&gt;

&lt;p&gt;It will actually put the content into the &lt;code&gt;slot&lt;/code&gt; in the &lt;code&gt;src/routes/_layout.svelte&lt;/code&gt; page. That's how it's setup in the official boilerplate at least.&lt;/p&gt;

&lt;p&gt;Sapper injects the styles for different components and pages when you navigate between the pages. When you visit a page, Sapper will fetch that page and also inject the style for that page and for the components it uses into the head tag of the document.&lt;/p&gt;

&lt;p&gt;Sapper and Svelte scope CSS classes defined in the components to the components themselves, reducing the risk of overriding the CSS.&lt;/p&gt;

&lt;p&gt;To understand more read the blog post &lt;a href="https://svelte.dev/blog/the-zen-of-just-writing-css"&gt;The zen of Just Writing CSS&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It's actually a really nice feature that you get out of the box in Svelte! You can see that by inspecting elements in the dev tools console. Each styled element will have a &lt;code&gt;svelte-[hash]&lt;/code&gt; class defined on it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution
&lt;/h2&gt;

&lt;p&gt;After wrestling with &lt;a href="https://github.com/egoist/rollup-plugin-postcss"&gt;rollup-plugin-postcss&lt;/a&gt; for some time, I gave up and went with the simplest setup possible.&lt;/p&gt;

&lt;p&gt;Instead of trying to integrate PostCSS into Rollup itself, I moved PostCSS processing outside of Rollup's pipeline. It's fast too, because the processing is done outside Rollup.&lt;/p&gt;

&lt;p&gt;Here is how I did it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create a Sapper project
&lt;/h3&gt;

&lt;p&gt;In order to fully understand what's needed, we will start from scratch by creating a &lt;a href="https://github.com/sveltejs/sapper-template"&gt;standard Sapper project&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;$ npx degit sveltejs/sapper-template#rollup sapper-with-postcss
$ cd sapper-with-postcss &amp;amp;&amp;amp; npm i
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can now start the app by running &lt;code&gt;npm run dev&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up Tailwind
&lt;/h2&gt;

&lt;p&gt;Let's add Tailwind and Tailwind's &lt;a href="https://github.com/tailwindlabs/tailwindcss-typography"&gt;typography&lt;/a&gt; plugin that we will use to style the blog posts.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ npm add -D tailwindcss @tailwindcss/typography
$ npx tailwindcss init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We now need to replace Tailwind's configuration file with 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="c1"&gt;// tailwind.config.js&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="na"&gt;future&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;removeDeprecatedGapUtilities&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="na"&gt;experimental&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;uniformColorPalette&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;extendedFontSizeScale&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="c1"&gt;// currently Sapper dev server chokes on this&lt;/span&gt;
    &lt;span class="c1"&gt;// applyComplexClasses: true,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;purge&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// needs to be set if we want to purge all unused&lt;/span&gt;
    &lt;span class="c1"&gt;// @tailwind/typography styles&lt;/span&gt;
    &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;all&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;content&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="s1"&gt;./src/**/*.svelte&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;./src/**/*.html&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;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;container&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;center&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="na"&gt;extend&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;variants&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
  &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&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;@tailwindcss/typography&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;Next thing we need to do is to create Tailwind's base file. We will put it in &lt;code&gt;src/assets&lt;/code&gt; folder, that you need to create first, and we will name it &lt;code&gt;global.pcss&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We are using &lt;code&gt;.pcss&lt;/code&gt; extension just to distinguish that it's a PostCSS file. It's not something you have to do. Plain &lt;code&gt;.css&lt;/code&gt; extension works just a good. I like to distinguish PostCSS files from plain CSS.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="cm"&gt;/* global.pcss */&lt;/span&gt;

&lt;span class="k"&gt;@tailwind&lt;/span&gt; &lt;span class="nt"&gt;base&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;@apply&lt;/span&gt; &lt;span class="nt"&gt;bg-indigo-100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;@tailwind&lt;/span&gt; &lt;span class="nt"&gt;components&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;@tailwind&lt;/span&gt; &lt;span class="nt"&gt;utilities&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Alright. Now that we are done with Tailwind configuration, let's wire it up into our PostCSS pipeline.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting up PostCSS with Tailwind
&lt;/h3&gt;

&lt;p&gt;First things first. We need to install PostCSS cli and a few PostCSS plugins that we will use.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ npm add -D postcss-cli
$ npm add -D autoprefixer postcss-import cssnano postcss-load-config postcss-preset-env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we need to create PostCSS configuration file in the project's root folder.&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;// postcss.config.js&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tailwind&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&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;tailwindcss&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;autoprefixer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&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;autoprefixer&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;cssnano&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&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;cssnano&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;postcssImport&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&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;postcss-import&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;presetEnv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&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;postcss-preset-env&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)({&lt;/span&gt;
  &lt;span class="na"&gt;features&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// enable nesting&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;nesting-rules&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="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;plugins&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NODE_ENV&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;production&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;postcssImport&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;tailwind&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;presetEnv&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;cssnano&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;postcssImport&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;tailwind&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;presetEnv&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;plugins&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cool! We are almost there. Theoretically, we have everything we need. We just need to wire everything up.&lt;/p&gt;

&lt;h2&gt;
  
  
  PostCSS in Svelte files
&lt;/h2&gt;

&lt;p&gt;Actually, I forgot something. We want to style our Svelte components with Tailwind and PostCSS too. In order for that to work we need to use the good ol' &lt;code&gt;svelte-preprocess&lt;/code&gt; plugin.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ npm add -D svelte-preprocess
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's cheat a bit. We will create a &lt;code&gt;svelte.config.js&lt;/code&gt; and setup the preprocessor there. Svelte config is needed for the editors to be able to work correctly. Syntax highlighting, intellisense and all those things.&lt;/p&gt;

&lt;p&gt;We will later re-use the exported preprocessor in our Rollup config to keep things DRY.&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;// svelte.config.js&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;autoProcess&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;svelte-preprocess&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;preprocess&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;autoProcess&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;postcss&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;There are a few different ways to setup Svelte prepocessor, but I found this the most minimal. The reason it works is that we installed &lt;a href="https://github.com/michael-ciniawsky/postcss-load-config"&gt;postcss-load-config&lt;/a&gt; plugin earlier. It will automatically load &lt;code&gt;postcss.config.js&lt;/code&gt; file if it exists. No need to require it in our code!&lt;/p&gt;

&lt;p&gt;Now that we have finished setting up the preprocessor, we need to import it in our Rollup config.&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;// rollup.config.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;preprocess&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;./svelte.config&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// add preprocess to Svelte plugin in client section&lt;/span&gt;
&lt;span class="nx"&gt;svelte&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nx"&gt;dev&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;hydratable&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;emitCss&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="nx"&gt;preprocess&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// &amp;lt;-- add this&lt;/span&gt;
&lt;span class="p"&gt;}),&lt;/span&gt;

&lt;span class="c1"&gt;// add preprocess to Svelte plugin in server section&lt;/span&gt;
&lt;span class="nx"&gt;svelte&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ssr&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;hydratable&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="nx"&gt;dev&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;preprocess&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// &amp;lt;-- add this&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Phew! Everything is now configured correctly. Hopefully.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adjust your NPM scripts
&lt;/h3&gt;

&lt;p&gt;Last thing we need to do is to wire everything together. We will do it by changing the &lt;code&gt;scripts&lt;/code&gt; section in our &lt;code&gt;package.json&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="nl"&gt;"scripts"&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;"dev"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"run-p watch:*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"watch:css"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"postcss src/assets/global.pcss -o static/global.css -w"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"watch:dev"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sapper dev"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"build"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"run-s build:css build:sapper"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"build:css"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"NODE_ENV=production postcss src/assets/global.pcss -o static/global.css"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"build:sapper"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sapper build --legacy"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"build:export"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sapper export --legacy"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"export"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"run-s build:css build:export"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"node __sapper__/build"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"serve"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"serve ___sapper__/export"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"cy:run"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cypress run"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"cy:open"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cypress open"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"run-p --race dev cy:run"&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;This requires some explanation. You can see that we have a &lt;code&gt;watch:css&lt;/code&gt; script. What it does is replaces Sappers &lt;code&gt;static/global.css&lt;/code&gt; with our Tailwind base file. We also need to explicitly set &lt;code&gt;NODE_ENV&lt;/code&gt; to "production" in &lt;code&gt;build:css&lt;/code&gt; since we are doing our PostCSS processing outside Sapper. It's needed by Tailwind in order to purge unused CSS styles from its base file.&lt;/p&gt;

&lt;p&gt;Be careful not to set &lt;code&gt;NODE_ENV&lt;/code&gt; to production in the Sapper build and export scripts. If you do, and you set any &lt;code&gt;:global&lt;/code&gt; styles in your components, they will be purged leading to missing styles.&lt;/p&gt;

&lt;p&gt;Oh, just another tip. If you what to use a background image in you CSS put it in the &lt;code&gt;static&lt;/code&gt; folder. You can then use it your CSS like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="nc"&gt;.hero&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nt"&gt;background-image&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;image&lt;/span&gt;&lt;span class="nc"&gt;.png&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Test-driving the new setup
&lt;/h2&gt;

&lt;p&gt;To check that Tailwind and PostCSS works in Svelte components, replace your &lt;code&gt;src/routes/index.svelte&lt;/code&gt; with this code.&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="c"&gt;&amp;lt;!-- index.svelte --&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;style &lt;/span&gt;&lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"postcss"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nc"&gt;.btn&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="err"&gt;@apply&lt;/span&gt; &lt;span class="err"&gt;bg-red-500&lt;/span&gt; &lt;span class="err"&gt;text-red-100&lt;/span&gt; &lt;span class="err"&gt;uppercase&lt;/span&gt; &lt;span class="err"&gt;tracking-wide&lt;/span&gt; &lt;span class="err"&gt;font-semibold&lt;/span&gt;
      &lt;span class="err"&gt;text-4xl&lt;/span&gt; &lt;span class="err"&gt;px-4&lt;/span&gt; &lt;span class="err"&gt;py-3&lt;/span&gt; &lt;span class="err"&gt;shadow-lg&lt;/span&gt; &lt;span class="err"&gt;rounded;&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;svelte:head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Sapper project template&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/svelte:head&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;"space-y-10 text-center"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;h1&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-7xl uppercase font-bold"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Great success!&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"btn"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;DO NOT PRESS THIS BUTTON!&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can see that we set &lt;code&gt;lang="postcss"&lt;/code&gt; in the style tag. That's not something that is required, PostCSS will still be processed. It's only so that editor understands it's dealing with PostCSS.&lt;/p&gt;

&lt;p&gt;To see Tailwind's typography plugin in action change &lt;code&gt;src/routes/blog/[slug].svelte&lt;/code&gt; to the code below.&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;context=&lt;/span&gt;&lt;span class="s"&gt;"module"&lt;/span&gt;&lt;span class="nt"&gt;&amp;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="nx"&gt;preload&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;query&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;res&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;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`blog/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.json`&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;data&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;res&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="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;200&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;post&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="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&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;message&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="nt"&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="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;post&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;span class="nt"&gt;&amp;lt;svelte:head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;{post.title}&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/svelte:head&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;&amp;lt;!-- prose is a class from Tailwind typography plugin --&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;'prose prose-lg'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;{post.title}&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;

  {@html post.html}
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And ... we are finally done!&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;Below you can see the setup in action running on Vercel. Make sure to check out individual blog posts to see Tailwind's typography plugin in action.&lt;/p&gt;

&lt;p&gt;Oh, and please don't press that button. Don't say I didn't warn you!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--My_vY7Kz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/codechips/image/upload/v1599381878/posts/sapper-postcss-tailwind_dcsvss.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--My_vY7Kz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/codechips/image/upload/v1599381878/posts/sapper-postcss-tailwind_dcsvss.webp" alt="sapper with postcss and tailwind" width="880" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Live demo: &lt;a href="https://sapper-with-postcss-and-tailwind.vercel.app"&gt;https://sapper-with-postcss-and-tailwind.vercel.app&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Mentioned and used plugins
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/cssnano/cssnano"&gt;https://github.com/cssnano/cssnano&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/csstools/postcss-preset-env"&gt;https://github.com/csstools/postcss-preset-env&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/egoist/rollup-plugin-postcss"&gt;https://github.com/egoist/rollup-plugin-postcss&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/pirxpilot/postcss-cli"&gt;https://github.com/pirxpilot/postcss-cli&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/postcss/autoprefixer"&gt;https://github.com/postcss/autoprefixer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/postcss/postcss-import"&gt;https://github.com/postcss/postcss-import&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/sveltejs/sapper-template"&gt;https://github.com/sveltejs/sapper-template&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/sveltejs/svelte-preprocess"&gt;https://github.com/sveltejs/svelte-preprocess&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/tailwindlabs/tailwindcss"&gt;https://github.com/tailwindlabs/tailwindcss&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/tailwindlabs/tailwindcss-typography"&gt;https://github.com/tailwindlabs/tailwindcss-typography&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Implementing PostCSS in Sapper becomes clear when you understand how Sapper deals with CSS files.&lt;/p&gt;

&lt;p&gt;We set up two separate PostCSS pipelines in our example app. First is processing Sapper's global CSS file. Second is replacing Sapper's component styling with PostCSS. We didn't actually change the way Sapper handles and serves CSS files, we only replaced it with PostCSS. Maybe "augmented" is a better word.&lt;/p&gt;

&lt;p&gt;You can find the full code here &lt;a href="https://github.com/codechips/sapper-with-postcss-and-tailwind"&gt;github.com/codechips/sapper-with-postcss-and-tailwind&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now go and create some beautifully styled apps!&lt;/p&gt;

</description>
      <category>svelte</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>tailwindcss</category>
    </item>
  </channel>
</rss>
