<?xml version="1.0" encoding="UTF-8"?>
    <rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom">
      <channel>
        <atom:link href="https://aralroca.com/rss.xml" rel="self" type="application/rss+xml" />
        <title>Aral Roca</title>
        <description>Aral Roca's personal web site. Open source does tend to be more stable software. It's the right way to do things.</description>
        <link>https://aralroca.com</link>
        <lastBuildDate>Thu Apr 30 2026 23:05:06 GMT+0000 (Coordinated Universal Time)</lastBuildDate>
      
            <item>
              <title>AI agents shouldn't control your apps; they should be the app</title>
              <description>From maintaining open source libraries to building an AI-powered tools OS with Rust and WebAssembly. How Kitmul went from a testing ground to 300+ browser-based tools.</description>
              <link>https://aralroca.com/blog/ai-agents-should-be-the-app</link>
              <guid isPermaLink="false">https://aralroca.com/blog/ai-agents-should-be-the-app/</guid>
              <pubDate>Tue Mar 31 2026 00:00:00 GMT+0000 (Coordinated Universal Time)</pubDate>
              <content:encoded><![CDATA[<p><a href="https://kitmul.com/en">Kitmul</a> started as something far more modest than what it is today. I maintain two open source libraries: <a href="https://github.com/aralroca/next-translate">NextTranslate</a> and <a href="https://github.com/teafuljs/teaful">Teaful</a>. I needed a real Next.js project where I could iterate on them. Not artificial demos or example repositories; a live product where bugs surface naturally and limitations become obvious.</p>
<p>That was the only goal.</p>
<h2 id="the-ai-multiplier-effect">The AI multiplier effect</h2>
<p>To speed up development, I started using AI coding agents: Claude Code, Gemini, and Codex. Not just for productivity. I wanted to understand firsthand how these agents behave in a real development workflow. What they&#39;re good at, where they break, and how they change the way you think about building software.</p>
<p>What I didn&#39;t expect was the effect on scope. When you can implement an idea in minutes instead of hours, you stop triaging ideas. You just build them. I went from &quot;let me maintain these two libraries&quot; to &quot;let me build 300+ tools&quot; in just 3 weeks.</p>
<p>Currently I&#39;m on Claude Code 20x. The combination of an agent that understands your codebase deeply and can execute multi-step tasks autonomously has been the biggest development velocity multiplier I&#39;ve experienced.</p>
<h2 id="the-architecture-everything-on-the-users-device">The architecture: everything on the user&#39;s device</h2>
<p>Kitmul&#39;s fundamental technical decision is that everything runs on the client. No exceptions whenever possible.</p>
<p>The stack is straightforward: if native JavaScript is enough, use JavaScript. If the operation is intensive (audio processing, heavy image manipulation, track separation) compile to WebAssembly. For performance-critical parts, Rust compiled to WASM.</p>
<p>For example, here&#39;s how we merge PDFs entirely in the browser:</p>
<pre><code class="hljs language-ts"><span class="hljs-keyword">import</span> { <span class="hljs-title class_">PDFDocument</span> } <span class="hljs-keyword">from</span> <span class="hljs-string">&quot;pdf-lib&quot;</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">mergePDFs</span>(<span class="hljs-params">files</span>) {
  <span class="hljs-keyword">const</span> merged = <span class="hljs-keyword">await</span> <span class="hljs-title class_">PDFDocument</span>.<span class="hljs-title function_">create</span>();

  <span class="hljs-keyword">for</span> (<span class="hljs-keyword">const</span> file <span class="hljs-keyword">of</span> files) {
    <span class="hljs-keyword">const</span> bytes = <span class="hljs-keyword">await</span> file.<span class="hljs-title function_">arrayBuffer</span>();
    <span class="hljs-keyword">const</span> pdf = <span class="hljs-keyword">await</span> <span class="hljs-title class_">PDFDocument</span>.<span class="hljs-title function_">load</span>(bytes);
    <span class="hljs-keyword">const</span> pages = <span class="hljs-keyword">await</span> merged.<span class="hljs-title function_">copyPages</span>(pdf, pdf.<span class="hljs-title function_">getPageIndices</span>());
    pages.<span class="hljs-title function_">forEach</span>(<span class="hljs-function">(<span class="hljs-params">page</span>) =&gt;</span> merged.<span class="hljs-title function_">addPage</span>(page));
  }

  <span class="hljs-keyword">return</span> merged.<span class="hljs-title function_">save</span>(); <span class="hljs-comment">// Returns Uint8Array, never leaves the browser</span>
}
</code></pre><p>Zero network calls. The file goes from <code>File API → pdf-lib → download</code>.</p>
<h2 id="why-rust--wasm-the-prime-number-checker">Why Rust + WASM: the prime number checker</h2>
<p>For heavier computation, JavaScript hits a wall. A real example from Kitmul: our <a href="https://kitmul.com/en/math/prime-number-checker">Prime Number Checker</a>. JavaScript can check primality for small numbers, but try testing a number with more than 1,200 digits and the browser tab freezes.</p>
<pre><code class="hljs language-ts"><span class="hljs-keyword">function</span> <span class="hljs-title function_">isPrime</span>(<span class="hljs-params">n</span>) {
  <span class="hljs-keyword">if</span> (n &lt;= <span class="hljs-number">1</span>) <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
  <span class="hljs-keyword">if</span> (n &lt;= <span class="hljs-number">3</span>) <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
  <span class="hljs-keyword">if</span> (n % <span class="hljs-number">2</span> === <span class="hljs-number">0</span> || n % <span class="hljs-number">3</span> === <span class="hljs-number">0</span>) <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
  <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">5</span>; i * i &lt;= n; i += <span class="hljs-number">6</span>) {
    <span class="hljs-keyword">if</span> (n % i === <span class="hljs-number">0</span> || n % (i + <span class="hljs-number">2</span>) === <span class="hljs-number">0</span>) <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
  }
  <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
}

<span class="hljs-comment">// Works for small numbers, but for a 1200+ digit number?</span>
<span class="hljs-comment">// BigInt arithmetic becomes so slow the tab freezes.</span>
</code></pre><p>The solution: we compiled a Rust crate that uses <code>num-bigint</code> with a Miller-Rabin primality test to WASM. The Rust side receives the number as a string (because it can be thousands of digits long) and returns whether it&#39;s prime:</p>
<pre><code class="hljs language-rust"><span class="hljs-keyword">use</span> num_bigint::BigUint;
<span class="hljs-keyword">use</span> num_traits::{One, Zero};

<span class="hljs-meta">#[no_mangle]</span>
<span class="hljs-keyword">pub</span> <span class="hljs-keyword">extern</span> <span class="hljs-string">&quot;C&quot;</span> <span class="hljs-keyword">fn</span> <span class="hljs-title function_">is_number_prime</span>(ptr: *<span class="hljs-keyword">const</span> <span class="hljs-type">u8</span>, len: <span class="hljs-type">usize</span>) <span class="hljs-punctuation">-&gt;</span> <span class="hljs-type">i32</span> {
    <span class="hljs-keyword">let</span> <span class="hljs-variable">bytes</span> = <span class="hljs-keyword">unsafe</span> { std::slice::<span class="hljs-title function_ invoke__">from_raw_parts</span>(ptr, len) };
    <span class="hljs-keyword">let</span> <span class="hljs-variable">num_str</span> = std::<span class="hljs-type">str</span>::<span class="hljs-title function_ invoke__">from_utf8</span>(bytes).<span class="hljs-title function_ invoke__">unwrap_or</span>(<span class="hljs-string">&quot;0&quot;</span>);
    <span class="hljs-keyword">let</span> <span class="hljs-variable">n</span> = num_str.parse::&lt;BigUint&gt;().<span class="hljs-title function_ invoke__">unwrap_or_else</span>(|_| BigUint::<span class="hljs-title function_ invoke__">zero</span>());

    <span class="hljs-keyword">if</span> n &lt;= BigUint::<span class="hljs-title function_ invoke__">one</span>() { <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>; }

    <span class="hljs-keyword">if</span> <span class="hljs-title function_ invoke__">miller_rabin</span>(&amp;n) { <span class="hljs-number">1</span> } <span class="hljs-keyword">else</span> { <span class="hljs-number">0</span> }
}
</code></pre><h2 id="the-thesis-ai-agents-shouldnt-control-your-apps-they-should-be-the-app">The thesis: AI agents shouldn&#39;t control your apps. They should BE the app.</h2>
<p>This is where I think the current industry is getting it wrong.</p>
<p>OpenAI with Operator, Anthropic with Computer Use, Google with Project Mariner: the big players are all building AI agents that <strong>control existing applications</strong>. They take screenshots of your screen, move your mouse, click buttons, fill forms. Essentially they&#39;re building really sophisticated RPA bots.</p>
<p>I think this approach is fundamentally flawed. Here&#39;s why:</p>
<p><strong>1. You&#39;re building on top of interfaces designed for humans, not machines.</strong> When an AI agent navigates a website, it&#39;s fighting against dropdowns, modals, cookie banners, CAPTCHAs, and layout changes. Every website redesign can break the agent. This is fragile by design.</p>
<p><strong>2. You still depend on third-party services.</strong> The AI agent might be smart, but it&#39;s still uploading your PDF to iLovePDF, still sending your images to Canva&#39;s servers, still giving your data to someone else. The privacy problem doesn&#39;t go away just because a robot is clicking the buttons.</p>
<p><strong>3. It&#39;s slow.</strong> Screenshot → analyze → click → wait for page load → screenshot again. This loop takes seconds per action. Meanwhile, a direct function call takes milliseconds.</p>
<p>The alternative: what I&#39;m building with Kitmul is radically different.</p>
<p><strong>The AI agent doesn&#39;t control apps. The AI agent IS the app.</strong></p>
<p>Instead of navigating to some website to remove an image background, the agent calls a local function that runs a WASM-compiled AI model directly in the browser. No screenshots. No navigation. No external servers. Just a function call that returns a result.</p>
<h2 id="from-dev-tools-to-tools-for-everyone">From dev tools to tools for everyone</h2>
<p>As an open source developer, I&#39;ve always built for other developers. Libraries, CLI tools, build utilities. Limited audience, limited impact.</p>
<p>With Kitmul I flipped the question. Instead of &quot;what tool does a dev need,&quot; I asked: &quot;what tool do people search for on Google and end up on a website that charges them or uploads their files to a server.&quot;</p>
<p>The answer: hundreds of tools. Remove image backgrounds, separate audio tracks, convert formats, compress PDFs, generate QR codes. Tools people use daily, and for which many sites charge €10-20/month.</p>
<p>Today Kitmul has over 300. And they&#39;re not trivial wrappers.</p>
<h2 id="the-part-that-gets-really-interesting-self-building-tools">The part that gets really interesting: self-building tools</h2>
<p>Here&#39;s where Kitmul diverges from everything else.</p>
<p>With 300+ tools, we cover a lot of use cases. But when a user asks for something we don&#39;t have, instead of saying &quot;sorry, we can&#39;t do that,&quot; the system should be able to <strong>create the tool on the fly with AI</strong>; and then, critically, have a <strong>human-in-the-loop</strong> verify that the generated tool actually works correctly before it becomes part of the permanent catalog.</p>
<p>Think about it:</p>
<ol>
<li>User asks: &quot;I need a tool that converts MIDI files to sheet music.&quot;</li>
<li>The AI generates the tool: a client-side implementation using WebAssembly.</li>
<li>A human reviewer (me, or eventually a community of contributors) verifies the tool works, handles edge cases, and meets Kitmul&#39;s quality standards.</li>
<li>Once approved, the tool is permanently added to the catalog.</li>
<li>The next user who asks for the same thing gets the verified, production-quality tool instantly.</li>
</ol>
<p><strong>The system literally builds itself based on what users need.</strong> Every unanswered request becomes a signal. Every verified tool makes the platform more capable. It&#39;s a flywheel where AI generates, humans verify, and the catalog grows organically.</p>
<p>This is fundamentally different from the &quot;generate code at runtime&quot; approach that some AI companies are pursuing. Generated code running without verification is a liability: it might have bugs, security holes, or simply not work for edge cases. The human-in-the-loop step is not a limitation; it&#39;s a <strong>feature</strong>. It ensures every tool in the catalog is production-quality.</p>
<h2 id="see-it-in-action">See it in action</h2>
<p>Here&#39;s a quick demo of how Kitmul works:</p>


<h2 id="whats-next">What&#39;s next</h2>
<p>The orchestration works for simple flows, but complex workflows with branching, conditionals, and feedback loops still need work. The self-building pipeline is being designed right now.</p>
<p>The long-term vision: a system where any task you do today with fragmented software (uploading files here, paying a subscription there, installing an app for something else) can be resolved within a single interface, executed locally, orchestrated by AI, and constantly expanding based on what users actually need.</p>
<p>The question I want to leave you with: <strong>Do we really need AI agents that puppet our existing apps? Or do we need to rethink what the app itself should be?</strong></p>
<p>I think the answer is the latter. And I think the browser is the right runtime to prove it.</p>
]]></content:encoded>
            </item>
            <item>
              <title>App with React API without tools as Webpack or Babel</title>
              <description>Learn how to make an application without tools as Webpack or Babel while using the same API of React.</description>
              <link>https://aralroca.com/blog/app-with-react-api-without-tools-as-webpack-or-babel</link>
              <guid isPermaLink="false">https://aralroca.com/blog/app-with-react-api-without-tools-as-webpack-or-babel/</guid>
              <pubDate>Thu May 07 2020 00:00:00 GMT+0000 (Coordinated Universal Time)</pubDate>
              <content:encoded><![CDATA[<p>There are tools like Webpack or Babel that seem indispensable when we work as frontends. But, could we make an application without such tools? Even without package.json or bundles? And being able to continue using the React API? Let&#39;s see how.</p>
<h2 id="getting-the-hello-world">Getting the hello world</h2>
<p>We are going to start our App with just two files: <code>index.html</code> and <code>App.js</code>:</p>
<pre><code>.
├── index.html
└── App.js
</code></pre><p>We are going to load our <code>App.js</code> file inside the <code>index.js</code> adding the <code>type=&quot;module&quot;</code>:</p>
<p><small>index.html:</small></p>
<pre><code class="hljs language-html"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">&quot;en&quot;</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">&quot;viewport&quot;</span> <span class="hljs-attr">content</span>=<span class="hljs-string">&quot;width=device-width, initial-scale=1&quot;</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">&quot;utf-8&quot;</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">type</span>=<span class="hljs-string">&quot;module&quot;</span> <span class="hljs-attr">src</span>=<span class="hljs-string">&quot;App.js&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>My App without Webpack<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">body</span> <span class="hljs-attr">id</span>=<span class="hljs-string">&quot;app&quot;</span> /&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre><p>Then, in our <code>App.js</code> file, we are going to use <a href="https://preactjs.com/">Preact</a> loaded directly using <a href="unpkg.com">unpkg.com</a>. Unpkg is a fast, global content delivery network for everything on npm. The reasons to choose Preact instead of React are:</p>
<ul>
<li>Instead of JSX (That requires Babel) we can use a similar syntax.</li>
<li>Is just 3kb and it has the same React API.</li>
<li>It has better performance than React.</li>
</ul>
<p><small>App.js:</small></p>
<pre><code class="hljs language-jsx"><span class="hljs-keyword">import</span> { html, render } <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;https://unpkg.com/htm/preact/standalone.module.js&#x27;</span>

<span class="hljs-keyword">function</span> <span class="hljs-title function_">App</span>(<span class="hljs-params"></span>) {
  <span class="hljs-keyword">return</span> html`<span class="language-xml">
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
      Hello world
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
  `</span>
}

<span class="hljs-title function_">render</span>(html`<span class="language-xml">&lt;</span><span class="hljs-subst">${App}</span><span class="language-xml"> /&gt;`</span>, <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">getElementById</span>(<span class="hljs-string">&#x27;app&#x27;</span>))
</code></pre><p>Now we can start the project in local with:</p>
<pre><code class="hljs language-bh">npx serve .
</code></pre><p>And open <a href="http://localhost:5000">http://localhost:5000</a>.</p>
<p>We did only 2 steps and already have our Preact App working! Without Webpack, babel, package.json...</p>
<h2 id="importing-other-components">Importing other components</h2>
<p>To import a new component into our project, once we&#39;ve created the file:</p>
<pre><code class="hljs language-diff">.
├── index.html
<span class="hljs-addition">+├── Header.js</span>
└── App.js
</code></pre><p>We can use a normal <code>import</code> but be careful, it should finish with the extension <code>.js</code>, because this is JavaScript, not Webpack.</p>
<p><small>In our App.js</small></p>
<pre><code class="hljs language-js"><span class="hljs-keyword">import</span> { html, render } <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;https://unpkg.com/htm/preact/standalone.module.js&#x27;</span>

<span class="hljs-comment">// New import:</span>
<span class="hljs-keyword">import</span> { <span class="hljs-title class_">Header</span> } <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;./Header.js&#x27;</span>

<span class="hljs-keyword">function</span> <span class="hljs-title function_">App</span>(<span class="hljs-params"></span>) {
  <span class="hljs-comment">// Fragments doesn&#x27;t exist anymore :)</span>
  <span class="hljs-keyword">return</span> html`<span class="language-xml">
    &lt;</span><span class="hljs-subst">${Header}</span><span class="language-xml"> title=&quot;This is my app&quot;&gt;
      An example without Webpack and Babel
    &lt;/</span><span class="hljs-subst">${Header}</span><span class="language-xml">&gt;

    <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
      Content of the page
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
  `</span>
}

<span class="hljs-title function_">render</span>(html`<span class="language-xml">&lt;</span><span class="hljs-subst">${App}</span><span class="language-xml"> /&gt;`</span>, <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">getElementById</span>(<span class="hljs-string">&#x27;app&#x27;</span>))
</code></pre><p><small>In our Header.js</small></p>
<pre><code class="hljs language-jsx"><span class="hljs-keyword">import</span> { html } <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;https://unpkg.com/htm/preact/standalone.module.js&#x27;</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">Header</span>(<span class="hljs-params">{ title, children }</span>) {
  <span class="hljs-keyword">return</span> html`<span class="language-xml">
    <span class="hljs-tag">&lt;<span class="hljs-name">header</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span></span><span class="hljs-subst">${title}</span><span class="language-xml"><span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
      </span><span class="hljs-subst">${children}</span><span class="language-xml">
    <span class="hljs-tag">&lt;/<span class="hljs-name">header</span>&gt;</span>
  `</span>
}
</code></pre><h2 id="using-hooks">Using hooks</h2>
<p>Sure. We can use hooks in Preact.</p>
<pre><code class="hljs language-jsx"><span class="hljs-comment">// The same React hooks are available on the same package</span>
<span class="hljs-keyword">import</span> {
  html,
  useEffect,
} <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;https://unpkg.com/htm/preact/standalone.module.js&#x27;</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">Header</span>(<span class="hljs-params">{ title, children }</span>) {
  <span class="hljs-title function_">useEffect</span>(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-variable language_">document</span>.<span class="hljs-property">title</span> = title
  }, [title])

  <span class="hljs-keyword">return</span> html`<span class="language-xml">
    <span class="hljs-tag">&lt;<span class="hljs-name">header</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span></span><span class="hljs-subst">${title}</span><span class="language-xml"><span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
      </span><span class="hljs-subst">${children}</span><span class="language-xml">
    <span class="hljs-tag">&lt;/<span class="hljs-name">header</span>&gt;</span>
  `</span>
}
</code></pre><h2 id="codesandbox">Codesandbox</h2>
<iframe
  src="https://codesandbox.io/embed/app-without-webpack-ee1l0?fontsize=14&hidenavigation=1&theme=dark"
  style="width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;"
  title="app-without-webpack"
  allow="accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr"
  sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"
></iframe>

<h2 id="support">Support</h2>
<p>Support of JavaScript modules is available in all modern browsers:</p>
<ul>
<li><a href="https://caniuse.com/#search=modules">https://caniuse.com/#search=modules</a></li>
</ul>
<p>If you want to use a fallback for legacy browser, you can use the <code>nomodule</code> attribute:</p>
<pre><code class="hljs language-html"><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">type</span>=<span class="hljs-string">&quot;module&quot;</span> <span class="hljs-attr">src</span>=<span class="hljs-string">&quot;modern-browsers.js&quot;</span> /&gt;</span><span class="language-handlebars"><span class="language-xml">
<span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">nomodule</span> <span class="hljs-attr">src</span>=<span class="hljs-string">&quot;legacy-browsers.js&quot;</span> /&gt;</span></span></span>
</code></pre><h2 id="using-more-packages">Using more packages</h2>
<p>On <a href="https://www.pika.dev/">https://www.pika.dev/</a> you can search all the npm packages that have support to modules, and their <a href="https://www.unpkg.com">https://www.unpkg.com</a> link to import to your project.</p>
]]></content:encoded>
            </item>
            <item>
              <title>Resting heart rate 68 to 56: what changed in my dev work</title>
              <description>A 6-month n=1 experiment applying athletic training principles to software development. Intermittent hypoxia, structured rest, and a few other physiological interventions that produced measurable differences.</description>
              <link>https://aralroca.com/blog/biohacks-that-actually-work</link>
              <guid isPermaLink="false">https://aralroca.com/blog/biohacks-that-actually-work/</guid>
              <pubDate>Sat Apr 04 2026 00:00:00 GMT+0000 (Coordinated Universal Time)</pubDate>
              <content:encoded><![CDATA[<p>I normally write about web frameworks, WebAssembly, and JavaScript internals. And I normally don&#39;t publish on a Saturday. But I&#39;ve been wanting to write this for a while and today I woke up inspired, so here it is.</p>
<p>This isn&#39;t a technical article. It&#39;s personal. But I genuinely believe it can help a lot of people, especially developers and founders who spend long hours in front of a screen and feel like their brain gives up before their schedule does.</p>
<p>I want to talk about the things that changed my daily performance more than any tool, framework, or productivity app ever did. Not hypothetical stuff I read on a blog. Things I&#39;ve been doing for months (some for years) that produced measurable, repeatable differences in how I work.</p>
<p>Here are the numbers first:</p>
<ul>
<li>Resting heart rate: 68 → 56 bpm</li>
<li>Breathing rate: 14-16 → 8-10 breaths/min</li>
<li>Deep work blocks: ~90 min max → 3-4 hours consistently</li>
<li>Post-meeting recovery: 20-30 min → basically instant</li>
</ul>
<p>None of this came from a supplement, an app, or a course. It came from applying things I learned through competitive athletics to how I work as a developer.</p>
<ul>
<li><a href="#some-context-about-me">Some context about me</a></li>
<li><a href="#the-parkour-connection">The parkour connection</a></li>
<li><a href="#hack-1-intermittent-hypoxia">Hack #1: Intermittent hypoxia</a><ul>
<li><a href="#what-it-actually-is">What it actually is</a></li>
<li><a href="#what-changed-for-me">What changed for me</a></li>
<li><a href="#the-science-briefly">The science (briefly)</a></li>
</ul>
</li>
<li><a href="#hack-2-the-pomodoro-technique-done-right">Hack #2: The Pomodoro technique, done right</a><ul>
<li><a href="#why-most-people-do-it-wrong">Why most people do it wrong</a></li>
<li><a href="#the-parkour-parallel">The parkour parallel</a></li>
<li><a href="#how-i-actually-use-it">How I actually use it</a></li>
</ul>
</li>
<li><a href="#hack-3-walking-meetings">Hack #3: Walking meetings</a></li>
<li><a href="#hack-4-strategic-caffeine">Hack #4: Strategic caffeine</a></li>
<li><a href="#hack-5-cold-exposure">Hack #5: Cold exposure</a></li>
<li><a href="#hack-6-power-naps">Hack #6: Power naps</a></li>
<li><a href="#what-didnt-work">What didn&#39;t work</a></li>
<li><a href="#the-thing-nobody-talks-about-silicon-valley-already-does-this">The thing nobody talks about: Silicon Valley already does this</a></li>
<li><a href="#the-physiological-argument-for-founders">The physiological argument for founders</a></li>
<li><a href="#how-to-start">How to start</a></li>
<li><a href="#references">References</a></li>
</ul>
<h2 id="some-context-about-me">Some context about me</h2>
<p>I&#39;ve been in software development for over ten years. Most of that time I&#39;ve been deep in open source. I created <a href="https://github.com/aralroca/next-translate">NextTranslate</a>, <a href="https://github.com/teafuljs/teaful">Teaful</a>, and more recently <a href="https://brisa.build">Brisa</a>, a web framework built on web components. I also recently built <a href="https://kitmul.com/en">Kitmul</a>, which went from a testing ground for my libraries to 300+ browser-based tools in three weeks (I wrote about that <a href="https://aralroca.com/blog/ai-agents-should-be-the-app">here</a>).</p>
<p>That&#39;s the professional side. But there&#39;s another side that&#39;s been part of my life for just as long: parkour.</p>
<h2 id="the-parkour-connection">The parkour connection</h2>
<p>I started doing parkour in the mid-2000s. By 2011 I was competing in Red Bull events. Not casually. This was serious training, serious risk, serious discipline. I trained alongside people who are now elite athletes preparing for the Olympics. Parkour will officially be part of the Games, and some of the people I used to train with are on that path.</p>
<figure align="center">
  <img class="center" src="https://aralroca.com/images/blog-images/biohack-aral-redbull.webp" alt="Aral competing at a Red Bull parkour event in Santorini" />
  <figcaption><small>Red Bull Art of Motion, Santorini</small></figcaption>
</figure>

<p>In 2021 I had the worst accident of my life. I misjudged a jump from a rooftop to a fence, fell badly, and ended up in a coma for three days. When I woke up, my vestibular system was damaged. The apparatus that controls balance. For weeks I couldn&#39;t walk straight without the world spinning.</p>
<p>Most people would have quit parkour after that. I did the opposite. I used parkour as rehabilitation. I went back to basics. Simple movements, low risk, building balance from scratch. The mindset was the same one I apply to debugging: the jump wasn&#39;t the problem. My execution was. I analyzed what I did wrong, and I trained specifically to fix it.</p>
<p>It took years of patient work. But this year, I went back and completed that exact same jump. Rooftop to fence. Clean.</p>


<p>I&#39;m telling this story because it&#39;s directly relevant to everything that follows. The accident taught me something I already knew intellectually but had never felt in my bones: your body is the foundation of everything you do. When it breaks, nothing else matters. Not your code, not your startup, not your deadlines. And when you rebuild it deliberately, with discipline, everything else gets better too.</p>
<p>These days I do parkour as a hobby to stay in shape. But the years of competitive training, the accident, the recovery, gave me something that no programming book or productivity course ever could: an intuitive understanding of how the body and brain actually perform under pressure. And how fragile that performance is if you don&#39;t take care of the hardware.</p>
<figure align="center">
  <img class="center" src="https://aralroca.com/images/blog-images/biohack-aral-climbing.webp" alt="Aral climbing natural rock formations" />
  <figcaption><small>Training outdoors</small></figcaption>
</figure>

<p>Athletes know things about performance that knowledge workers generally ignore. How rest periods affect output quality. How breathing patterns change your nervous system state. How adrenaline management is a trainable skill, not a personality trait. How the difference between a good day and a bad day often comes down to physiology, not motivation. And how you recover from failure matters more than the failure itself.</p>
<p>Once I started applying these principles to my work as a developer, everything changed.</p>
<p>And exercise itself, just moving your body regularly, is the most underrated performance hack that exists. It increases BDNF (the protein that helps your brain form new connections), regulates cortisol, improves sleep, and directly enhances executive function. Everything else I&#39;m about to describe works better on top of a foundation of consistent physical activity. Parkour is my thing, but any movement practice works.</p>


<h2 id="hack-1-intermittent-hypoxia">Hack #1: Intermittent hypoxia</h2>
<p>This is the one that surprised me the most.</p>
<h3 id="what-it-actually-is">What it actually is</h3>
<p>Intermittent hypoxia training (IHT) is simple: you alternate short periods of reduced oxygen breathing with normal recovery. It&#39;s the same principle behind altitude training, the thing that endurance athletes have been doing for decades to improve oxygen efficiency, except you can do it at sea level, sitting at your desk, in 15-30 minutes.</p>
<p>The protocol: controlled breathing cycles that temporarily reduce your blood oxygen saturation, followed by recovery breathing. Repeat for several rounds. That&#39;s it.</p>
<p>I started doing this to improve my breath-hold times and cardiovascular performance for parkour. The results in training were immediate. Longer runs, faster recovery between sequences, and much better composure during high-risk movements. When you&#39;re mid-air in a precision jump, the ability to manage your adrenaline response is not optional. IHT gave me noticeably more control over that.</p>
<h3 id="what-changed-for-me">What changed for me</h3>
<p>But the real surprise came outside of training sessions. And this is the part that I think matters for anyone who works with their brain.</p>
<p>After about two weeks of consistent IHT practice, I noticed something: <strong>I was breathing more slowly throughout the entire day</strong>. Not because I was trying to. My body had simply recalibrated. Where I used to take 14-16 breaths per minute sitting at my desk, I dropped to 8-10.</p>
<p>This sounds minor. It&#39;s not.</p>
<p>Patrick McKeown, author of <em>The Oxygen Advantage</em>, has documented extensively how slower, lighter nasal breathing improves CO2 tolerance. Higher CO2 tolerance means better oxygen delivery to tissues, including your brain. It&#39;s counterintuitive (more CO2 = better oxygenation?), but the physiology is well-established: CO2 is what triggers hemoglobin to release oxygen to cells (the Bohr effect). If you breathe fast and shallow, you actually reduce oxygen delivery despite taking in more air.</p>
<p>Here&#39;s what I noticed in my daily work after my breathing shifted:</p>
<p><strong>Sustained focus got dramatically easier.</strong> I used to hit a wall at about 90 minutes of deep work. That wall moved to 3-4 hours. Not through willpower or discipline. I just didn&#39;t get tired as quickly. Better brain oxygenation, fewer stress hormones circulating, more stable energy.</p>
<p><strong>Adrenaline management at work.</strong> This was the one I didn&#39;t expect. Running a startup (or even just being a senior developer) means constant high-stakes moments. A production incident at 2am. A hard conversation with a coworker. An investor call where you have 20 minutes to make your case. Before IHT, these situations would leave me wired for hours afterward. Now the recovery is almost instant. The same nervous system control that helps me stay calm mid-air during parkour helps me stay clear-headed during a crisis at work.</p>
<p><strong>Faster cognitive recovery.</strong> After an intense 3-hour coding session or a difficult meeting, I used to need a significant break before I could do anything useful again. Now I bounce back in minutes. It&#39;s like my brain&#39;s recovery time dropped from &quot;restart the computer&quot; to &quot;close the tab and open a new one.&quot;</p>
<figure align="center">
  <img class="center" src="https://aralroca.com/images/blog-images/biohack-meditation.webp" alt="Controlled breathing practice at sunrise" />
  <figcaption><small>The simplest performance intervention: breathe less</small></figcaption>
</figure>

<h3 id="the-science-briefly">The science (briefly)</h3>
<p>I&#39;ll keep this short because I&#39;m a developer, not a scientist, but I did go down the rabbit hole on the research:</p>
<p>A 2022 study in <em>Frontiers in Neuroscience</em> found that IHT causes &quot;proadaptive modifications&quot; in the brain, stimulating BDNF (brain-derived neurotrophic factor) and increasing what they called &quot;the adaptive potential, endurance, and working capacity of the brain&quot; <a href="#references">[1]</a>. Another study showed significant gains in memory recall and attention in participants following IHT protocols <a href="#references">[2]</a>.</p>
<p>The mechanism is called <strong>hormesis</strong>. Controlled, small doses of stress that make the body more resilient. Same principle as cold exposure, sauna, or fasting. The difference is that hypoxia specifically targets oxygen efficiency and neural adaptation.</p>
<p>A study published in <em>Physiology</em> documented that intermittent hypoxia upregulates three growth factors (EPO, BDNF, and VEGF) all directly linked to enhanced neural function and cellular resilience <a href="#references">[10]</a>. This isn&#39;t about feeling zen. It&#39;s about building a more capable brain at the physiological level.</p>
<p>Andrew Huberman, a Stanford neuroscientist, has documented how breathing protocols shift autonomic nervous system state, reducing cortisol, increasing parasympathetic tone, and enhancing executive function <a href="#references">[4]</a>. IHT is essentially a more intense version of breathwork: it doesn&#39;t just teach you to breathe better in the moment. It permanently upgrades your baseline respiratory efficiency.</p>
<p>My resting heart rate went from 68 to 56 bpm. That&#39;s not a small change.</p>
<h2 id="hack-2-the-pomodoro-technique-done-right">Hack #2: The Pomodoro technique, done right</h2>
<p>I know, I know. Everyone&#39;s heard of Pomodoro. &quot;25 minutes of work, 5 minutes of break, revolutionary.&quot; Most people try it for a week and drop it because it feels artificial.</p>
<p>I did the same thing years ago. Then parkour taught me something that made me come back to it with a completely different understanding.</p>
<h3 id="why-most-people-do-it-wrong">Why most people do it wrong</h3>
<p>The issue isn&#39;t the timer. It&#39;s what people do during the &quot;break.&quot;</p>
<p>Most developers hit the 5-minute break and immediately check Slack, scroll Twitter, read email, or look at their phone. That&#39;s not rest. That&#39;s a different kind of cognitive load. Your prefrontal cortex doesn&#39;t get to discharge. It just switches to a different task. When the timer starts again, you&#39;re not fresh. You&#39;re fragmented.</p>
<h3 id="the-parkour-parallel">The parkour parallel</h3>
<p>Here&#39;s what parkour taught me about rest, and it took me years to really internalize this:</p>
<p><strong>You need rest to improve. The gains happen during recovery, not during effort.</strong></p>
<p>In a parkour training session, you give 100% on a sequence. A run, a series of jumps, a technical combination. Then you walk back. You breathe. You shake out your arms. You stand there for a minute doing literally nothing. And then you go again at 100%.</p>
<p>If you skip that recovery and try to chain explosive movements back-to-back, two things happen: your performance degrades fast, and you get injured. Every serious practitioner learns this the hard way.</p>
<p>Your brain works exactly the same.</p>
<p>When you try to code for four straight hours without structured breaks, the last two hours are objectively worse. Your attention drifts. Your error rate climbs. You make architectural decisions you&#39;ll regret tomorrow. But you don&#39;t notice it in the moment because cognitive degradation is invisible from the inside.</p>
<h3 id="how-i-actually-use-it">How I actually use it</h3>
<figure align="center">
  <img class="center" src="https://aralroca.com/images/blog-images/biohack-focus.webp" alt="Focused deep work session at a desk" />
  <figcaption><small>25 minutes of this, then 5 minutes of not-this</small></figcaption>
</figure>

<p>Once I understood that the 5-minute break is not a concession to weakness but the actual mechanism that makes high performance sustainable, Pomodoro clicked for me.</p>
<p><strong>25 minutes of full-intensity focus.</strong> One task. No Slack. No email. No &quot;quick check&quot; on anything. If something pops into my head, I write it on a sticky note and go back to what I&#39;m doing. The goal is to create a state of total immersion, the same mental state I&#39;m in during a parkour sequence where I literally can&#39;t afford to think about anything else.</p>
<p><strong>5 minutes of real rest.</strong> Stand up. Look out the window. Walk to the kitchen. Breathe. The key word is &quot;real.&quot; Your eyes need to leave the screen. Your working memory needs to flush. If you don&#39;t do this, you&#39;re just doing 4 hours of gradually degrading work with arbitrary timer interruptions.</p>
<p>The parallel to parkour is direct: train hard in bursts, rest completely between sets. The rest is what lets you sustain 100% intensity. Without it, by the third round you&#39;re operating at 60% and making mistakes. Except at your desk, the &quot;injury&quot; is a bug you&#39;ll spend two days debugging.</p>
<p>Here&#39;s a trick I use that combines both hacks: <strong>during some Pomodoro breaks, I do a quick round of breath-hold exercises.</strong> Five minutes of controlled breathing resets my autonomic nervous system and primes me for the next focus block. It&#39;s like a mini-reboot. When the timer starts again, I&#39;m genuinely fresh, not just &quot;took a break&quot; fresh.</p>
<p>After four Pomodoro cycles, I take a longer break of 15-20 minutes. Go outside if possible. Move. The compounding effect across a full day is dramatic. Hour six feels like hour one.</p>
<h2 id="hack-3-walking-meetings">Hack #3: Walking meetings</h2>
<p>This one is simple but I almost never see people talk about it.</p>
<p>Many meetings don&#39;t require you to be sitting at a desk staring at slides. Status updates, brainstorming sessions, 1-on-1s, planning discussions. For all of these, you can be walking.</p>
<p>I use a walking treadmill at my desk during meetings. Slow pace, 3-4 km/h, just enough to keep blood flowing. The effects are immediate: better attention, less fidgeting, fewer distractions. There&#39;s something about low-intensity movement that keeps the brain engaged without consuming cognitive resources. Research backs this up: walking improves creative thinking and sustained attention.</p>
<figure align="center">
  <img class="center" src="https://aralroca.com/images/blog-images/biohack-aral-treadmill.webp" alt="Aral walking on a treadmill desk while working" />
  <figcaption><small>My walking treadmill setup. Most meetings don't require sitting.</small></figcaption>
</figure>

<p>But here&#39;s the part nobody mentions: <strong>what happens after the meeting.</strong> When you&#39;ve been sitting for an hour-long call, you finish drained. You need a transition period before you can do real work again. When you&#39;ve been walking during that same call, you finish energized. You sit down at your desk and you&#39;re immediately ready to work. The meeting didn&#39;t drain your battery. It charged it.</p>
<p>This has been particularly useful for me during weeks with heavy meeting loads. The meetings themselves become light exercise, and the transitions between meetings and deep work become seamless.</p>
<p>Obviously this doesn&#39;t work for every meeting. If you&#39;re pair programming, sharing your screen, or doing a code review where you need to type, sit down. But for the 60-70% of meetings that are primarily conversation, walking is strictly better than sitting.</p>
<h2 id="hack-4-strategic-caffeine">Hack #4: Strategic caffeine</h2>
<p>I&#39;ve never been a big coffee drinker. But I noticed that the few times I did have coffee, the effect was massive. That got me thinking about why.</p>
<p>The answer is simple: most people drink coffee every day, and daily caffeine consumption builds tolerance fast. After a couple of weeks, your morning coffee doesn&#39;t enhance your performance. It gets you back to your baseline. You&#39;re not getting a boost. You&#39;re paying a tax (dependency, sleep disruption, afternoon crashes) to feel normal. That&#39;s a bad trade.</p>
<p>Because I don&#39;t have that daily habit, I can treat caffeine purely as a tool.</p>
<figure align="center">
  <img class="center" src="https://aralroca.com/images/blog-images/biohack-coffee.webp" alt="Coffee cups" />
  <figcaption><small>What if the daily coffee ritual is actually costing you more than it gives?</small></figcaption>
</figure>

<p>Most days I drink water. Just water. This means my caffeine tolerance stays low. Then, when I actually need it (I slept poorly and have a big day ahead, there&#39;s a product launch, I&#39;m doing a full day of complex pair programming) a single coffee delivers a genuine cognitive boost. Alertness, processing speed, working memory, all measurably better because my body isn&#39;t habituated.</p>
<p>Think of it like a power-up in a game. If you use it on every level, it stops being special. If you save it for the boss fight, it actually matters.</p>
<p>The transition period when you stop daily coffee is about a week of mild headaches and low energy. After that, you reach a new normal where you feel fine without it, and caffeine becomes something you choose to deploy strategically rather than something you depend on to function.</p>
<p>I know this one is controversial. Coffee culture is deeply embedded in developer identity. All I can say is: try it for two weeks and see what happens. The worst that can happen is you go back to your regular habit.</p>
<h2 id="hack-5-cold-exposure">Hack #5: Cold exposure</h2>
<p>This is a quick one. A cold shower at the end of my normal shower, 60-90 seconds of cold water, produces a norepinephrine spike that improves alertness and mood for hours. This is well-documented by Huberman and others <a href="#references">[4]</a>.</p>
<p>I&#39;m not doing ice baths or any extreme Wim Hof protocol. Just cold water at the end. The cost is minimal (it&#39;s uncomfortable for about 30 seconds, then you adapt). The effect on morning focus is noticeable and consistent.</p>
<p>Wim Hof&#39;s method (breathing + cold + meditation) was actually validated in a Radboud University study that showed trained participants could voluntarily influence their sympathetic nervous system and innate immune response <a href="#references">[8]</a>. That&#39;s remarkable. But you don&#39;t need the full method to get the basic alertness benefit. Just turn the handle to cold for a minute at the end of your shower.</p>
<h2 id="hack-6-power-naps">Hack #6: Power naps</h2>
<p>When I hit a wall after lunch or after a heavy morning, a 20-30 minute nap resets me completely. Not a &quot;nice to have.&quot; A genuine cognitive reboot.</p>
<p>The key is keeping it under 30 minutes. Go longer and you enter deep sleep, which means you wake up groggy and worse than before. But a short nap, 20 minutes is the sweet spot, clears the mental fog and gives you what feels like a second morning. I&#39;ve had some of my most productive afternoons right after a power nap.</p>
<p>I don&#39;t schedule them. I just listen to my body. If I&#39;m not tired, napping is pointless. But when the signal is there, fighting it with coffee or willpower is a losing strategy. The nap is faster, free, and has no side effects.</p>
<h2 id="what-didnt-work">What didn&#39;t work</h2>
<p>I think it&#39;s important to mention what I tried that either didn&#39;t stick or didn&#39;t produce noticeable results. Not everything works for everyone, and I don&#39;t want to give the impression that I just tried five things and all five were magic.</p>
<p><strong>Meditation apps.</strong> I tried Headspace and Calm for a few months each. They&#39;re well-made products, but guided meditation didn&#39;t produce the same physiological changes as breathwork and IHT. My suspicion is that meditation works better as a long-term practice (years, not months) while breathing protocols produce measurable changes in weeks. Your mileage may vary.</p>
<p><strong>Strict time-blocking the entire day.</strong> I tried scheduling every 30-minute slot for a few weeks. It created more anxiety than productivity. Pomodoro works for me because it structures <em>intensity and rest</em>, not <em>content</em>. I decide what to work on, then I decide how intensely to focus on it. Scheduling every minute took away the flexibility that makes creative work possible.</p>
<h2 id="the-thing-nobody-talks-about-silicon-valley-already-does-this">The thing nobody talks about: Silicon Valley already does this</h2>
<figure align="center">
  <img class="center" src="https://aralroca.com/images/blog-images/biohack-sv.webp" alt="Modern tech office corridor" />
  <figcaption><small>Behind the clean offices, a quiet obsession with physiological optimization</small></figcaption>
</figure>

<p>Here&#39;s what I find interesting. These aren&#39;t fringe biohacking experiments. Breathwork and oxygen manipulation have been quietly adopted across the tech industry for years. Not as a trend, but as a serious performance tool.</p>
<p>Bryan Johnson, the founder of Braintree (sold to PayPal for $800M), spends roughly $2M per year on biohacking protocols. The man literally moved his office into a hyperbaric chamber <a href="#references">[5]</a>. Now, $2M/year is absurd and not what I&#39;m suggesting. But the underlying logic, that physiological optimization directly translates to cognitive performance, is sound.</p>
<p>Jack Dorsey, co-founder of Twitter and Block, has practiced meditation and controlled breathing for over 20 years <a href="#references">[6]</a>. Tim Ferriss has dedicated entire podcast episodes to breathing protocols for performance, hosting James Nestor (author of the NYT bestseller <em>Breath: The New Science of a Lost Art</em>) to discuss how subtle breathing adjustments transform cognitive output <a href="#references">[7]</a>.</p>
<p>Dave Asprey, founder of Bulletproof, has worked extensively with Wim Hof. Patrick McKeown&#39;s Oxygen Advantage method, which is essentially breath-hold training that simulates altitude, has been adopted by athletes, military operators, and increasingly, tech executives <a href="#references">[3]</a>.</p>
<p><em>Longevity Technology</em> reported that biohacking has become &quot;a major trend among Silicon Valley executives seeking to optimize health and performance,&quot; with breathwork, cryotherapy, and oxygen manipulation among the most commonly adopted protocols <a href="#references">[9]</a>.</p>
<p>What&#39;s telling is <em>why</em> these people gravitate toward physiological hacks rather than (or in addition to) productivity software. When you&#39;re building something that demands 12-16 hours of sustained cognitive output per day, across months and years, the bottleneck is not your task manager. It&#39;s not your team structure. It&#39;s not your tech stack. <strong>The bottleneck is your brain&#39;s ability to maintain quality output under sustained pressure.</strong> That&#39;s a physiology problem, not a tooling problem.</p>
<p>And marginal gains in oxygen efficiency and stress resilience compound dramatically over time.</p>
<h2 id="the-physiological-argument-for-founders">The physiological argument for founders</h2>
<p>The startup world glorifies hustle but ignores the hardware it runs on. You can optimize your deployment pipeline, your sprint process, your hiring funnel. But if your brain is running on shallow breathing and chronic cortisol, none of it matters. You&#39;re making your worst decisions at the end of the day, exactly when they matter most.</p>
<p>Consider what changes when your baseline physiology is different:</p>
<p><strong>Your resting heart rate drops.</strong> Mine went from 68 to 56 bpm. A lower resting heart rate correlates with better cardiovascular efficiency, better stress recovery, and better autonomic regulation. You&#39;re literally running calmer all day.</p>
<p><strong>Your breathing rate drops.</strong> 8 breaths per minute instead of 15. Fewer breaths means less sympathetic nervous system activation throughout the day. Less fight-or-flight. Less background anxiety. Less cortisol.</p>
<p><strong>Your work is structured around recovery.</strong> Pomodoro ensures that hour six of your day is as sharp as hour one. You don&#39;t gradually degrade. You stay at peak performance in short bursts and actively recover between them.</p>
<p><strong>Your oxygen utilization improves.</strong> The same blood volume delivers more oxygen to your prefrontal cortex, the part of your brain that handles planning, decision-making, and impulse control. The exact functions you need most as a founder.</p>
<p><strong>Your stress recovery accelerates.</strong> The interval between &quot;something went wrong&quot; and &quot;here&#39;s what we&#39;re going to do about it&quot; shrinks from minutes to seconds. This is probably the most practically valuable change. In a startup, the speed at which you can move from panic to plan is everything.</p>
<p>None of this is woo. James Nestor&#39;s research, documented across multiple peer-reviewed collaborations, shows that breathing efficiency is one of the single highest-leverage interventions for cognitive performance <a href="#references">[7]</a>. And a study published in <em>Physiology</em> documented that intermittent hypoxia upregulates EPO, BDNF, and VEGF, three growth factors directly linked to neural function and cellular resilience <a href="#references">[10]</a>.</p>
<h2 id="how-to-start">How to start</h2>
<p>None of this requires expensive equipment or complicated protocols.</p>
<p><strong>Intermittent hypoxia, the 5-minute version:</strong></p>
<ol>
<li>Breathe normally through your nose for 2 minutes. Just calm, quiet nasal breathing.</li>
<li>Take a normal breath in, exhale gently, and hold your breath.</li>
<li>Walk slowly while holding. When you feel a moderate urge to breathe, stop.</li>
<li>Resume nasal breathing and recover for 1-2 minutes.</li>
<li>Repeat for 6-8 rounds.</li>
</ol>
<p>This is a simplified version of Patrick McKeown&#39;s Oxygen Advantage method <a href="#references">[3]</a>. It works by increasing your CO2 tolerance and training your body to do more with less oxygen, exactly what altitude training does, but at sea level.</p>
<p>For more structured sessions with timed rounds and progressive difficulty, I built an <a href="https://kitmul.com/en/sport-performance/hypoxia-breathing-timer">intermittent hypoxia breathing timer</a> as one of the tools on Kitmul.</p>
<p><strong>Pomodoro, the non-negotiable version:</strong></p>
<ol>
<li>Pick one task. Not &quot;a few things.&quot; One.</li>
<li>Set a timer for 25 minutes.</li>
<li>Work at full focus. No Slack, no email, no phone.</li>
<li>When the timer rings, stop. Stand up. Walk away from your screen for 5 minutes.</li>
<li>Repeat. After 4 cycles, take a 15-20 minute break.</li>
</ol>
<p>The discipline is in the rest, not the work. Anyone can focus for 25 minutes. The hard part is actually stopping and actually resting. If you want a good timer, there&#39;s a <a href="https://kitmul.com/en/agile-project-management/pomodoro-agile">Pomodoro timer on Kitmul</a> too.</p>
<p><strong>Caffeine reset:</strong></p>
<p>Stop drinking coffee for one week. Push through the headaches (they peak at day 2-3 and disappear by day 5). Then only use caffeine on days when you genuinely need a boost. You&#39;ll be amazed at how much more effective a single cup becomes.</p>
<p><strong>Cold exposure:</strong></p>
<p>At the end of your normal shower, turn the water to cold for 60-90 seconds. That&#39;s it. The first week is uncomfortable. After that, it becomes almost pleasant, and the alertness benefit is consistent.</p>
<p><strong>Walking meetings:</strong></p>
<p>Buy a cheap walking treadmill. Use it during any meeting where you don&#39;t need to type. You&#39;ll finish meetings energized instead of drained.</p>
<hr>
<p>I started all of this because of parkour. The breathing work, the structured rest, the body awareness. It all came from athletic training, not from reading productivity blogs. But the transfer to knowledge work was immediate and dramatic.</p>
<p>The best productivity hack I&#39;ve found in over ten years of professional software development isn&#39;t a tool, a framework, or an AI agent. It&#39;s optimizing the machine that runs all the other machines: your body.</p>
<p>Oxygen and well-timed rest. That&#39;s it.</p>
<h2 id="references">References</h2>
<ol>
<li><p>Rybnikova, E. &amp; Nalivaeva, N. (2022). &quot;Intermittent Hypoxic Training as an Effective Tool for Increasing the Adaptive Potential, Endurance and Working Capacity of the Brain.&quot; <em>Frontiers in Neuroscience</em>. <a href="https://pmc.ncbi.nlm.nih.gov/articles/PMC9254677/">Read study</a></p>
</li>
<li><p>Schega, L. et al. (2013). &quot;Effects of Intermittent Hypoxia on Cognitive Performance and Quality of Life in Elderly Adults.&quot; <em>Gerontology</em>. <a href="https://pubmed.ncbi.nlm.nih.gov/23689305/">Read on PubMed</a></p>
</li>
<li><p>McKeown, P. <em>The Oxygen Advantage</em>. Breath-hold exercises that simulate altitude training at sea level. <a href="https://oxygenadvantage.com/pages/patrick-mckeown-m-a-tcd">Official site</a></p>
</li>
<li><p>Huberman, A. &quot;Breathwork Protocols for Health, Focus &amp; Stress.&quot; <em>Huberman Lab</em>. <a href="https://www.hubermanlab.com/newsletter/breathwork-protocols-for-health-focus-stress">Read article</a></p>
</li>
<li><p>Johnson, B. Braintree founder, $2M/year biohacking protocol including hyperbaric oxygen. <a href="https://longevity.technology/news/silicon-valleys-biohacking-obsession-why-tech-executives-are-hooked/">Source</a></p>
</li>
<li><p>Dorsey, J. 20+ years of meditation and breathwork practice. <a href="https://longevity.technology/news/silicon-valleys-biohacking-obsession-why-tech-executives-are-hooked/">Source</a></p>
</li>
<li><p>Nestor, J. <em>Breath: The New Science of a Lost Art</em>. NYT bestseller on breathing science. <a href="https://www.mrjamesnestor.com/">Official site</a></p>
</li>
<li><p>Wim Hof Method. Radboud University study on voluntary influence over sympathetic nervous system. <a href="https://www.wimhofmethod.com/biohacking">Read more</a></p>
</li>
<li><p>&quot;Silicon Valley&#39;s biohacking obsession: Why tech executives are hooked.&quot; <em>Longevity Technology</em>. <a href="https://longevity.technology/news/silicon-valleys-biohacking-obsession-why-tech-executives-are-hooked/">Read article</a></p>
</li>
<li><p>Dale, E. et al. (2014). &quot;Unexpected Benefits of Intermittent Hypoxia: Enhanced Respiratory and Nonrespiratory Motor Function.&quot; <em>Physiology</em>. <a href="https://pmc.ncbi.nlm.nih.gov/articles/PMC4073945/">Read study</a></p>
</li>
<li><p><a href="https://kitmul.com/en/sport-performance/hypoxia-breathing-timer">Intermittent Hypoxia Breathing Timer</a>. Free guided tool for structured IHT sessions.</p>
</li>
<li><p><a href="https://kitmul.com/en/agile-project-management/pomodoro-agile">Pomodoro Timer</a>. Free focus timer with structured work/rest cycles.</p>
</li>
</ol>
]]></content:encoded>
            </item>
            <item>
              <title>Don't Fall Into the CDN Trap! 🪤</title>
              <description>How Brisa's HTML Streaming feature can help you avoid the CDN trap and improve your app’s performance. We’ll also compare Brisa with other popular tools like HTMX and React to highlight the benefits of using Brisa for server-side rendering.</description>
              <link>https://aralroca.com/blog/brisa-vs-htmx-vs-react</link>
              <guid isPermaLink="false">https://aralroca.com/blog/brisa-vs-htmx-vs-react/</guid>
              <pubDate>Thu Dec 19 2024 00:00:00 GMT+0000 (Coordinated Universal Time)</pubDate>
              <content:encoded><![CDATA[<p>This blog post’ll explore how <strong>Brisa&#39;s HTML Streaming</strong> feature can help you avoid the CDN trap and improve your app’s performance. We’ll also compare <a href="https://brisa.build">Brisa</a> with other popular tools like <strong>HTMX</strong> and <strong>React</strong> to highlight the benefits of using Brisa for server-side rendering.</p>
<h2 id="why-html-streaming-is-important">Why HTML Streaming is Important</h2>
<p>HTML Streaming is a powerful technique that allows you to send <strong>HTML incrementally</strong> from the server to the client, improving performance and reducing latency. With <a href="https://brisa.build">Brisa</a>, you can stream HTML content directly to the client, not only for the initial render but also for subsequent updates and server actions.</p>
<img src="https://aralroca.com/images/blog-images/render.png" alt="HTML Streaming vs CDN + fetching data" class="center" />

<p>If your app needs just <strong>1 request</strong> to fetch server data, you’re stuck in the <strong>CDN trap</strong>. Add more requests, and you’re in a cascading nightmare. CDNs are fine for assets—nothing more. Your website is an asset only if it doesn’t rely on server data. </p>
<p><strong>Rendering</strong> the Components on the <strong>server</strong> with <strong>streaming</strong> is the best way to <strong>avoid the CDN trap</strong>. Brisa not only uses <strong>HTML Streaming</strong> for the first render but also as a response to <strong>Server Actions</strong> after rendering a component on the server.</p>
<p>You may wonder, but many components are static and I don&#39;t want to render them always on the server. Well, Brisa allows you to <a href="https://brisa.build/api-reference/extended-props/renderOn#renderon"><strong>pre-render them at build-time</strong></a> with just one attribute, so that <strong>only dynamic components are rendered</strong> on the server.</p>
<pre><code class="hljs language-tsx">&lt;<span class="hljs-title class_">StaticComponent</span> renderOn=<span class="hljs-string">&quot;build&quot;</span> /&gt;
</code></pre><h2 id="do-dynamic-imports-solve-the-cdn-trap">Do dynamic imports solve the CDN trap?</h2>
<p><strong>No</strong>. Using HTML Streaming on Server Actions have exactly the same benefits as using it on the first render.</p>
<p>Imagine managing the <strong>opening</strong> and <strong>rendering</strong> of a <strong>dialog</strong> with a <strong>dynamic import</strong>:</p>
<img src="https://aralroca.com/images/blog-images/dialog.png" alt="Server-side Dialog vs Client-side Dialog" class="center" />

<p>Only if the dialog needs server data, we are in the same problem.</p>
<h2 id="code-example-quiz-with-html-streaming">Code example: Quiz with HTML Streaming</h2>
<p>As an example, we are going to make a quiz of questions in Brisa using HTML Streaming through Server Actions, and you will see how easy it is to do it, apart from its benefits.</p>
<p>In order to make the quiz, we are going to create a component that will be rendered on the server and will be responsible for managing the questions and answers. The questions will be sent to the client through HTML Streaming, and the answers will be processed on the server.</p>
<pre><code class="hljs language-tsx"><span class="hljs-keyword">import</span> { renderComponent } <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;brisa/server&#x27;</span>;

<span class="hljs-comment">// Define the type for questions to ensure type safety.</span>
<span class="hljs-keyword">type</span> <span class="hljs-title class_">Question</span> = {
  <span class="hljs-attr">answer</span>: <span class="hljs-built_in">string</span>; <span class="hljs-comment">// The correct answer for the question (&#x27;yes&#x27; or &#x27;no&#x27;).</span>
  <span class="hljs-attr">question</span>: <span class="hljs-built_in">string</span>; <span class="hljs-comment">// The text of the question to be displayed.</span>
  <span class="hljs-attr">id</span>: <span class="hljs-built_in">number</span>; <span class="hljs-comment">// Unique identifier for each question.</span>
};

<span class="hljs-comment">// All this code is server-code. It ensures that the user cannot infer the correct</span>
<span class="hljs-comment">// answers by inspecting the page source or the network.</span>
<span class="hljs-keyword">const</span> <span class="hljs-attr">questions</span>: <span class="hljs-title class_">Question</span>[] = [
  { <span class="hljs-attr">id</span>: <span class="hljs-number">1</span>, <span class="hljs-attr">answer</span>: <span class="hljs-string">&#x27;no&#x27;</span>, <span class="hljs-attr">question</span>: <span class="hljs-string">&#x27;Is the Earth flat? &#x27;</span> },
  { <span class="hljs-attr">id</span>: <span class="hljs-number">2</span>, <span class="hljs-attr">answer</span>: <span class="hljs-string">&#x27;yes&#x27;</span>, <span class="hljs-attr">question</span>: <span class="hljs-string">&#x27;Is the Earth round? &#x27;</span> },
  { <span class="hljs-attr">id</span>: <span class="hljs-number">3</span>, <span class="hljs-attr">answer</span>: <span class="hljs-string">&#x27;no&#x27;</span>, <span class="hljs-attr">question</span>: <span class="hljs-string">&#x27;Can giraffes lay eggs?&#x27;</span> },
  { <span class="hljs-attr">id</span>: <span class="hljs-number">4</span>, <span class="hljs-attr">answer</span>: <span class="hljs-string">&#x27;yes&#x27;</span>, <span class="hljs-attr">question</span>: <span class="hljs-string">&#x27;Can penguins fly?&#x27;</span> },
  { <span class="hljs-attr">id</span>: <span class="hljs-number">5</span>, <span class="hljs-attr">answer</span>: <span class="hljs-string">&#x27;no&#x27;</span>, <span class="hljs-attr">question</span>: <span class="hljs-string">&#x27;Can a cow jump over the moon?&#x27;</span> },
  { <span class="hljs-attr">id</span>: <span class="hljs-number">6</span>, <span class="hljs-attr">answer</span>: <span class="hljs-string">&#x27;yes&#x27;</span>, <span class="hljs-attr">question</span>: <span class="hljs-string">&#x27;Is water wet by definition?&#x27;</span> },
  { <span class="hljs-attr">id</span>: <span class="hljs-number">7</span>, <span class="hljs-attr">answer</span>: <span class="hljs-string">&#x27;yes&#x27;</span>, <span class="hljs-attr">question</span>: <span class="hljs-string">&#x27;Is the sky blue?&#x27;</span> },
  { <span class="hljs-attr">id</span>: <span class="hljs-number">8</span>, <span class="hljs-attr">answer</span>: <span class="hljs-string">&#x27;yes&#x27;</span>, <span class="hljs-attr">question</span>: <span class="hljs-string">&#x27;Do fish sleep with their eyes open?&#x27;</span> },
  { <span class="hljs-attr">id</span>: <span class="hljs-number">9</span>, <span class="hljs-attr">answer</span>: <span class="hljs-string">&#x27;no&#x27;</span>, <span class="hljs-attr">question</span>: <span class="hljs-string">&#x27;Can a cow fly?&#x27;</span> },
];

<span class="hljs-comment">// Function to open the modal with a random question.</span>
<span class="hljs-keyword">function</span> <span class="hljs-title function_">openModal</span>(<span class="hljs-params"></span>) {
  <span class="hljs-keyword">const</span> randomIndex = <span class="hljs-title class_">Math</span>.<span class="hljs-title function_">floor</span>(<span class="hljs-title class_">Math</span>.<span class="hljs-title function_">random</span>() * questions.<span class="hljs-property">length</span>);
  
  <span class="hljs-title function_">renderComponent</span>({
    <span class="hljs-attr">element</span>: <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">Modal</span> {<span class="hljs-attr">...questions</span>[<span class="hljs-attr">randomIndex</span>]} /&gt;</span></span>,
    <span class="hljs-attr">target</span>: <span class="hljs-string">&#x27;#content&#x27;</span>, <span class="hljs-comment">// Specify the DOM element where the modal should render.</span>
  });
}

<span class="hljs-comment">// Function to process the user’s answer and display the result.</span>
<span class="hljs-keyword">function</span> <span class="hljs-title function_">processAnswer</span>(<span class="hljs-params"><span class="hljs-attr">e</span>: <span class="hljs-title class_">ClickEvent</span>, value = <span class="hljs-string">&#x27;yes&#x27;</span></span>) {
  <span class="hljs-comment">// We have the event on the server, so we can access the target and the dataset!</span>
  <span class="hljs-keyword">const</span> id = (e.<span class="hljs-property">target</span> <span class="hljs-keyword">as</span> <span class="hljs-title class_">HTMLButtonElement</span>).<span class="hljs-property">dataset</span>.<span class="hljs-property">id</span>;

  <span class="hljs-comment">// Check if the user&#x27;s answer matches the correct answer.</span>
  <span class="hljs-keyword">const</span> isCorrect = questions.<span class="hljs-title function_">find</span>(<span class="hljs-function">(<span class="hljs-params">q</span>) =&gt;</span> q.<span class="hljs-property">id</span> === <span class="hljs-title class_">Number</span>(id)).<span class="hljs-property">answer</span> === value;

  <span class="hljs-comment">// Render feedback based on the correctness of the answer.</span>
  <span class="hljs-title function_">renderComponent</span>({
    <span class="hljs-attr">element</span>: isCorrect ? (
      <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">id</span>=<span class="hljs-string">&quot;content&quot;</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;correct&quot;</span>&gt;</span>
        Correct!
      <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span></span>
    ) : (
      <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">id</span>=<span class="hljs-string">&quot;content&quot;</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;incorrect&quot;</span>&gt;</span>
        Incorrect!
      <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span></span>
    ),
    <span class="hljs-attr">target</span>: <span class="hljs-string">&#x27;dialog&#x27;</span>,
  });
}

<span class="hljs-comment">// Main homepage component.</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">Homepage</span>(<span class="hljs-params"></span>) {
  <span class="hljs-keyword">return</span> (
    <span class="language-xml"><span class="hljs-tag">&lt;&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;hero&quot;</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;h1_addition&quot;</span>&gt;</span>Welcome to <span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>Brisa
        <span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;edit-note&quot;</span>&gt;</span>✏️ SSR Modal example<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">code</span>&gt;</span>src/pages/index.tsx<span class="hljs-tag">&lt;/<span class="hljs-name">code</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

      <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">onSubmit</span>=<span class="hljs-string">{openModal}</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">button</span>&gt;</span>Open modal<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">&quot;content&quot;</span> /&gt;</span> {/* Container for rendering dynamic content */}
      <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
    <span class="hljs-tag">&lt;/&gt;</span></span>
  );
}

<span class="hljs-comment">// Modal component to display a question and answer options.</span>
<span class="hljs-keyword">function</span> <span class="hljs-title function_">Modal</span>(<span class="hljs-params">{ question, answer, id }: <span class="hljs-title class_">Question</span></span>) {
  <span class="hljs-keyword">return</span> (
    <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dialog</span> <span class="hljs-attr">open</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">method</span>=<span class="hljs-string">&quot;dialog&quot;</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>{question}<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
        {/* Button for &quot;Yes&quot; answer */}
        <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">data-id</span>=<span class="hljs-string">{id}</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{(e)</span> =&gt;</span> processAnswer(e, &#x27;yes&#x27;)}&gt;
          Yes
        <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
        {/* Button for &quot;No&quot; answer */}
        <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">data-id</span>=<span class="hljs-string">{id}</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{(e)</span> =&gt;</span> processAnswer(e, &#x27;no&#x27;)}&gt;
          No
        <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">dialog</span>&gt;</span></span>
  );
}
</code></pre><ul>
<li><a href="https://github.com/brisa-build/brisa/tree/canary/examples/with-ssr-modal">🔗 Code example here</a></li>
</ul>
<p>One of the advantages of using server-side logic for rendering and controlling a dialog is the enhanced security and simplicity it offers. With Brisa, you can offload both the rendering and logic processing to the server, ensuring that critical data remains inaccessible to the client and maintaining a clean separation of concerns.</p>
<p>In this example, a modal dialog is used to present random quiz questions to the user. The logic for selecting the question, validating the answer, and rendering the UI is entirely managed on the server. This approach eliminates the risk of exposing sensitive data, such as the correct answer, to the client.</p>
<p>Normally we load modals on the client with a dynamic import to avoid loading them at the start, requesting the CDN, and then if the modal needs server data, once rendered it has to make a cascade of calls to the server. With Brisa, we can load the modal directly from the server, avoiding the need to make additional calls and keeping the modal logic on the server.</p>
<div align="center">

</div>


<p>With this approach, we get the following benefits:</p>
<ol>
<li><strong>Enhanced Security:</strong> Sensitive data, such as correct answers, remain on the server, preventing unauthorized access or manipulation.</li>
<li><strong>Improve UX</strong>: By rendering the modal directly on the server with streaming, we can avoid additional network requests and improve the user experience thanks to streaming.</li>
<li><strong>Simplified State Management:</strong>  By centralizing logic on the server, the client remains lightweight and focuses only on rendering and user interaction.</li>
<li><strong>Reduced Client-Side Complexity and Size:</strong> No need for complex state management libraries or additional client-side logic to handle the modal. The server manages everything and you can do an SPA without increasing the client bundle size.</li>
</ol>
<h2 id="spa-without-client-side-javascript">SPA without Client-Side JavaScript?</h2>
<p><strong>Yes</strong>, you can! Although we support writing <a href="https://brisa.build/building-your-application/components-details/web-components">Web Components</a> with <strong>JSX</strong> and <strong>Signals</strong> with very little code (3kb), we want you to use them <strong>only</strong> for pure <strong>client interactions</strong>. Our <strong>goal</strong> is to make it possible to create <strong>SPAs without</strong> the need for <strong>client-side JavaScript</strong>.</p>
<p>Imagine that your <strong>e-commerce</strong> site, instead of having a <strong>cascade</strong> of <strong>requests</strong> and a lot of <strong>client-side JavaScript code</strong> that harms <strong>performance</strong> and <strong>user experience</strong>, becomes a <strong>SPA</strong> without client-side JavaScript, since most interactions that require server data can be <strong>managed directly on the server</strong>. </p>
<p>Rendering on the server is very <strong>cheap</strong> and <strong>fast</strong> (~10ms)! </p>
<figure align="center">
<img src="https://aralroca.com/images/blog-images/cheap.gif" alt="Cheap and fast" class="center" />
  <figcaption><small>Cheap and fast</small></figcaption>
</figure>

<h2 id="control-stream-chunks-with-async-generators">Control stream chunks with Async Generators</h2>
<p>Brisa allows you to control the <strong>stream chunks</strong> with <strong>Async Generators</strong>. This way, you can <strong>stream</strong> the <strong>HTML</strong> in <strong>chunks</strong> and <strong>control</strong> the <strong>flow</strong> of the <strong>stream</strong>.</p>
<pre><code class="hljs language-tsx"><span class="hljs-keyword">import</span> { <span class="hljs-title class_">Database</span> } <span class="hljs-keyword">from</span> <span class="hljs-string">&quot;bun:sqlite&quot;</span>;
<span class="hljs-keyword">import</span> { renderComponent } <span class="hljs-keyword">from</span> <span class="hljs-string">&quot;brisa/server&quot;</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">LoadMovies</span>(<span class="hljs-params"></span>) {
  <span class="hljs-keyword">function</span> <span class="hljs-title function_">streamMovies</span>(<span class="hljs-params"></span>) {
    <span class="hljs-title function_">renderComponent</span>({
      <span class="hljs-attr">element</span>: <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">MovieItems</span> /&gt;</span></span>,
      <span class="hljs-attr">target</span>: <span class="hljs-string">&quot;ul&quot;</span>,
      <span class="hljs-attr">placement</span>: <span class="hljs-string">&quot;append&quot;</span>,
    });
  }

  <span class="hljs-keyword">return</span> (
    <span class="language-xml"><span class="hljs-tag">&lt;&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">id</span>=<span class="hljs-string">&quot;movies&quot;</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{streamMovies}</span>&gt;</span>
        Click here to stream movies from a Server Action
      <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">ul</span> /&gt;</span>
    <span class="hljs-tag">&lt;/&gt;</span></span>
  )
}

<span class="hljs-keyword">const</span> db = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Database</span>(<span class="hljs-string">&quot;db.sqlite&quot;</span>);

<span class="hljs-keyword">async</span> <span class="hljs-keyword">function</span>* <span class="hljs-title class_">MovieItems</span>() {
  <span class="hljs-keyword">for</span> (<span class="hljs-keyword">const</span> movie <span class="hljs-keyword">of</span> db.<span class="hljs-title function_">query</span>(<span class="hljs-string">&quot;SELECT title, year FROM movies&quot;</span>)) {
    <span class="hljs-title function_">yield</span> (
      <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>
        {movie.title} ({movie.year})
      <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span></span>
    );
  }
}
</code></pre><p>In this example, we are using an <a href="https://brisa.build/building-your-application/data-management/fetching#async-generators"><strong>Async Generator</strong></a> to <strong>stream</strong> the movies from a <strong>SQLite database</strong>. Any <code>yield</code> is a chunk of the stream that will be sent to the client.</p>
<ul>
<li><a href="https://github.com/brisa-build/brisa/tree/canary/examples/with-sqlite-with-server-action">🔗 Code example Streaming HTML from SQLite</a></li>
</ul>
<h2 id="what-is-rendercomponent">What is <code>renderComponent</code>?</h2>
<p>Brisa’s <code>renderComponent</code> allows developers to:</p>
<ol>
<li><strong>Re-render components dynamically on server actions.</strong></li>
<li><strong>Render specific components to specific locations</strong> in the DOM.</li>
<li><strong>Choose how and where to place them</strong> (e.g., replace, append, prepend, after &amp; before).</li>
<li><strong>Enhance transitions</strong> with the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Document/startViewTransition">View Transition API</a>.</li>
<li><strong>Stream JSX components</strong> incrementally from the server.</li>
</ol>
<p>Here’s a quick look:</p>
<pre><code class="hljs language-tsx"><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">MyComponent</span>(<span class="hljs-params">{ text = <span class="hljs-string">&quot;foo&quot;</span> }: { text: <span class="hljs-built_in">string</span> }</span>) {
  <span class="hljs-keyword">function</span> <span class="hljs-title function_">handleClick</span>(<span class="hljs-params"></span>) {
    <span class="hljs-comment">// Re-render the same component</span>
    <span class="hljs-title function_">renderComponent</span>();

    <span class="hljs-comment">// Re-render with new props</span>
    <span class="hljs-title function_">renderComponent</span>({ <span class="hljs-attr">element</span>: <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">MyComponent</span> <span class="hljs-attr">text</span>=<span class="hljs-string">&quot;bar&quot;</span> /&gt;</span></span> });

    <span class="hljs-comment">// Render another Component to a specific location</span>
    <span class="hljs-title function_">renderComponent</span>({
      <span class="hljs-attr">element</span>: <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">AnotherComponent</span> /&gt;</span></span>,
      <span class="hljs-attr">target</span>: <span class="hljs-string">&quot;#target-id&quot;</span>,
      <span class="hljs-attr">placement</span>: <span class="hljs-string">&quot;append&quot;</span>,
      <span class="hljs-attr">withTransition</span>: <span class="hljs-literal">true</span>, <span class="hljs-comment">// Enhance transitions with the View Transition API</span>
    });
  }

  <span class="hljs-keyword">return</span> (
    <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{handleClick}</span>&gt;</span>{text}<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
}
</code></pre><p>All this code is server-side code. In Brisa, all the events from server components are Server Actions.</p>
<h2 id="why-is-this-a-game-changer">Why is This a Game-Changer?</h2>
<p>Brisa’s approach to server actions is inspired by React’s model and HTMX concepts but is designed to be simpler and inherently safer.</p>
<h3 id="brisa-vs-htmx">Brisa vs. HTMX</h3>
<p><strong>HTMX</strong> allows developers to dynamically update portions of the DOM via server responses. But it lacks:</p>
<ul>
<li><strong>Component-level granularity:</strong> HTMX relies on server-generated HTML partials without the concept of components.</li>
<li><strong>Streaming support:</strong> HTMX does not natively support streaming updates to the client.</li>
<li><strong>Bundle size</strong>: HTMX is a 14KB library, while Brisa is only 2KB.</li>
</ul>
<p>With <code>renderComponent</code>, you get:</p>
<ul>
<li><strong>Component reuse:</strong> Brisa components are re-rendered seamlessly, leveraging JSX and React-like composition.</li>
<li><strong>Dynamic placement:</strong> Update or append components where in the DOM.</li>
<li><strong>Streaming support:</strong> Send and render data incrementally using server-side streams.</li>
</ul>
<h3 id="brisa-vs-react">Brisa vs. React</h3>
<p>In <strong>React</strong>, implementing server actions often involves using <code>&quot;use server&quot;</code> and <code>&quot;use client&quot;</code> directives. This dual model introduces the potential for human error and can unintentionally expose components to the client. </p>
<p>Key differentiators include:</p>
<ul>
<li><strong>Streaming HTML support:</strong> React communicates between the server and client by sending JavaScript, which can add significant overhead. Conversely, Brisa streams HTML directly to the client, reducing complexity and improving performance.</li>
<li><strong>Signals - fine-grained reactivity:</strong> Brisa’s client-side signals automatically react to server-side changes by updating Web Components, avoiding the need for a complete re-render.</li>
<li><strong>Bundle size:</strong> React-DOM v.19 weighs around 200KB, while Brisa maintains an ultra-lightweight footprint of just 2KB.</li>
<li><strong>Selective updates:</strong> Brisa allows you to update specific components on the server, reducing the need for full-page reloads.</li>
</ul>
<h2 id="conclusion">Conclusion</h2>
<p>Brisa’s HTML Streaming avoids the CDN trap and improves your app’s performance. You can stream HTML content directly to the client for the initial render, subsequent updates, and Server Actions. This approach improves security, and user experience, and simplifies state management, making it an ideal choice for server-side rendering.</p>
<p>If you’re looking to build fast, secure, and scalable web applications, give <a href="https://brisa.build">Brisa</a> a try today!</p>
<p><strong>Support Us:</strong> <a href="https://brisadotbuild.myspreadshop.es/">Visit our shop</a> for Brisa swag! 🛍️</p>
<div align="center">
<a href="https://brisadotbuild.myspreadshop.es/" alt="Brisa Shop" target="_blank">
<img width="400" height="425" src="https://brisa.build/images/blog-images/shop.webp" alt="Brisa Shop" />
</a>
</div>
]]></content:encoded>
            </item>
            <item>
              <title>Image classifier: in the browser</title>
              <description>Learn how to implement any kind of image recognition in the browser by implementing a cat/dog classifier in Tensorflow.js.</description>
              <link>https://aralroca.com/blog/cat-dog-classifier</link>
              <guid isPermaLink="false">https://aralroca.com/blog/cat-dog-classifier/</guid>
              <pubDate>Tue Jul 07 2020 00:00:00 GMT+0000 (Coordinated Universal Time)</pubDate>
              <content:encoded><![CDATA[<p>This is a small tutorial to implement an application that predicts if it&#39;s a cat or a dog image. To do this we&#39;ll use Tensorflow.js to make the prediction directly in the browser.</p>
<p>I recommend reading <a href="https://aralroca.com/blog/first-steps-with-tensorflowjs">this other article</a> where I introduce Tensorflow.js.</p>
<p>However, after this, you&#39;ll be able to classify any kind of image in an easy way even without any knowledge of ML. Also, it can be replicated for any image classification problem.</p>
<p><strong>We will cover the following:</strong></p>
<ul>
<li><a href="#the-dataset">The dataset</a></li>
<li><a href="#training-the-model">Training the model</a></li>
<li><a href="#testing-our-model">Testing our model</a></li>
<li><a href="#using-the-model-in-our-preact-app">Using the model in our (P)React app</a><ul>
<li><a href="#installing-dependencies">Installing dependencies</a></li>
<li><a href="#loading-the-model">Loading the model</a></li>
<li><a href="#using-the-model">Using the model</a></li>
</ul>
</li>
<li><a href="#why-in-the-browser">Why in the browser?</a></li>
<li><a href="#code-of-this-article">Code of this article</a></li>
<li><a href="#conclusion">Conclusion</a></li>
<li><a href="#references-and-acknowledgments">References and acknowledgments</a></li>
</ul>
<h2 id="the-dataset">The dataset</h2>
<p>Before we start training a model, we need to have many images of cats and dogs, as varied as possible, to not have any bias. We have two options:</p>
<ul>
<li>Recopilate our custom dataset</li>
<li>Use an existing dataset</li>
</ul>
<p>For this, I&#39;m going to use this dataset from Kaggle, with 10.000 images of cats/dogs:</p>
<ul>
<li><a href="https://www.kaggle.com/tongpython/cat-and-dog">https://www.kaggle.com/tongpython/cat-and-dog</a></li>
</ul>
<p>Thus, you only need to download it.</p>
<blockquote>
<p><strong>Note:</strong> On <a href="https://www.kaggle.com/datasets">Kaggle</a> you&#39;ll find a lot of available datasets, it&#39;s a good place to search for data. For our purposes, we&#39;ll choose a small dataset of 218MB. I recommend using one not too big at least for now, so you won&#39;t end up with your device&#39;s resources.</p>
</blockquote>
<h2 id="training-the-model">Training the model</h2>
<p>Once our dataset of images is ready, we can train the model.</p>
<p>The first thing we have to know is what kind of model we want. We&#39;ll train an <a href="https://www.tensorflow.org/tutorials/images/classification">Image Classification Model</a>, which after a given input image will say if it&#39;s a cat or dog.</p>
<p>There is a model called <a href="https://github.com/tensorflow/tfjs-examples/tree/master/mobilenet">Mobilenet</a>, already trained to classify <a href="https://github.com/tensorflow/tfjs-examples/blob/master/mobilenet/imagenet_classes.js">1000 different images</a>. The problem? It does not classify the images we want. To fix this we&#39;ll use a technique called <a href="https://en.wikipedia.org/wiki/Transfer_learning">transfer learning</a>, to use its &quot;intelligence&quot; to recognize our images.</p>
<p>Currently, we can transfer this knowledge without coding thanks to some open source tools. That&#39;s what we&#39;re going to do, we&#39;ll leave the code for the usage part of this model.</p>
<p>Let&#39;s use this tool:</p>
<ul>
<li><a href="https://thekevinscott.github.io/ml-classifier-ui/">https://thekevinscott.github.io/ml-classifier-ui/</a></li>
</ul>
<p>This tool uses a layer of the MobileNet neural network located at the end (<code>conv_pw_13_relu</code>). This means that it works well for images similar to the ones MobileNet has trained with (animals, instruments, everyday objects...). If you want to use more different images (for example skin freckles to detect a melanoma), it may not work unless you use an earlier layer. The closer the layer is to the end, the faster it&#39;ll be and the less resources will be used when training the model.</p>
<p>Now you need to drag and drop the <code>training_set</code> folder from the downloaded dataset and wait. That&#39;s all.</p>
<blockquote>
<p><strong>Note:</strong> Depending on your device GPU performance it can take a long time. If you have chosen a bigger dataset or another layer and your browser doesn&#39;t have enough resources, you can use the <a href="https://github.com/thekevinscott/ml-classifier">ml-classifier</a> in a Node.js environment.</p>
</blockquote>
<br />
<img src="https://aralroca.com/images/blog-images/training-cats-dogs-classifier.gif" alt="Training the model" class="center" />

<h2 id="testing-our-model">Testing our model</h2>
<p>Testing a model lets us know if it works with new images, not only the ones you have already trained. That&#39;s how we know that a model is working.</p>
<p>To test it, we&#39;ll use the <code>test_set</code> folder of the dataset. We can drag and drop it again. It contains different images from the ones we&#39;ve used in the training.</p>
<p>It will be much faster now than before.</p>
<br />
<img src="https://aralroca.com/images/blog-images/test-cats-dogs-classifier.gif" alt="Testing the model" class="center" />

<p>After checking that the trained model predicts quite well, we&#39;ll download it to use it in our app.</p>
<h2 id="using-the-model-in-our-preact-app">Using the model in our (P)React app</h2>
<p>We are going to create a Preact app with Snowpack by doing:</p>
<pre><code>npx create-snowpack-app cat-dog-detection-tfjs --template @snowpack/app-template-preact --use-yarn
</code></pre><p>Then, we&#39;ll add our model downloaded files (JSON + weights) inside <code>cat-dog-detection-tfjs/public</code>.</p>
<pre><code class="hljs language-diff">public
├── favicon.ico
├── index.html
<span class="hljs-addition">+├── model</span>
<span class="hljs-addition">+│   ├── ml-classifier-dogs-cats.json</span>
<span class="hljs-addition">+│   └── ml-classifier-dogs-cats.weights.bin</span>
└── robots.txt
</code></pre><blockquote>
<p><strong>Note:</strong> If you want to rename the model, don&#39;t forget to modify the <code>weightsManifest</code> inside the <code>.json</code> file to point correctly to the renamed <code>.weights.bin</code> file.</p>
</blockquote>
<h3 id="installing-dependencies">Installing dependencies</h3>
<p>To load the model we&#39;ll use <a href="https://www.tensorflow.org/js/tutorials/setup">Tensorflow.js</a>. Also, add <code>preact/hooks</code> to use hooks.</p>
<pre><code>yarn add @tensorflow/tfjs@1.0.0 preact/hooks
</code></pre><h3 id="loading-the-model">Loading the model</h3>
<p>To load our model, first we must load the Mobilenet model, as this is the model from which we have applied transfer learning. It&#39;s necessary for prediction. We will also load our model.</p>
<p>We&#39;re going to create two files:</p>
<ul>
<li>Hook to load the model</li>
<li>Our component to load the hook</li>
</ul>
<p>Hook to load the model (<code>src/hooks/useLoadCatsDogsModel.js</code>):</p>
<pre><code class="hljs language-jsx"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> tf <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;@tensorflow/tfjs&#x27;</span>
<span class="hljs-keyword">import</span> { useEffect, useState } <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;preact/hooks&#x27;</span>

<span class="hljs-keyword">const</span> pretrainedModel = {
  <span class="hljs-attr">url</span>:
    <span class="hljs-string">&#x27;https://storage.googleapis.com/tfjs-models/tfjs/mobilenet_v1_0.25_224/model.json&#x27;</span>,
  <span class="hljs-attr">layer</span>: <span class="hljs-string">&#x27;conv_pw_13_relu&#x27;</span>,
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">useLoadCatsDogsModel</span>(<span class="hljs-params"></span>) {
  <span class="hljs-keyword">const</span> [state, setState] = <span class="hljs-title function_">useState</span>([])

  <span class="hljs-title function_">useEffect</span>(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">async</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">loadModel</span>(<span class="hljs-params"></span>) {
      <span class="hljs-keyword">const</span> mobilenet = <span class="hljs-keyword">await</span> tf.<span class="hljs-title function_">loadLayersModel</span>(pretrainedModel.<span class="hljs-property">url</span>)
      <span class="hljs-keyword">const</span> layer = mobilenet.<span class="hljs-title function_">getLayer</span>(pretrainedModel.<span class="hljs-property">layer</span>)
      <span class="hljs-keyword">const</span> pretrained = <span class="hljs-keyword">await</span> tf.<span class="hljs-title function_">model</span>({
        <span class="hljs-attr">inputs</span>: mobilenet.<span class="hljs-property">inputs</span>,
        <span class="hljs-attr">outputs</span>: layer.<span class="hljs-property">output</span>,
      })

      <span class="hljs-keyword">const</span> model = <span class="hljs-keyword">await</span> tf.<span class="hljs-title function_">loadLayersModel</span>(
        <span class="hljs-string">&#x27;./model/ml-classifier-dogs-cats.json&#x27;</span>
      )

      <span class="hljs-title function_">setState</span>([model, pretrained])
    }
    <span class="hljs-title function_">loadModel</span>()
  }, [])

  <span class="hljs-keyword">return</span> state
}
</code></pre><p>Our component to load the hook (<code>src/CatsDogsDetection.jsx</code>):</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">import</span> { h } <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;preact&#x27;</span>
<span class="hljs-keyword">import</span> useLoadCatsDogsModel <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;./hooks/useLoadCatsDogsModel&#x27;</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">CatsDogsDetection</span>(<span class="hljs-params"></span>) {
  <span class="hljs-keyword">const</span> model = <span class="hljs-title function_">useLoadCatsDogsModel</span>()

  <span class="hljs-keyword">if</span> (!model) <span class="hljs-keyword">return</span> <span class="hljs-string">&#x27;Loading the model...&#x27;</span>

  <span class="hljs-keyword">return</span> <span class="hljs-string">&#x27;Model loaded!&#x27;</span>
}
</code></pre><p>In order to test if it loads correctly:</p>
<ul>
<li>Add the <code>&lt;CatsDogsDetection /&gt;</code> component inside your <code>src/App.jsx</code>.</li>
<li>Run <code>yarn start</code></li>
</ul>
<br />
<img src="https://aralroca.com/images/blog-images/model-loaded.gif" alt="Checking that the model is loaded correctly" class="center" />

<p>We already have the loaded model. Now we are going to replace the displayed text &quot;Model loaded!&quot; by using this model.</p>
<h3 id="using-the-model">Using the model</h3>
<p>In this tutorial, we are going to implement something not too complex by simply loading an image from the filesystem. It will display the prediction (cat or dog). We could complicate it by adding a camera, but this is not the purpose of the article.</p>
<p>What we&#39;re going to do to get the prediction is this:</p>
<img src="https://aralroca.com/images/blog-images/prediction-flow.png" alt="Flow to predict" class="center transparent" />

<p>In order to implement this, we&#39;re going to replace our <code>CatsDogsDetection</code> component to this:</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">import</span> { h } <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;preact&#x27;</span>
<span class="hljs-keyword">import</span> { useState } <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;preact/hooks&#x27;</span>
<span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> tf <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;@tensorflow/tfjs&#x27;</span>
<span class="hljs-keyword">import</span> useLoadCatsDogsModel <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;./hooks/useLoadCatsDogsModel&#x27;</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">CatsDogsDetection</span>(<span class="hljs-params"></span>) {
  <span class="hljs-keyword">const</span> [model, pretrainedModel] = <span class="hljs-title function_">useLoadCatsDogsModel</span>()
  <span class="hljs-keyword">const</span> [previewUrl, setPreviewUrl] = <span class="hljs-title function_">useState</span>()
  <span class="hljs-keyword">const</span> [predictionStatus, setPredictionStatus] = <span class="hljs-title function_">useState</span>()

  <span class="hljs-keyword">function</span> <span class="hljs-title function_">onLoadPreview</span>(<span class="hljs-params">e</span>) {
    <span class="hljs-keyword">const</span> image = e.<span class="hljs-property">target</span>.<span class="hljs-property">files</span>[<span class="hljs-number">0</span>]
    <span class="hljs-keyword">if</span> (!image) <span class="hljs-keyword">return</span>
    <span class="hljs-keyword">if</span> (previewUrl) <span class="hljs-variable constant_">URL</span>.<span class="hljs-title function_">revokeObjectURL</span>(previewUrl)
    <span class="hljs-title function_">setPreviewUrl</span>(<span class="hljs-variable constant_">URL</span>.<span class="hljs-title function_">createObjectURL</span>(image))
    <span class="hljs-title function_">setPredictionStatus</span>(<span class="hljs-string">&#x27;predicting&#x27;</span>)
  }

  <span class="hljs-keyword">async</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">predict</span>(<span class="hljs-params"></span>) {
    <span class="hljs-keyword">const</span> pixels = tf.<span class="hljs-property">browser</span>.<span class="hljs-title function_">fromPixels</span>(<span class="hljs-variable language_">document</span>.<span class="hljs-title function_">querySelector</span>(<span class="hljs-string">&#x27;img&#x27;</span>))
    <span class="hljs-keyword">const</span> image = tf
      .<span class="hljs-title function_">reshape</span>(pixels, [<span class="hljs-number">1</span>, <span class="hljs-number">224</span>, <span class="hljs-number">224</span>, <span class="hljs-number">3</span>])
      .<span class="hljs-title function_">toFloat</span>()
      .<span class="hljs-title function_">div</span>(tf.<span class="hljs-title function_">scalar</span>(<span class="hljs-number">127</span>))
      .<span class="hljs-title function_">sub</span>(tf.<span class="hljs-title function_">scalar</span>(<span class="hljs-number">1</span>))
    <span class="hljs-keyword">const</span> modelPrediction = model.<span class="hljs-title function_">predict</span>(pretrainedModel.<span class="hljs-title function_">predict</span>(image))
    <span class="hljs-keyword">const</span> [dog, cat] = <span class="hljs-title class_">Array</span>.<span class="hljs-title function_">from</span>(modelPrediction.<span class="hljs-title function_">dataSync</span>())
    <span class="hljs-title function_">setPredictionStatus</span>(dog &gt;= cat ? <span class="hljs-string">&#x27;🐶&#x27;</span> : <span class="hljs-string">&#x27;😸&#x27;</span>)
  }

  <span class="hljs-keyword">if</span> (!model) <span class="hljs-keyword">return</span> <span class="hljs-string">&#x27;Loading the model...&#x27;</span>

  <span class="hljs-keyword">return</span> (
    <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Choose a dog or cat image<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">&quot;file&quot;</span> <span class="hljs-attr">onChange</span>=<span class="hljs-string">{onLoadPreview}</span> <span class="hljs-attr">accept</span>=<span class="hljs-string">&quot;image/*&quot;</span> /&gt;</span>
      {previewUrl &amp;&amp; (
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">style</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">marginTop:</span> <span class="hljs-attr">10</span> }}&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">img</span>
            <span class="hljs-attr">src</span>=<span class="hljs-string">{previewUrl}</span>
            <span class="hljs-attr">onLoad</span>=<span class="hljs-string">{predict}</span>
            <span class="hljs-attr">width</span>=<span class="hljs-string">{224}</span>
            <span class="hljs-attr">height</span>=<span class="hljs-string">{224}</span>
            <span class="hljs-attr">alt</span>=<span class="hljs-string">&quot;preview&quot;</span>
          /&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      )}
      {predictionStatus === &#x27;predicting&#x27; ? (
        &#x27;Predicting...&#x27;
      ) : (
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">style</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">fontSize:</span> <span class="hljs-attr">50</span> }}&gt;</span>{predictionStatus}<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      )}
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  )
}
</code></pre><p>What it does:</p>
<ol>
<li>Using the input file, we show in the <code>&lt;img&gt;</code> element the image preview with 224x224px resolution (important to keep it).</li>
<li>Once the image is loaded (onLoad event) we can start predicting.</li>
</ol>
<p>And the result:</p>
<img src="https://aralroca.com/images/blog-images/cats-dogs-result.gif" class="center" alt="result" />

<h2 id="why-in-the-browser">Why in the browser?</h2>
<p>You&#39;ve probably wondered at some point why are we doing it with JavaScript, rather than Python or something else.</p>
<img src="https://aralroca.com/images/blog-images/why-in-the-browser.png" alt="Why in the browser" class="center transparent" />

<p>Here are several reasons:</p>
<ul>
<li><strong>Faster predictions</strong>: It&#39;s not necessary to make a request to any server from our application, so we save the time it takes for the request.</li>
<li><strong>Working offline</strong>: As in the previous point, we can make predictions with our device (mobile, tablet, desktop...) even without the Internet.</li>
<li><strong>Cost zero in money</strong>: We just need to put our app on a CDN. If 2000 people are using the application at the same time to make predictions, we won&#39;t saturate any server as there is no need even to have a server. Each user will make the predictions directly from their device.</li>
<li><strong>Open-source models</strong>: Instead of hiding the models behind a server by using them with JavaScript, we are publishing them in such a way that any developer who likes the application can use the same models for their project.</li>
<li><strong>Privacy</strong>: The data is not stored in any external database nor travels on the net, it stays on the device.</li>
</ul>
<h2 id="code-of-this-article">Code of this article</h2>
<p>The code of this article can be found in my GitHub:</p>
<ul>
<li><a href="https://github.com/aralroca/cat-dog-detection-tfjs">https://github.com/aralroca/cat-dog-detection-tfjs</a></li>
</ul>
<p>And the demo link:</p>
<ul>
<li><a href="https://cat-dog-detection-tfjs.vercel.app/">https://cat-dog-detection-tfjs.vercel.app/</a></li>
</ul>
<h2 id="conclusion">Conclusion</h2>
<p>We&#39;ve seen how to solve any kind of image classification problem with a few steps. As an example, we have implemented a cat/dog classifier. The same example can be replicated for any type of image classification:</p>
<ul>
<li>Skin cancer detection</li>
<li>Rock-paper-scissors game</li>
<li>etc</li>
</ul>
<h2 id="references-and-acknowledgments">References and acknowledgments</h2>
<p>I want to thank <a href="https://github.com/thekevinscott">Kevin Scott</a> (author of ml-classifier tool) for this article. He helped me understand and solve some problems.</p>
<ul>
<li><a href="https://github.com/thekevinscott/ml-classifier-ui">https://github.com/thekevinscott/ml-classifier-ui</a></li>
<li><a href="https://thekevinscott.com/image-classification-with-javascript/">https://thekevinscott.com/image-classification-with-javascript/</a></li>
<li><a href="https://www.tensorflow.org/js/tutorials">https://www.tensorflow.org/js/tutorials</a></li>
</ul>
]]></content:encoded>
            </item>
            <item>
              <title>Creating Scalable and Reusable React Components</title>
              <description>This post advocates for the creation of versatile and elegant React components from the start to avoid issues with single-use components</description>
              <link>https://aralroca.com/blog/creating-scalable-and-reusable-react-components</link>
              <guid isPermaLink="false">https://aralroca.com/blog/creating-scalable-and-reusable-react-components/</guid>
              <pubDate>Sun Apr 30 2023 00:00:00 GMT+0000 (Coordinated Universal Time)</pubDate>
              <content:encoded><![CDATA[<h2 id="tldr">TLDR;</h2>
<p>In this article, I will discuss a <strong>common issue</strong> that I have encountered in React applications: the creation of <strong>single-use components</strong> that lack the versatility and elegance of a truly reusable component. Rather than designing components for specific use cases, it is more beneficial to create generic components that can be adapted to various contexts.</p>
<h2 id="problem-simple-use-and-overly-contextualized-components">Problem: Simple-use and overly contextualized components</h2>
<p>Some developers argue that it is acceptable to <strong>initially</strong> create a <strong>single-use</strong> component and <strong>refactor</strong> it <strong>later</strong> if a similar component is needed elsewhere. While this approach may avoid violating the YAGNI principle, it is not ideal.</p>
<blockquote>
<p>The YAGNI (You Aren&#39;t Gonna Need It) principle is a software development practice that focuses on avoiding the implementation of unnecessary features. Instead of anticipating all possible future requirements and writing code for them, developers should write code only to meet the current project&#39;s needs.</p>
</blockquote>
<p>...Are we doing the right thing here?</p>
<p>Consider an <strong>example</strong> where we are creating a <strong><code>ProductCardCarousel</code></strong> component because we will <strong>only use</strong> it momentarily for the <strong>product page</strong>.</p>
<p>We have created <strong>first</strong> the <strong>product page</strong> and placed a significant amount of logic there, and subsequently, we have <strong>transferred</strong> some of this <strong>logic into a component</strong> named <code>ProductCardCarousel</code> because it is responsible for displaying the products in a carousel.</p>
<p>First we implemented the <strong>product page</strong>:</p>
<pre><code class="hljs language-jsx"><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">ProductPage</span>(<span class="hljs-params">{ products }</span>) {
  <span class="hljs-keyword">return</span> (
    <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
      {/* ...more code here... */}
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">&quot;product-carousel&quot;</span>&gt;</span>
        {products.map((product) =&gt; (
          <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">&quot;card&quot;</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{product.id}</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{product.image}</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">&quot;Product&quot;</span> /&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h4</span>&gt;</span>{product.name}<span class="hljs-tag">&lt;/<span class="hljs-name">h4</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{product.description}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        ))}
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  )
}
</code></pre><p>Then transferred some logic into <strong><code>ProductCardCarousel</code></strong>:</p>
<pre><code class="hljs language-jsx"><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">ProductPage</span>(<span class="hljs-params">{ products }</span>) {
  <span class="hljs-keyword">return</span> (
    <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
      {/* ...more code here... */}
      <span class="hljs-tag">&lt;<span class="hljs-name">ProductCardCarousel</span> <span class="hljs-attr">products</span>=<span class="hljs-string">{products}</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  )
}

<span class="hljs-comment">// product-card-carousel.js</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">ProductCardCarousel</span>(<span class="hljs-params">{ products }</span>) {
  <span class="hljs-keyword">return</span> (
    <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">&quot;product-carousel&quot;</span>&gt;</span>
      {products.map((product) =&gt; (
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">&quot;card&quot;</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{product.id}</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{product.image}</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">&quot;Product&quot;</span> /&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">h4</span>&gt;</span>{product.name}<span class="hljs-tag">&lt;/<span class="hljs-name">h4</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{product.description}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      ))}
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  )
}
</code></pre><p><em>Does that make sense?</em> 🤔 Well, we are <strong>approaching this task inaccurately</strong>.</p>
<p>Creating <strong>single-use components</strong> is a <strong>bad practice</strong> because it can lead to <strong>inefficient</strong> and <strong>duplicative code</strong>. These components <strong>lack versatility and elegance</strong> and are designed for <strong>specific use cases</strong>, making it <strong>challenging to adapt</strong> them to different contexts. Placing a <strong>significant amount of logic</strong> in a specific part of the codebase can result in <strong>code duplication</strong>, as developers may need to create similar components for other use cases. <strong>Refactoring</strong> single-use components into reusable ones can also be <strong>challenging</strong>.</p>
<h3 id="shift-in-focus">Shift in focus</h3>
<p>The <strong>best practice</strong> is to design <strong>versatile and elegant components</strong> from the outset to <strong>avoid these issues</strong>.</p>
<p>What I propose is a <strong>shift</strong> in <strong>focus</strong> when implementing React components <strong>from the start</strong>. This change not only adheres to the YAGNI principle but also makes your project <strong>more scalable</strong>, allowing for <strong>more reusable</strong> components and <strong>reducing the need for future refactoring</strong>.</p>
<p>The key is to ensure that components are as &quot;dumb&quot; as possible concerning business logic. Their sole responsibility should be to serve as UI components that can receive any context required to fulfill their UI functionality.</p>
<p>When component names start to include business logic or other UI behaviors rather than just one UI element, it is a sign that the component may be <strong>overly contextualized</strong>. In the last <code>ProductCardCarousel</code> example, should not contain product-specific logic neither card-specific logic.</p>
<figure align="center">
  <img src="https://aralroca.com/images/blog-images/lego.jpg" alt="Lego pieces" class="center" />
  <figcaption><small>React Components as Lego pieces</small></figcaption>
</figure>

<p>Developing a <strong>web page</strong> using React <strong>without</strong> first defining its <strong>components</strong> is akin to attempting to construct a <strong>Lego house</strong> <strong>without</strong> the requisite <strong>Lego pieces</strong>.</p>
<h2 id="solution-components-as-lego-blocks">Solution: Components as Lego Blocks</h2>
<p>To address this issue, it is helpful to approach the implementation of new React features by <strong>starting</strong> with the <strong>lower-level components</strong> and working your <strong>way up</strong> to the parent component, which will handle the business logic. The parent component can then use the created UI components (or existing ones) and feed them the necessary context without burdening them with the responsibility of managing business logic.</p>
<p>A <strong>common mistake</strong> is to begin at the <strong>top level</strong>. This approach often leads to the creation of a component containing all the logic, which is later broken down into smaller components. This only serves to pass the responsibility of managing business logic down through the component hierarchy. For example:</p>
<ul>
<li><strong>1</strong>: <code>ProductSection</code><ul>
<li><strong>2</strong>: <code>ProductSubsection</code><ul>
<li><strong>3</strong>: <code>ProductCardCarousel</code></li>
<li><strong>4</strong>: <code>ProductCardList</code></li>
</ul>
</li>
</ul>
</li>
</ul>
<p>This method does not make sense. <em>It is reminiscent of working with a single HTML file 20 years ago—the only difference is that the code is now distributed across multiple components</em>.</p>
<p>The true power of components lies in their reusability and separation from business logic.</p>
<figure align="center">
  <img src="https://aralroca.com/images/blog-images/lego-component.jpg" alt="React Components as Lego Blocks: A Method for Scalable Development" class="center" />
  <figcaption><small>React Components as Lego Blocks: A Method for Scalable Development</small></figcaption>
</figure>

<p>In order to fix the example above, development should begin with the <code>Card</code> component, which should be entirely agnostic to the business logic. Although the <code>Card</code> is required for displaying products, it should be adaptable to any context (e.g., product, user, etc.). Once the <code>Card</code> component is complete, developers can create both a <code>List</code> and a <code>Carousel</code> component to display cards. These components (<code>List</code> and <code>Carousel</code>) should <strong>also</strong> be <strong>context-agnostic</strong>, accepting any content such as <code>Card</code>, images, paragraphs, etc.</p>
<p>Finally, at the highest level, all these &quot;Lego pieces&quot; can be assembled to construct the <code>ProductSection</code>. By keeping the <strong>business logic</strong> at the <strong>top level</strong>, the <code>ProductSection</code> can <strong>combine these context-agnostic UI components</strong> to satisfy the requirements of the specific business model.</p>
<ul>
<li><strong>4</strong>: <code>ProductSection</code><ul>
<li><strong>2</strong>: <code>Carousel</code><ul>
<li><strong>1</strong>: <code>Card</code></li>
</ul>
</li>
<li><strong>3</strong>: <code>List</code><ul>
<li><strong>1</strong>: <code>Card</code></li>
</ul>
</li>
</ul>
</li>
</ul>
<p>Simple example in code:</p>
<pre><code class="hljs language-jsx"><span class="hljs-comment">// card.js</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">Card</span>(<span class="hljs-params">{ image, alt, name, description }</span>) {
  <span class="hljs-keyword">return</span> (
    <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">&quot;card&quot;</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{image}</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">{alt}</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h4</span>&gt;</span>{name}<span class="hljs-tag">&lt;/<span class="hljs-name">h4</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{description}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  )
}

<span class="hljs-comment">// carousel.js</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">Carousel</span>(<span class="hljs-params">{ children }</span>) {
  <span class="hljs-keyword">return</span> <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">&quot;carousel&quot;</span>&gt;</span>{children}<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
}

<span class="hljs-comment">// list.js</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">List</span>(<span class="hljs-params">{ children }</span>) {
  <span class="hljs-keyword">return</span> <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">&quot;list&quot;</span>&gt;</span>{children}<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
}

<span class="hljs-comment">// product-section.js</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">ProductSection</span>(<span class="hljs-params">{ products }</span>) {
  <span class="hljs-keyword">const</span> productCards = products.<span class="hljs-title function_">map</span>(<span class="hljs-function">(<span class="hljs-params">product</span>) =&gt;</span> (
    <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">Card</span>
      <span class="hljs-attr">key</span>=<span class="hljs-string">{product.id}</span>
      <span class="hljs-attr">alt</span>=<span class="hljs-string">&quot;Product&quot;</span>
      <span class="hljs-attr">image</span>=<span class="hljs-string">{product.image}</span>
      <span class="hljs-attr">name</span>=<span class="hljs-string">{product.name}</span>
      <span class="hljs-attr">description</span>=<span class="hljs-string">{product.description}</span>
    /&gt;</span></span>
  ))

  <span class="hljs-keyword">return</span> (
    <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">&quot;product-section&quot;</span>&gt;</span>
      {/* ...more code here... */}
      <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>Products carousel<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Carousel</span>&gt;</span>{productCards}<span class="hljs-tag">&lt;/<span class="hljs-name">Carousel</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>Products list<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">List</span>&gt;</span>{productCards}<span class="hljs-tag">&lt;/<span class="hljs-name">List</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  )
}
</code></pre><p>Once you have established your components, you can <strong>create</strong> not only the required web page but also <strong>other pages</strong> with entirely <strong>different business logic</strong>, leveraging the <strong>same building blocks</strong>.</p>
<p>The advantage of using reusable components is that they can be <strong>adapted</strong> to various <strong>contexts</strong>, providing a more efficient and effective development process. By designing generic components that can be utilized in different scenarios, developers can <strong>avoid duplicative</strong> and <strong>inefficient code</strong>, resulting in a <strong>more robust</strong> and <strong>scalable</strong> application.</p>
<h2 id="conclusion">Conclusion</h2>
<p>In conclusion, when developing in React, it is crucial to avoid creating components that lack real utility or usefulness and are designed for single-use. Instead, developers should focus on creating generic components that can be used in many different contexts.</p>
<p>When implementing a new React feature, it is best to start with the lowest-level components and work up to the parent, which will have the business logic and can use the UI components that have been created to display the UI. This approach ensures that components are much more reusable and scalable and avoids violating the YAGNI principle.</p>
<p>Finally, developers should shift their focus from creating the simplest possible components to the business model. The only responsibility of a UI component should be to display the UI and be fed with any context necessary to fulfill its UI functionality. By following these guidelines, developers can create more efficient and maintainable React applications.</p>
<figure align="center">
  <img src="https://aralroca.com/images/blog-images/sharing-lego-components.jpg" alt="Generic components as Lego blocks that can be used in many different contexts" class="center" />
  <figcaption><small>Generic components as Lego blocks that can be used in many different contexts</small></figcaption>
</figure>
]]></content:encoded>
            </item>
            <item>
              <title>👋 Say Goodbye to Spread Operator: Use Default Composer</title>
              <description>"Default-composer" is a tiny library that simplifies setting default values for nested objects</description>
              <link>https://aralroca.com/blog/default-composer</link>
              <guid isPermaLink="false">https://aralroca.com/blog/default-composer/</guid>
              <pubDate>Mon Jun 05 2023 00:00:00 GMT+0000 (Coordinated Universal Time)</pubDate>
              <content:encoded><![CDATA[<p>When working with objects in JavaScript, it is common to need to set default values for empty <code>strings</code>/<code>objects</code>/<code>arrays</code>, <code>null</code>, or <code>undefined</code> properties. When dealing with nested objects, this can become even more complicated and require complex programming logic. However, with the &quot;<strong><a href="https://github.com/aralroca/default-composer">default-composer</a></strong>&quot; library, this task becomes simple and easy.</p>
<h2 id="what-is-default-composer">What is &quot;default-composer&quot;?</h2>
<p>&quot;<a href="https://github.com/aralroca/default-composer">default-composer</a>&quot; is a lightweight (~300B) JavaScript library that allows you to set default values for nested objects. The library replaces empty strings/arrays/objects, null, or undefined values in an existing object with the defined default values, which helps simplify programming logic and reduce the amount of code needed to set default values.</p>
<a href="https://github.com/aralroca/default-composer" target="_blank">
  <figure align="center">
    <img class="center" width="200" height="200" src="https://aralroca.com/images/blog-images/default-composer.svg" alt="Default Composer logo" />
    <figcaption><small>Default Composer logo</small></figcaption>
  </figure>
</a>

<h2 id="benefits-over-spread-operator-and-objectassign">Benefits over Spread Operator and Object.assign</h2>
<p>While <a href="https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Operators/Spread_syntax"><code>...spread</code> operator</a> and <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign"><code>Object.assign()</code></a> can also be used to set default values for objects, &quot;<strong><a href="https://github.com/aralroca/default-composer"><code>default-composer</code></a></strong>&quot; provides several benefits over these methods.</p>
<ul>
<li>Works with <strong>nested objects</strong>, whereas the spread operator and <code>Object.assign()</code> only work with shallow objects.</li>
<li>More concise and <strong>easier to read</strong> than spread operator or <code>Object.assign()</code>. The code required to set default values with these methods can become very verbose and difficult to read, especially when dealing with nested objects.</li>
<li>More granular <strong>control</strong> over which <strong>properties</strong> should be <strong>set to default values</strong>. With spread operator and <code>Object.assign()</code>.</li>
</ul>
<p>Imagine we have this original object:</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">const</span> original = {
  <span class="hljs-attr">name</span>: <span class="hljs-string">&#x27;&#x27;</span>,
  <span class="hljs-attr">score</span>: <span class="hljs-literal">null</span>,
  <span class="hljs-attr">address</span>: {
    <span class="hljs-attr">street</span>: <span class="hljs-string">&#x27;&#x27;</span>,
    <span class="hljs-attr">city</span>: <span class="hljs-string">&#x27;&#x27;</span>,
    <span class="hljs-attr">state</span>: <span class="hljs-string">&#x27;&#x27;</span>,
    <span class="hljs-attr">zip</span>: <span class="hljs-string">&#x27;&#x27;</span>,
  },
  <span class="hljs-attr">emails</span>: [],
  <span class="hljs-attr">hobbies</span>: [],
  <span class="hljs-attr">another</span>: <span class="hljs-string">&#x27;anotherValue&#x27;</span>,
}
</code></pre><p>And these are the defaults:</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">const</span> defaults = {
  <span class="hljs-attr">name</span>: <span class="hljs-string">&#x27;John Doe&#x27;</span>,
  <span class="hljs-attr">score</span>: <span class="hljs-number">5</span>,
  <span class="hljs-attr">address</span>: {
    <span class="hljs-attr">street</span>: <span class="hljs-string">&#x27;123 Main St&#x27;</span>,
    <span class="hljs-attr">city</span>: <span class="hljs-string">&#x27;Anytown&#x27;</span>,
    <span class="hljs-attr">state</span>: <span class="hljs-string">&#x27;CA&#x27;</span>,
    <span class="hljs-attr">zip</span>: <span class="hljs-string">&#x27;12345&#x27;</span>,
  },
  <span class="hljs-attr">emails</span>: [<span class="hljs-string">&#x27;john.doe@example.com&#x27;</span>],
  <span class="hljs-attr">hobbies</span>: [<span class="hljs-string">&#x27;reading&#x27;</span>, <span class="hljs-string">&#x27;traveling&#x27;</span>],
}
</code></pre><p>We want to merge these objects replacing the original values that are <code>&quot;&quot;</code>, <code>null</code>, <code>[]</code>, <code>undefined</code> and <code>{}</code> to the default value. So the idea is to get:</p>
<pre><code class="hljs language-js"><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(results)
<span class="hljs-comment">/**
 * {
 * &quot;name&quot;: &quot;John Doe&quot;,
 * &quot;score&quot;: 5,
 * &quot;address&quot;: {
 *   &quot;street&quot;: &quot;123 Main St&quot;,
 *   &quot;city&quot;: &quot;Anytown&quot;,
 *   &quot;state&quot;: &quot;CA&quot;,
 *   &quot;zip&quot;: &quot;12345&quot;
 * },
 * &quot;emails&quot;: [
 *   &quot;john.doe<span class="hljs-doctag">@example</span>.com&quot;
 * ],
 * &quot;hobbies&quot;: [
 *   &quot;reading&quot;,
 *   &quot;traveling&quot;
 * ],
 * &quot;another&quot;: &quot;anotherValue&quot;
 **/</span>
</code></pre><p>Probably with spread operator we will have to do something like that:</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">const</span> results = {
  ...defaults,
  ...original,
  <span class="hljs-attr">name</span>: original.<span class="hljs-property">name</span> || defaults.<span class="hljs-property">name</span>,
  <span class="hljs-attr">score</span>: original.<span class="hljs-property">score</span> ?? defaults.<span class="hljs-property">score</span>, <span class="hljs-comment">// &quot;??&quot; beacause 0 is valid</span>
  <span class="hljs-attr">address</span>: {
    ...defaults.<span class="hljs-property">address</span>,
    ...original.<span class="hljs-property">address</span>,
    <span class="hljs-attr">street</span>: original.<span class="hljs-property">address</span>.<span class="hljs-property">street</span> || defaults.<span class="hljs-property">address</span>.<span class="hljs-property">street</span>,
    <span class="hljs-attr">city</span>: original.<span class="hljs-property">address</span>.<span class="hljs-property">city</span> || defaults.<span class="hljs-property">address</span>.<span class="hljs-property">city</span>,
    <span class="hljs-attr">state</span>: original.<span class="hljs-property">address</span>.<span class="hljs-property">state</span> || defaults.<span class="hljs-property">address</span>.<span class="hljs-property">state</span>,
    <span class="hljs-attr">zip</span>: original.<span class="hljs-property">address</span>.<span class="hljs-property">zip</span> || defaults.<span class="hljs-property">address</span>.<span class="hljs-property">zip</span>,
  },
  <span class="hljs-attr">emails</span>: original.<span class="hljs-property">emails</span>.<span class="hljs-property">length</span> ? original.<span class="hljs-property">emails</span> : defaults.<span class="hljs-property">emails</span>,
  <span class="hljs-attr">hobbies</span>: original.<span class="hljs-property">hobbies</span>.<span class="hljs-property">length</span> ? original.<span class="hljs-property">hobbies</span> : defaults.<span class="hljs-property">hobbies</span>,
}
</code></pre><p>and with <code>Object.assign</code> something like this:</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">const</span> results = <span class="hljs-title class_">Object</span>.<span class="hljs-title function_">assign</span>({}, defaults, original, {
  <span class="hljs-attr">name</span>: original.<span class="hljs-property">name</span> || defaults.<span class="hljs-property">name</span>,
  <span class="hljs-attr">score</span>: original.<span class="hljs-property">score</span> ?? defaults.<span class="hljs-property">score</span>, <span class="hljs-comment">// &quot;??&quot; beacause 0 is valid</span>
  <span class="hljs-attr">address</span>: <span class="hljs-title class_">Object</span>.<span class="hljs-title function_">assign</span>({}, defaults.<span class="hljs-property">address</span>, original.<span class="hljs-property">address</span>, {
    <span class="hljs-attr">street</span>: original.<span class="hljs-property">address</span>.<span class="hljs-property">street</span> || defaults.<span class="hljs-property">address</span>.<span class="hljs-property">street</span>,
    <span class="hljs-attr">city</span>: original.<span class="hljs-property">address</span>.<span class="hljs-property">city</span> || defaults.<span class="hljs-property">address</span>.<span class="hljs-property">city</span>,
    <span class="hljs-attr">state</span>: original.<span class="hljs-property">address</span>.<span class="hljs-property">state</span> || defaults.<span class="hljs-property">address</span>.<span class="hljs-property">state</span>,
    <span class="hljs-attr">zip</span>: original.<span class="hljs-property">address</span>.<span class="hljs-property">zip</span> || defaults.<span class="hljs-property">address</span>.<span class="hljs-property">zip</span>,
  }),
  <span class="hljs-attr">emails</span>: original.<span class="hljs-property">emails</span>.<span class="hljs-property">length</span> ? original.<span class="hljs-property">emails</span> : defaults.<span class="hljs-property">emails</span>,
  <span class="hljs-attr">hobbies</span>: original.<span class="hljs-property">hobbies</span>.<span class="hljs-property">length</span> ? original.<span class="hljs-property">hobbies</span> : defaults.<span class="hljs-property">hobbies</span>,
})
</code></pre><p>Maintaining this can be very tidious, especially with huge, heavily nested objects.</p>
<figure align="center">
  <img class="center" width="500" height="333" src="https://aralroca.com/images/blog-images/headache.jpg" alt="Headache" />
  <figcaption><small>Headache...</small></figcaption>
</figure>

<p>With <code>defaultComposer</code> we could only use this:</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">import</span> defaultComposer <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;default-composer&#x27;</span> <span class="hljs-comment">// 300B</span>
<span class="hljs-comment">// ...</span>
<span class="hljs-keyword">const</span> results = <span class="hljs-title function_">defaultComposer</span>(defaults, original)
</code></pre><p>Easier to maintain, right? 😉</p>
<figure align="center">
  <img class="center" width="500" height="347" src="https://aralroca.com/images/blog-images/learning-js.jpg" alt="Easier" />
  <figcaption><small>Happier an easier</small></figcaption>
</figure>

<p>What happens if in our project there is a special property that works differently from the others and we want another replacement logic? Well, although <code>defaultComposer</code> has by default a configuration to detect the defautable values, you can configure it as you like.</p>
<pre><code class="hljs language-ts"><span class="hljs-keyword">import</span> { defaultComposer, setConfig } <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;default-composer&#x27;</span>

<span class="hljs-title function_">setConfig</span>({
  <span class="hljs-comment">// This function is executed for each value of each key that exists in</span>
  <span class="hljs-comment">// both the original object and the defaults object.</span>
  <span class="hljs-attr">isDefaultableValue</span>: <span class="hljs-function">(<span class="hljs-params">
    <span class="hljs-comment">// - key: key of original or default object</span>
    <span class="hljs-comment">// - value: value in the original object</span>
    <span class="hljs-comment">// - defaultableValue: pre-calculed boolean, you can use or not,</span>
    <span class="hljs-comment">//   depending if all the rules of the default-composer library are correct</span>
    <span class="hljs-comment">//   for your project or you need a totally different ones.</span>
    { key, value, defaultableValue }
  </span>) =&gt;</span> {
    <span class="hljs-keyword">if</span> (key === <span class="hljs-string">&#x27;rare-key&#x27;</span>) {
      <span class="hljs-keyword">return</span> defaultableValue || value === <span class="hljs-string">&#x27;EMPTY&#x27;</span>
    }

    <span class="hljs-keyword">return</span> defaultableValue
  },
})
</code></pre><h2 id="conclusions">Conclusions</h2>
<p>I&#39;ve introduced the &quot;<a href="https://github.com/aralroca/default-composer">default-composer</a>&quot; library as a solution for setting <strong>default values</strong> for <strong>nested objects</strong> in JavaScript.</p>
<p>The library is lightweight and provides more concise and easier-to-read code than the spread operator and <code>Object.assign</code> methods. It also offers more granular control over which properties should be set to default values.</p>
<p>In this article I provide examples of how to use the library and how it simplifies the code for maintaining nested objects.</p>
<p>Finally, I explain how the library <strong>can be configured</strong> to handle <strong>special cases</strong> where a different replacement logic is required. Overall, &quot;<a href="https://github.com/aralroca/default-composer">default-composer</a>&quot; is a useful library for simplifying the task of setting default values for nested objects in JavaScript.</p>
]]></content:encoded>
            </item>
            <item>
              <title>Detect text toxicity using React</title>
              <description>Discover how to detect threatening language, insults, obscenities, identity-based hate or sexually explicit language on texts using React.</description>
              <link>https://aralroca.com/blog/detect-text-toxicity-with-react</link>
              <guid isPermaLink="false">https://aralroca.com/blog/detect-text-toxicity-with-react/</guid>
              <pubDate>Tue Apr 28 2020 00:00:00 GMT+0000 (Coordinated Universal Time)</pubDate>
              <content:encoded><![CDATA[<p>In a previous article I explained <strong>how to start with Tensorflow.js</strong>, from creating a simple linear regression model to using pretrained models as posenet. I <strong>highly recommend</strong> to read <a href="https://aralroca.com/blog/first-steps-with-tensorflowjs">this article</a> to understand how it works.</p>
<p>In today&#39;s article I want to show you how easy it&#39;s to use the text toxicity detection model without any previous knowledge of machine learning.</p>
<h2 id="what-is-text-toxicity-detection">What is text toxicity detection?</h2>
<p>Toxicity detection detects text containing toxic content such as threatening language, insults, obscenities, identity-based hate, or sexually explicit language.</p>
<img class="center" alt="text toxicity example" src="https://aralroca.com/images/blog-images/27.gif" />
<small class="center">Fig 1: Text tocicity example</small>

<p>With this in your browser, it&#39;ll be esier to prevent unwanted comments/opinions and speed up the review process of these content.</p>
<p>However, this looks so complicated... Nah, good news for you! You don&#39;t need to be a machine-learning expert to use this model. Let&#39;s see how.</p>
<h2 id="the-hook">The hook</h2>
<p>I wrote a React hook to simplify the way to use it, so you can get the predictions of text just by using a hook in one line of code:</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">import</span> useTextToxicity <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;react-text-toxicity&#x27;</span>

<span class="hljs-comment">// Usage inside your component or custom hook</span>
<span class="hljs-keyword">const</span> predictions = <span class="hljs-title function_">useTextToxicity</span>(<span class="hljs-string">&#x27;This is an example&#x27;</span>)
<span class="hljs-comment">/*
  {
    &quot;label&quot;: &quot;identity_attack&quot;,
    &quot;match&quot;: false,
    &quot;probability&quot;: &quot;3.40%&quot;,
    &quot;probabilities&quot;: [0.9659664034843445, 0.03403361141681671],
  },
  {
    &quot;label&quot;: &quot;insult&quot;,
    &quot;match&quot;: true,
    &quot;probability&quot;: &quot;91.8%&quot;,
    &quot;probabilities&quot;: [0.08124706149101257, 0.9187529683113098],
  },
  ...
*/</span>
</code></pre><p>I uploaded the npm package so you can use it by doing:</p>
<pre><code>yarn add react-text-toxicity
</code></pre><p>And the GitHub repo 👉 <a href="https://github.com/aralroca/react-text-toxicity">https://github.com/aralroca/react-text-toxicity</a></p>
<p>We can connect the <code>useTextToxicity</code> hook to a <code>state</code> by using:</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">const</span> [value, setValue] = <span class="hljs-title function_">useState</span>(<span class="hljs-string">&#x27;&#x27;</span>)
<span class="hljs-keyword">const</span> predictions = <span class="hljs-title function_">useTextToxicity</span>(value)

<span class="hljs-comment">//...</span>
;<span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">textarea</span> <span class="hljs-attr">value</span>=<span class="hljs-string">{value}</span> <span class="hljs-attr">onChange</span>=<span class="hljs-string">{(e)</span> =&gt;</span> setValue(e.target.value)} /&gt;</span>
</code></pre><p>This way, everytime that the value changes, the predictions will be updated (we can predict &quot;on the fly&quot;).</p>
<p>Here&#39;s the full example code of <em>Fig 1</em>:</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">import</span> <span class="hljs-title class_">React</span>, { <span class="hljs-title class_">Fragment</span>, useState } <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;react&#x27;</span>
<span class="hljs-keyword">import</span> useTextToxicity <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;react-text-toxicity&#x27;</span>

<span class="hljs-keyword">function</span> <span class="hljs-title function_">Toxicity</span>(<span class="hljs-params">{ predictions }</span>) {
  <span class="hljs-keyword">const</span> style = { <span class="hljs-attr">margin</span>: <span class="hljs-number">10</span> }

  <span class="hljs-keyword">if</span> (!predictions) <span class="hljs-keyword">return</span> <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">style</span>=<span class="hljs-string">{style}</span>&gt;</span>Loading predictions...<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>

  <span class="hljs-keyword">return</span> (
    <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">style</span>=<span class="hljs-string">{style}</span>&gt;</span>
      {predictions.map(({ label, match, probability }) =&gt; (
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">style</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">margin:</span> <span class="hljs-attr">5</span> }} <span class="hljs-attr">key</span>=<span class="hljs-string">{label}</span>&gt;</span>
          {`${label} - ${probability} - ${match ? &#x27;🤢&#x27; : &#x27;🥰&#x27;}`}
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      ))}
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  )
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">Index</span>(<span class="hljs-params"></span>) {
  <span class="hljs-keyword">const</span> [value, setValue] = <span class="hljs-title function_">useState</span>(<span class="hljs-string">&#x27;&#x27;</span>)

  <span class="hljs-comment">// predictions are updated every time the value is updated</span>
  <span class="hljs-keyword">const</span> predictions = <span class="hljs-title function_">useTextToxicity</span>(value)

  <span class="hljs-keyword">return</span> (
    <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">style</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">display:</span> &#x27;<span class="hljs-attr">flex</span>&#x27; }}&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>Write something<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">textarea</span>
          <span class="hljs-attr">style</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">width:</span> <span class="hljs-attr">300</span>, <span class="hljs-attr">height:</span> <span class="hljs-attr">200</span> }}
          <span class="hljs-attr">value</span>=<span class="hljs-string">{value}</span>
          <span class="hljs-attr">onChange</span>=<span class="hljs-string">{(e)</span> =&gt;</span> setValue(e.target.value)}
        /&gt;
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      {value &amp;&amp; <span class="hljs-tag">&lt;<span class="hljs-name">Toxicity</span> <span class="hljs-attr">predictions</span>=<span class="hljs-string">{predictions}</span> /&gt;</span>}
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  )
}
</code></pre><h2 id="under-the-hook">Under the &quot;hook&quot;</h2>
<p>Under the hood, the hook is using Tensorflow.js toxicity model:</p>
<ul>
<li><a href="https://github.com/tensorflow/tfjs-models/tree/master/toxicity">https://github.com/tensorflow/tfjs-models/tree/master/toxicity</a></li>
</ul>
<p>If you need to implement the same outside React, you can use this repo.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Sometimes when we listen about machine learning and/or Tensorflow our mind disconnects, we think that&#39;s too complicated for us. However, there are pretrained models we can use without headaches.</p>
<p>The usage of React hooks facilitates data prediction from pretrained models in one simple line of code.</p>
<p>Now, I encourage you to experiment with these Tensorflow models or start using them in your projects!</p>
]]></content:encoded>
            </item>
            <item>
              <title>Discovering Snowpack</title>
              <description>Discover how Snowpack fits into the JavaScript ecosystem.</description>
              <link>https://aralroca.com/blog/discovering-snowpack</link>
              <guid isPermaLink="false">https://aralroca.com/blog/discovering-snowpack/</guid>
              <pubDate>Mon Jun 22 2020 00:00:00 GMT+0000 (Coordinated Universal Time)</pubDate>
              <content:encoded><![CDATA[<p>In today&#39;s article, we&#39;ll do some exploring to see what <a href="https://www.snowpack.dev/">Snowpack</a> is and what are its advantages.</p>
<p>In the last few months, I&#39;ve heard a lot of talk about Snowpack and I hadn&#39;t given it a chance. The time has come.</p>
<img src="https://aralroca.com/images/blog-images/snowpack_logo.png" alt="Snowpack logo" class="center transparent" />

<h2 id="working-with-esm">Working with ESM</h2>
<p>To understand what Snowpack does, let&#39;s see first how to work with ESM directly, without any tools.</p>
<p>Currently, if we want for example to setup a Preact app without any tooling, we can do something like this:</p>
<p><strong>index.html</strong></p>
<pre><code class="hljs language-html"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">&quot;en&quot;</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">&quot;viewport&quot;</span> <span class="hljs-attr">content</span>=<span class="hljs-string">&quot;width=device-width, initial-scale=1&quot;</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">&quot;utf-8&quot;</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">type</span>=<span class="hljs-string">&quot;module&quot;</span> <span class="hljs-attr">src</span>=<span class="hljs-string">&quot;index.js&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">&quot;stylesheet&quot;</span> <span class="hljs-attr">type</span>=<span class="hljs-string">&quot;text/css&quot;</span> <span class="hljs-attr">href</span>=<span class="hljs-string">&quot;style.css&quot;</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Example app<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">body</span> <span class="hljs-attr">id</span>=<span class="hljs-string">&quot;app&quot;</span> /&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre><p>Adding the <code>type=&quot;module&quot;</code> to the <code>script</code> tag is enough to tell the browser that we are using ESM.</p>
<p>Then, the <strong>index.js</strong> will be the entrypoint of our Preact app, the top of the Component tree.</p>
<p><strong>index.js</strong></p>
<pre><code class="hljs language-js"><span class="hljs-keyword">import</span> { html, render } <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;https://unpkg.com/htm/preact/standalone.module.js&#x27;</span>
<span class="hljs-keyword">import</span> { <span class="hljs-title class_">Example</span> } <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;./example.js&#x27;</span>

<span class="hljs-keyword">function</span> <span class="hljs-title function_">App</span>(<span class="hljs-params"></span>) {
  <span class="hljs-keyword">return</span> html`<span class="language-xml">
    <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Some example<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
    &lt;</span><span class="hljs-subst">${Example}</span><span class="language-xml"> /&gt;
  `</span>
}

<span class="hljs-title function_">render</span>(html`<span class="language-xml">&lt;</span><span class="hljs-subst">${App}</span><span class="language-xml"> /&gt;`</span>, <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">getElementById</span>(<span class="hljs-string">&#x27;app&#x27;</span>))
</code></pre><p>This works well. However, it has certain drawbacks that current tools such as Webpack or Parcel already solved. Among the most important:</p>
<ul>
<li>Hot reloading in development</li>
<li>Importing other files as <code>.json</code>, <code>.css</code>...</li>
<li>Tooling as Typescript, JSX, PostCSS, Sass, Babel...</li>
<li>Compatibility with legacy browsers</li>
<li>Managment of thirty party libraries</li>
</ul>
<p>So... Why not use Webpack or Parcel to cover this? It may still make sense to use bundlers such as Webpack or Parcel.
Let&#39;s keep asking... What does a bundler do? Why do we really need a bundler?</p>
<h2 id="module-bundlers">Module bundlers</h2>
<p>In 2012 there were no ESM and with the arrival of Webpack the use of bunlders began to be relevant. Thanks to them it&#39;s possible to use <code>.js</code> files as if they were modules, being able to use <code>import</code> and <code>export</code> everywhere.</p>
<p>Bundlers still make a lot of sense today, since many browsers do not yet support ESM. We can use the same process to minimize and optimize our production code.</p>
<p>The main question here should be &quot;Does it make sense to keep using a bundler in development?&quot; Running your entire application through a bundler introduces additional work and complexity to your dev workflow that is unnecessary now that ESM is widely supported.</p>
<h2 id="unbundled-development">Unbundled Development</h2>
<p>Snowpack was intended to use an unbundled development, using directly ESM. Among other advantages:</p>
<ul>
<li>Much faster (no-wait build time, reflecting changes immediately)</li>
<li>Easier to debug</li>
<li>Project size doesn&#39;t affect dev speed</li>
<li>Simpler tooling</li>
<li>Minimal configuration</li>
</ul>
<p>It also provides a solution to the ESM problems we have mentioned. Although you can do the production build directly with Snowpack, it gives you the flexibility to still optimizing your production builds with Webpack or Parcel.</p>
<img src="https://aralroca.com/images/blog-images/unbundle_dog.jpeg" alt="Image of dog and packages" class="center" />

<h2 id="preact-with-snowpack">Preact with Snowpack</h2>
<p>Let&#39;s create a Preact project with <a href="https://github.com/pikapkg/create-snowpack-app">create-snowpack-app</a> CLI:</p>
<pre><code>npx create-snowpack-app preact-snowpack-example --template @snowpack/app-template-preact --use-yarn
</code></pre><p>Then:</p>
<pre><code>cd preact-snowpack-example
yarn start
</code></pre><p>After <code>yarn start</code>, in ~50ms we have our Preact dev environment up under <code>http://localhost:8080</code>, with Babel, JSX and familiar Webpack things.</p>
<p>You can test it to see how fast and easy it is.</p>
<img src="https://aralroca.com/images/blog-images/snowpack-preact-example.gif" alt="Example Snowpack with Preact" class="center" />

<p>If you inspect the source code you&#39;ll see that ESM is used behind the scenes, with some differences:</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">import</span> { h, render } <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;/web_modules/preact.js&#x27;</span> <span class="hljs-comment">// Uses /web_modules/* for dependencies</span>
<span class="hljs-keyword">import</span> <span class="hljs-string">&#x27;/web_modules/preact/devtools.js&#x27;</span>
<span class="hljs-keyword">import</span> <span class="hljs-title class_">App</span> <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;./App.js&#x27;</span>
<span class="hljs-keyword">import</span> <span class="hljs-string">&#x27;./index.css.proxy.js&#x27;</span> <span class="hljs-comment">// Uses .js files for css imports</span>
</code></pre><h2 id="conclusion">Conclusion</h2>
<p>We have seen a bit of Snowpack&#39;s surface, enough to start understanding how it fits into the JavaScript ecosystem.</p>
<p>I hope this article will make you curious and eager to try Snowpack. It&#39;s not a guide, to know more details about Snowpack and ESM I recommend to visit the reference links.</p>
<h2 id="references">References</h2>
<ul>
<li><a href="https://www.snowpack.dev">https://www.snowpack.dev</a></li>
<li><a href="https://hacks.mozilla.org/2018/03/es-modules-a-cartoon-deep-dive/">https://hacks.mozilla.org/2018/03/es-modules-a-cartoon-deep-dive/</a></li>
</ul>
]]></content:encoded>
            </item>
            <item>
              <title>Do all roads lead to Rome?</title>
              <description>Learn what Rome is, how it fits into the JavaScript ecosystem and my thoughts about it... Will Rome replace all the current tooling?</description>
              <link>https://aralroca.com/blog/do-all-roads-lead-to-rome</link>
              <guid isPermaLink="false">https://aralroca.com/blog/do-all-roads-lead-to-rome/</guid>
              <pubDate>Mon Aug 24 2020 00:00:00 GMT+0000 (Coordinated Universal Time)</pubDate>
              <content:encoded><![CDATA[<p>We&#39;ll see what <a href="https://github.com/romefrontend/rome">Rome</a> is, how it fits into the JavaScript ecosystem, and my thoughts about it. <strong>Will Rome replace all the current tooling?</strong> Let&#39;s see.</p>
<h2 id="what-is-rome">What is Rome?</h2>
<p><a href="https://github.com/romefrontend/rome"><strong>Rome</strong></a> is a full toolchain (can be used via CLI, VSCode extension, and in the future via API for testing purposes) that unifies the JavaScript and Typescript development: bundling, compiling, documentation generation, formatting, linting, minification, testing, type checking, etc. It tries to be a &quot;<strong>one-in-all</strong>&quot; solution, being able to do all these tasks under one CLI.</p>
<p>Perhaps you wonder if we can handle Webpack, Babel, Prettier, Jest, etc with this Rome CLI? The answer is <strong>NO</strong>... Rome implemented all these toolchain <strong>from scratch</strong>, without any dependency, using the same configuration file for everything.</p>
<p>The person behind this project is <a href="https://twitter.com/sebmck">Sebastian McKenzie</a>, the same author of Babel and Yarn, who has also worked on projects such as Prepack and React Native.</p>
<figure align="center">
 <img src="https://aralroca.com/images/blog-images/rome-paths.jpg" alt="All tools lead to Rome" class="center" />
  <figcaption><small>All tools lead to Rome</small></figcaption>
</figure>

<blockquote>
<p>Rome is <strong>still in beta</strong>, and right now the current areas fully implemented are <strong>linting and formatting</strong>. The other areas are still in development.</p>
</blockquote>
<h2 id="better-performance">Better performance</h2>
<p>Current tools as webpack, TS, Babel, or ESlint, among others, <strong>run their own parser</strong> to generate an <a href="https://en.wikipedia.org/wiki/Abstract_syntax_tree"><strong>Abstract Syntax Tree</strong></a> (AST). After this, they manipulate and process their own AST.</p>
<p>When all these tasks become the only tool (Rome), we&#39;ll be able to reuse the AST for each task, parsing the files <strong>only once</strong>. Also, some processes will be simplified, for example: watching files, dependency verification, or integration with your editor.</p>
<figure align="center">
 <img src="https://aralroca.com/images/blog-images/ast-toolchain.png" alt="Reusing the AST" class="center transparent" />
  <figcaption><small>Reusing the AST</small></figcaption>
</figure>

<p>Who benefits?</p>
<ul>
<li><strong>Maintainers/contributors</strong>: simplifies all unnecessary duplications.</li>
<li><strong>Developers</strong>: a faster development experience with less consumption of resources.</li>
</ul>
<h2 id="only-one-configuration-file">Only one configuration file</h2>
<p>Right now we need to configure too many tools for a simple feature, for example, to work with absolute imports you need to configure ESLint, TS, and also webpack!</p>
<figure align="center">
 <img src="https://aralroca.com/images/blog-images/config-duplication.png" alt="Config duplication" class="center transparent" />
  <figcaption><small>Config duplication</small></figcaption>
</figure>

<p>Instead, using the same tool (Rome) removes the duplicated configurations, so we&#39;ll only have one config file.</p>
<figure align="center">
 <img src="https://aralroca.com/images/blog-images/one-config.png" alt="One config file for everything" class="center transparent" />
  <figcaption><small>One config file for everything</small></figcaption>
</figure>

<p>In addition, some tools configurations are currently depending on others, for example, Prettier has to be integrated with ESLint to avoid conflicting formatting. Often these tools integrations require adding a whole bunch of plugins to make them work together but, with Rome, we don&#39;t have the need to use <strong>any plugin</strong> since all the tools are <strong>perfectly integrated</strong>.</p>
<p>Who benefits?</p>
<ul>
<li><strong>Maintainers/contributors</strong>: Tools like Babel had exposed all their internals to allow the creation of plugins, making it much more difficult to maintain and evolve.</li>
<li><strong>Developers</strong>: For a feature, you only need to touch one configuration, <strong>simplifying the development</strong> without the need of plugins. It also makes it much easier to <strong>reuse settings</strong> between projects.</li>
</ul>
<h2 id="less-dependencies">Less dependencies</h2>
<p>In the JavaScript ecosystem, we&#39;re used to work with many <code>devDependencies</code> for all the tools and their plugins: Jest, Babel, Prettier, webpack. Besides, each of them have their own dependencies.</p>
<p>With Rome, many of these <code>devDependencies</code> disappear, moreover <a href="https://github.com/romefrontend/rome/blob/4fdfc5fb7252085ede73a342d895457328dca46e/package.json#L8"><strong>Rome has zero dependencies</strong></a>.</p>
<figure align="center">
 <img src="https://aralroca.com/images/blog-images/dependencies-rome.gif" alt="Webpack vs Rome dependencies tree" class="center" />
  <figcaption><small>Webpack vs Rome dependencies tree</small></figcaption>
</figure>

<p>So we have:</p>
<ul>
<li><strong>Stronger security</strong>: The chance to have a vulnerability decreases because we drastically reduce all dependencies.</li>
<li><strong>Better maintenance and evolution of your project</strong>: Before, updating a new release of some tool could have broken the integration with another one. Now all is under one well-integrated big tool.</li>
<li><strong>Better maintenance and evolution of Rome</strong>: Rome has no dependencies, this means it has a lot of flexibility and a great innovator potential. For example, if they wanted to migrate to another language using wasm, they could do it.</li>
<li><strong>Faster installation</strong>: You won&#39;t have to wait so long after doing <code>npm install</code>. Less things to install.</li>
</ul>
<h2 id="friendly-for-beginners">Friendly for beginners</h2>
<p>As we&#39;ve seen in the previous sections, Rome makes it much <strong>easier to start</strong> a project.</p>
<p>We feel more encouraged to start learning something new if we see that it has an easy start. In the last years starting JavaScript projects without frameworks was really complicated. Simplifying all this tooling will encourage many to learn and enter the world of JavaScript in a much more enjoyable way.</p>
<figure align="center">
 <img src="https://aralroca.com/images/blog-images/learning-js.jpg" alt="Picture of a woman learning JavaScript" class="center" />
  <figcaption><small>Photo by Annie Spratt on Unsplash</small></figcaption>
</figure>

<p>In order to take the first steps in Rome, you can <a href="https://romefrontend.dev/#installation">install it</a> via <code>yarn</code> or <code>npm</code>, and then run <code>npx rome init</code> / <code>yarn rome init</code> to create the default <code>.config/rome.rjson</code>.</p>
<p>Finally, you can use every command of the CLI using <code>rome [command]</code>, as <code>rome check</code> for linting and formatting a set of files, <code>rome format</code> to format a single file, <code>rome start</code> to start a daemon, etc. You can see all the available commands by running <code>rome --help</code>.</p>
<h2 id="do-all-roads-lead-to-rome-my-thoughts-about-it">Do all roads lead to Rome? My thoughts about it</h2>
<p>We started the article questioning about if <strong>Rome is going to replace all current toolchain</strong>. It&#39;s a question that will be answered over time, depending on the adoption of Rome within the community and the evolution of Rome and the current tools.</p>
<p>From my point of view, I think that in the short term the answer is &quot;no&quot;. Although it replaces the functionality of those tools, that doesn&#39;t obsolete them. There are still a lot of projects that depend on them. However, in the long term I think the answer would be &quot;yes&quot;. It may not be Rome, maybe another similar approach like Deno adopting a similar philosophy of having everything integrated.</p>
<p>In my opinion, the JavaScript ecosystem is too fragmented right now, you usually need to have a bunch of dependencies installed for most things. New projects like Rome or Deno, even though many people think that they&#39;re fragmenting even more the ecosystem, I think it&#39;s just the opposite. They&#39;re tools that aim to fix that fragmentation, and that&#39;s positive.</p>
<p>You may be wondering if one of the pieces doesn&#39;t fit 100% your needs and you still want to use your old tool. For example, you could prefer to continue using webpack as a compiler. The good thing about Rome is that if you only want to use one piece, you can. Thus, you can also use all of Rome&#39;s pieces except the compiler, to continue using webpack or another one.</p>
<figure align="center">
 <img src="https://aralroca.com/images/blog-images/me.jpg" alt="Aral Roca (me) in philosophical mode" class="center" />
  <figcaption><small>Aral Roca (me) in philosophical mode</small></figcaption>
</figure>

<p>Now it&#39;s your turn, what do you think about the future of JavaScript / TypeScript tooling?</p>
<h2 id="references">References</h2>
<ul>
<li><a href="https://romefrontend.dev/blog/2020/08/08/introducing-rome.html">https://romefrontend.dev/blog/2020/08/08/introducing-rome.html</a></li>
<li><a href="https://news.ycombinator.com/item?id=24094377">https://news.ycombinator.com/item?id=24094377</a></li>
<li><a href="https://jasonformat.com/rome-javascript-toolchain/">https://jasonformat.com/rome-javascript-toolchain/</a></li>
</ul>
]]></content:encoded>
            </item>
            <item>
              <title>Don’t control everything! React forms</title>
              <description>Use uncontrolled forms as default way to handle forms.</description>
              <link>https://aralroca.com/blog/dont-control-everything-react-forms</link>
              <guid isPermaLink="false">https://aralroca.com/blog/dont-control-everything-react-forms/</guid>
              <pubDate>Fri Nov 02 2018 00:00:00 GMT+0000 (Coordinated Universal Time)</pubDate>
              <content:encoded><![CDATA[<p>Forms are a crucial part of almost all applications. At least one of them is usually necessary: the “Sign in” page. In this article, we are going to explain the benefits of uncontrolled forms in React and how to do it as simple as possible to re-use it in every form. We are going to use the classic “Sign in” page as an example.</p>
<img class="center" src="https://aralroca.com/images/blog-images/9.png" alt="Sign in image" />

<h3 id="difference-between-controlled-and-uncontrolled">Difference between controlled and uncontrolled</h3>
<p>To understand what “uncontrolled” means, first, we’ll see the meaning of “controlled”.</p>
<p>A common mistake in React is to try to control every single field of a form using a state and an onChange method. This way is usually chosen to allow the use of this state inside the onSubmit method, although it’s not the only and best way to get the fields.</p>
<h4 id="controlled-fields">Controlled fields</h4>
<pre><code class="hljs language-jsx">&lt;form onSubmit={onSignIn}&gt;

  <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
      <span class="hljs-attr">required</span>
      <span class="hljs-attr">value</span>=<span class="hljs-string">{this.state.username}</span>
      <span class="hljs-attr">onChange</span>=<span class="hljs-string">{e</span> =&gt;</span> this.setState({ username: e.target.value )}
      name=&quot;username&quot;
      type=&quot;text&quot;
      placeholder={userNamePlaceholder}
    /&gt;
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>

  <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
      <span class="hljs-attr">value</span>=<span class="hljs-string">{this.state.password}</span>
      <span class="hljs-attr">onChange</span>=<span class="hljs-string">{e</span> =&gt;</span> this.setState({ password: e.target.value )}
      name=&quot;password&quot;
      type=&quot;password&quot;
      placeholder={passwordPlaceholder}
    /&gt;
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>

  <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">type</span>=<span class="hljs-string">&quot;submit&quot;</span>&gt;</span>
      Sign In
  <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span></span>
&lt;/form&gt;
</code></pre><p>Then we can use the state directly in the onSignIn method.</p>
<pre><code class="hljs language-js">onSignIn = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> { username, password } = <span class="hljs-variable language_">this</span>.<span class="hljs-property">state</span>
  <span class="hljs-comment">// ...</span>
}
</code></pre><p>These fields are controlled because every time that the state changes, the text rendered changes inside the input. Moreover, every time that the user types, the onChange event is fired to save the new state. If we type a username of 15 characters and a password of 8; 24 react renders will happen under the hood (one for each character + one extra for the first render).</p>
<p>This controlled behavior is useful if the state is used before submitting the form. For example, to validate dynamically the fields. Otherwise, if we want to use all the fields after submitting the form, it will be more useful to do it uncontrolled.</p>
<p>Uncontrolled fields are the natural way to write without a React state:</p>
<h4 id="uncontrolled-input">Uncontrolled input</h4>
<pre><code class="hljs language-js">&lt;form onSubmit={onSignIn}&gt;
  <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
      <span class="hljs-attr">required</span>
      <span class="hljs-attr">name</span>=<span class="hljs-string">&quot;username&quot;</span>
      <span class="hljs-attr">type</span>=<span class="hljs-string">&quot;text&quot;</span>
      <span class="hljs-attr">placeholder</span>=<span class="hljs-string">{userNamePlaceholder}</span>
    /&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>

  <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">name</span>=<span class="hljs-string">&quot;password&quot;</span> <span class="hljs-attr">type</span>=<span class="hljs-string">&quot;password&quot;</span> <span class="hljs-attr">placeholder</span>=<span class="hljs-string">{passwordPlaceholder}</span> /&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>

  <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">type</span>=<span class="hljs-string">&quot;submit&quot;</span>&gt;</span>Sign In<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span></span>
&lt;/form&gt;
</code></pre><p>In this case, the state is not necessary. We need these fields on the event onSubmit but it’s not necessary to store it at every change in the React state because we already have it in the event. This means that we only do 1 simple render for this component: The first render.</p>
<p>On the onSignIn function, we can find the username and password fields inside event.target.</p>
<pre><code class="hljs language-js">onSignIn = <span class="hljs-function">(<span class="hljs-params">event</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> [username, password] = <span class="hljs-title class_">Array</span>.<span class="hljs-property"><span class="hljs-keyword">prototype</span></span>.<span class="hljs-property">slice</span>
    .<span class="hljs-title function_">call</span>(event.<span class="hljs-property">target</span>)
    .<span class="hljs-title function_">map</span>(<span class="hljs-function">(<span class="hljs-params">field</span>) =&gt;</span> field.<span class="hljs-property">value</span>)

  <span class="hljs-comment">// ...</span>
}
</code></pre><p>However, although we simplified it a little, it’s still quite ugly to repeat this Array.prototype.slice.call in every single form submit. Let’s see how to improve it.</p>
<h3 id="improving-the-uncontrolled-way">Improving the uncontrolled way</h3>
<p>Our goal here is to simplify the logic of every “submit” event in order to avoid the need of finding continuously the fields inside the event.target. We want something more enjoyable like:</p>
<pre><code class="hljs language-js">onSignIn = <span class="hljs-function">(<span class="hljs-params">{ username, password }</span>) =&gt;</span> {
  <span class="hljs-comment">// ...</span>
}
</code></pre><p>In this case, we will provide the fields directly as an argument. This argument is an object with all the fields of the form when the key is the name attribute.</p>
<p>To achieve our goal, we can replace the form tag to our personal Component:</p>
<img class="center" src="https://aralroca.com/images/blog-images/10.png" alt="form" />

<p>Our reusable personal Form Component could be:</p>
<pre><code class="hljs language-jsx"><span class="hljs-keyword">function</span> <span class="hljs-title function_">Form</span>(<span class="hljs-params">{ children, onSubmit, ...restOfProps }</span>) {
  <span class="hljs-keyword">const</span> onSubmitAllFields = <span class="hljs-title function_">useCallback</span>(
    <span class="hljs-function">(<span class="hljs-params">event</span>) =&gt;</span> {
      event.<span class="hljs-title function_">preventDefault</span>()
      event.<span class="hljs-title function_">stopPropagation</span>()

      <span class="hljs-keyword">const</span> fields = <span class="hljs-title class_">Array</span>.<span class="hljs-property"><span class="hljs-keyword">prototype</span></span>.<span class="hljs-property">slice</span>
        .<span class="hljs-title function_">call</span>(event.<span class="hljs-property">target</span>)
        .<span class="hljs-title function_">filter</span>(<span class="hljs-function">(<span class="hljs-params">field</span>) =&gt;</span> field.<span class="hljs-property">name</span>)
        .<span class="hljs-title function_">reduce</span>(
          <span class="hljs-function">(<span class="hljs-params">form, { name, value }</span>) =&gt;</span> ({
            ...form,
            [name]: <span class="hljs-keyword">typeof</span> value === <span class="hljs-string">&#x27;string&#x27;</span> ? value.<span class="hljs-title function_">trim</span>() : value,
          }),
          {}
        )

      <span class="hljs-title function_">onSubmit</span>(fields)
    },
    [onSubmit]
  )

  <span class="hljs-keyword">return</span> (
    <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">form</span> {<span class="hljs-attr">...restOfProps</span>} <span class="hljs-attr">onSubmit</span>=<span class="hljs-string">{onSubmitAllFields}</span>&gt;</span>
      {children}
    <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span></span>
  )
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-title function_">memo</span>(<span class="hljs-title class_">Form</span>)
</code></pre><p>Thus, we are moving the repeating code that we always do in our forms: <strong>preventDefault</strong> , <strong>stopPropagation</strong> , <strong>extract fields</strong> + <strong>trim string fields</strong>.</p>
<p>Now, we can use this new approach by only changing one character, from “form” to “Form”.</p>
<blockquote>
<p><strong>Note</strong> : I’m using the new hooks API (proposal), even though it can also be written as a class component.</p>
</blockquote>
<h3 id="conclusions">Conclusions</h3>
<p>Both approaches; controlled and uncontrolled forms are great for different reasons. We have to know the difference to choose the best for any occasion. My advice would be: use normally uncontrolled unless you really need the state to do dynamic checks or to change dynamically the text of each input.</p>
<p>{% twitter 1015954912075644930 %}</p>
<p>If you want to try the Form component, I added in npm:</p>
<pre><code>npm install react-form-uncontrolled --save
</code></pre><p>Repo: <a href="https://github.com/aralroca/react-form-uncontrolled">https://github.com/aralroca/react-form-uncontrolled</a></p>
]]></content:encoded>
            </item>
            <item>
              <title>Etiketai - speed up training your AI models with a free open source app</title>
              <description>I've launched Etiketai. Training AI models are now more friendlier. Use it from any device ensuring security. It has support for ImageNet and YOLO.</description>
              <link>https://aralroca.com/blog/etiketai</link>
              <guid isPermaLink="false">https://aralroca.com/blog/etiketai/</guid>
              <pubDate>Sun Sep 20 2020 00:00:00 GMT+0000 (Coordinated Universal Time)</pubDate>
              <content:encoded><![CDATA[<p>I&#39;d like to tell you why I made <strong><a href="https://github.com/aralroca/etiketai">Etiketai</a></strong>, a tool that makes it easier to train image recognition AI models (ImageNet, YOLO and its variants) from any device ensuring security.</p>
<h2 id="a-little-bit-of-context">A little bit of context</h2>
<p>When we want to directly use existing image recognition models such as ImageNet, COCO-ssd or YOLO, we are limited to predict only everyday objects such as cars, people, etc. This is because these models have only learned to recognize these objects within an image. However, there are techniques such as transfer-learning that allow us to retrain these models to <strong>predict what we want</strong>. </p>
<p>In order to retrain these models, we have to <strong>manually label each object</strong> that we want to recognize by writing the coordinates of the object in a text file for each image to train. This way the model will be able to learn how to recognize them. This labeling process can be very boring and tedious.</p>
<p>Currently, there are not many alternative tools for this labeling process. The best known current tool is <a href="https://github.com/tzutalin/labelImg">labelImg</a>. The tool is good and does its job, although it has some root problems:</p>
<ul>
<li><strong>Not available in all devices</strong>. It can only be downloaded as a desktop application.</li>
<li><strong>Requires installation</strong>. It requires installation and it isn&#39;t very beginner-friendly. Depending on your OS and Python version the dependencies will be different. For example on Mac with Python 3+, you need to install first some dependencies like <code>qt</code> and <code>libxml2</code> with Homebrew, and <code>pyqt5</code> and <code>lxml</code> with pip.</li>
<li><strong>Security</strong>. The application manipulates the files on your system. In theory, it only manipulates files related to annotations. I say &quot;in theory&quot;, because we hope there won&#39;t be a bug in the future touching what it shouldn&#39;t... </li>
<li><strong>Updates are not automatic</strong>. Related to the previous point, many updates are made for security reasons, especially if it has dependencies. The fact that updates are not done automatically makes it your responsibility to keep your application up to date.</li>
</ul>
<h2 id="launching-etiketai">Launching Etiketai</h2>
<p>Using labelImg during the last months, I realized that a <strong>web application</strong> inspired by it would solve several of these problems:</p>
<ul>
<li><strong>Available in all devices</strong>. Being a web application makes it accessible from any device, even tablets, and mobiles.</li>
<li><strong>No installation required</strong>. It speeds up the start, as it does not require installation and has no dependencies on your operating system. Only the browser.</li>
<li><strong>Automatic updates</strong>. You will always have the latest version available.</li>
<li><strong>Security</strong>. No file on your system is directly manipulated. Files are imported/saved using the security layer of your browser.</li>
<li><strong>Beginner-friendly</strong>. We want it to be an easy-to-use process without losing flexibility. To start, you only need to open a browser with any device.</li>
</ul>
<p>So during my August holiday, I took the opportunity to implement the first POC of my idea. And today, I announce that its <strong>first version is out</strong>.</p>
<a href="https://github.com/aralroca/etiketai">
  <figure align="center">
    <img class="center" src="https://aralroca.com/images/blog-images/etiketai.png" alt="Etiketai logo" />
    <figcaption><small>Etiketai</small></figcaption>
  </figure>
</a>

<p>This version 1.0.0 is focused on being useful as a web tool to label your images and supports both <strong>ImageNet</strong> and <strong>YOLO</strong>, and its variants.</p>
<p>In addition, I tried to improve the user experience when labeling by making it less necessary to press so many buttons.</p>
<p>Currently, I have some future ideas to expand the features so that it does not remain only as an annotation tool, but to train models after labeling the images.</p>
<figure align="center">
  <img class="center" src="https://aralroca.com/images/blog-images/demo.gif" alt="Etiketai demo" />
  <figcaption><small>Etiketai demo</small></figcaption>
</figure>


<h2 id="future-features">Future features</h2>
<p>As a free open-source tool we want to evolve according to the contributions of the community. However, in the first version, there are some things that have not yet been implemented and the idea is to implement them for the next version:</p>
<ul>
<li><strong>Improve tablet / mobile experience</strong>. Now the support is minimal, it works, but not as well as some users would like. For example, it is not very responsive. This should be improved in a next version.</li>
<li>Possibility to <strong>train directly</strong> your labeled images <strong>with the same app</strong> and also to save the generated model.</li>
<li><strong>Offline</strong> support. Now it only works online, but one of the improvements would be to support it offline as PWA.</li>
</ul>
<p>Any further improvements you would like to make? Please let me know in the comments.</p>
<h2 id="try-it">Try it</h2>
<p>I encourage you to try the app and contribute to GitHub to evolve this tool according to the community.</p>
<ul>
<li>App: <a href="https://etiketai.vercel.app/">https://etiketai.vercel.app/</a></li>
<li>GitHub: <a href="https://github.com/aralroca/etiketai">https://github.com/aralroca/etiketai</a></li>
</ul>
<p>To help me boost this project, please, let me know that you like it by <strong>starring on GitHub</strong>.</p>
]]></content:encoded>
            </item>
            <item>
              <title>Exploring HTMLPortalElement with React</title>
              <description>In this article, I will explain how to use HTMLPortalElement doing a “Hello world” demo with React.</description>
              <link>https://aralroca.com/blog/exploring-htmlportalelement-with-react</link>
              <guid isPermaLink="false">https://aralroca.com/blog/exploring-htmlportalelement-with-react/</guid>
              <pubDate>Mon Jun 10 2019 00:00:00 GMT+0000 (Coordinated Universal Time)</pubDate>
              <content:encoded><![CDATA[<p>HTMLPortalElement is a draft of a new HTML Element, very similar to iframes but with the big difference that it allows to navigate to the content of the &quot;iframe&quot; by using a page transition.</p>
<img class="center" src="https://aralroca.com/images/blog-images/3.gif" alt="Example of HTMLPortalElement" />

<p>To know more about it, I recommend to read these references:</p>
<ul>
	<li><a href="https://wicg.github.io/portals/#the-portalactivateevent-interface">https://wicg.github.io/portals/#the-portalactivateevent-interface</a></li>
	<li><a href="https://web.dev/hands-on-portals">https://web.dev/hands-on-portals</a></li>
	<li><a href="https://github.com/WICG/portals/blob/master/explainer.md">https://github.com/WICG/portals/blob/master/explainer.md</a></li>
</ul>

<p>In this article, I will explain how to use this future feature to do a &quot;Hello world&quot; demo with React.</p>
<h2>Getting started</h2>
<p>
First of all, to use this draft feature you'll need Chrome Canary. Once you have it, activate the flag of Portals:
</p>

<img class="center" src="https://aralroca.com/images/blog-images/4.png" alt="Portal flag in Chrome" />

<p>Next, we&#39;ll test portals. Remember that portals need to be on the top level of our app (unlike it happens with iframes).</p>
<p><strong>Hello world with HTMLPortalElement and React:</strong></p>
<pre><code class="hljs language-jsx"><span class="hljs-keyword">import</span> <span class="hljs-title class_">React</span>, { useState, useEffect, useRef } <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;react&#x27;</span>
<span class="hljs-keyword">import</span> { render } <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;react-dom&#x27;</span>

<span class="hljs-keyword">function</span> <span class="hljs-title function_">PortalExample</span>(<span class="hljs-params"></span>) {
  <span class="hljs-keyword">if</span> (!<span class="hljs-variable language_">window</span>.<span class="hljs-property">HTMLPortalElement</span>) {
    <span class="hljs-keyword">return</span> <span class="hljs-string">&#x27;HTMLPortalElement is not supported in your browser.&#x27;</span>
  }

  <span class="hljs-keyword">return</span> <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">portal</span> <span class="hljs-attr">src</span>=<span class="hljs-string">&quot;https://aralroca.com&quot;</span> /&gt;</span></span>
}

<span class="hljs-title function_">render</span>(<span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">PortalExample</span> /&gt;</span></span>, <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">getElementById</span>(<span class="hljs-string">&#x27;root&#x27;</span>))
</code></pre><p>We get a similar result than using an iframe:</p>
<img class="center" src="https://aralroca.com/images/blog-images/5.png" alt="Devtools inspecting the portal" />

<p>Nevertheless, we want a beautiful transition to navigate to the content of this page. How could we get this?</p>
<h2>Navigating to a portal</h2>
As I said, there is a significant difference between portals and iframes; with portals we can navigate to the content. In order to do that, the element has the function <strong>activate</strong> to go to the page.

<pre><code class="hljs language-jsx">&lt;portal
  src=<span class="hljs-string">&quot;https://aralroca.com&quot;</span>
  <span class="hljs-comment">// navigate to content</span>
  onClick={<span class="hljs-function">(<span class="hljs-params">{ target }</span>) =&gt;</span> target.<span class="hljs-title function_">activate</span>()}
/&gt;
</code></pre><p>Now we can navigate to the content. Although without any transition... yet:</p>
<img class="center" src="https://aralroca.com/images/blog-images/6.gif" alt="Using the portal" width="800" height="412" />
<br />

<h2>Adding a page transition</h2>
Instead of calling the <strong>activate</strong> function on the <strong>onClick</strong> event, we are going to use the <strong>onClick</strong> event to add an extra css class with the transition. Then, we are going to use the <strong>onTransitionEnd </strong>event to control when the css transition is finished. After that, we'll call the <strong>activate </strong>function<strong>.</strong>

<p>Therefore, our css transition is going to scale the portal until the portal fits all the content of the page (width and height 100%).</p>
<p>React code:</p>
<pre><code class="hljs language-jsx"><span class="hljs-keyword">import</span> <span class="hljs-title class_">React</span>, { useState } <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;react&#x27;</span>
<span class="hljs-keyword">import</span> { render } <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;react-dom&#x27;</span>

<span class="hljs-keyword">import</span> <span class="hljs-string">&#x27;./style.css&#x27;</span>

<span class="hljs-keyword">function</span> <span class="hljs-title function_">PortalExample</span>(<span class="hljs-params"></span>) {
  <span class="hljs-keyword">const</span> [transition, setTransition] = <span class="hljs-title function_">useState</span>(<span class="hljs-literal">false</span>)

  <span class="hljs-keyword">if</span> (!<span class="hljs-variable language_">window</span>.<span class="hljs-property">HTMLPortalElement</span>) {
    <span class="hljs-keyword">return</span> <span class="hljs-string">&#x27;HTMLPortalElement is not supported in your browser.&#x27;</span>
  }

  <span class="hljs-keyword">return</span> (
    <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">portal</span>
      <span class="hljs-attr">src</span>=<span class="hljs-string">&quot;https://aralroca.com&quot;</span>
      <span class="hljs-attr">className</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">portal</span> ${<span class="hljs-attr">transition</span> ? &#x27;<span class="hljs-attr">portal-reveal</span>&#x27; <span class="hljs-attr">:</span> &#x27;&#x27;}`}
      <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> setTransition(true)}
      onTransitionEnd={(e) =&gt;
        e.propertyName === &#x27;transform&#x27; &amp;&amp; e.target.activate()
      }
    /&gt;</span>
  )
}

<span class="hljs-title function_">render</span>(<span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">PortalExample</span> /&gt;</span></span>, <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">getElementById</span>(<span class="hljs-string">&#x27;root&#x27;</span>))
</code></pre><p>Styles:</p>
<pre><code class="hljs language-css"><span class="hljs-selector-tag">body</span> {
  <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#212121</span>;
}

<span class="hljs-selector-class">.portal</span> {
  <span class="hljs-attribute">position</span>: fixed;
  <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>;
  <span class="hljs-attribute">cursor</span>: pointer;
  <span class="hljs-attribute">height</span>: <span class="hljs-number">100%</span>;
  <span class="hljs-attribute">transition</span>: transform <span class="hljs-number">0.4s</span>;
  <span class="hljs-attribute">box-shadow</span>: <span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">20px</span> <span class="hljs-number">10px</span> <span class="hljs-number">#999</span>;
  <span class="hljs-attribute">transform</span>: <span class="hljs-built_in">scale</span>(<span class="hljs-number">0.4</span>);
}

<span class="hljs-selector-class">.portal</span><span class="hljs-selector-class">.portal-reveal</span> {
  <span class="hljs-attribute">transform</span>: <span class="hljs-built_in">scale</span>(<span class="hljs-number">1</span>);
}
</code></pre><p>Finally, we get the page transition in our portal:</p>
<img class="center" src="https://aralroca.com/images/blog-images/7.gif" alt="Adding a transition to the portal" width="800" height="412" />

<p>Code: <a href="https://github.com/aralroca/HTMLPortalElement-react-example">https://github.com/aralroca/HTMLPortalElement-react-example</a></p>
<h2>Benefits of portals</h2>
Portals are a new proposal to load pages as an iframe, allowing the navigation to the content with a beautiful transition and improving the user's experience.

<p>They can be useful for previews of videos / audio, so you can navigate to the content page without stop watching / listening the media at any moment.</p>
<img class="center" src="https://aralroca.com/images/blog-images/8.gif" alt="Final example using portals" width="800" height="413" />

<p>Of course, here we are using a different origin (YouTube). Nevertheless, if we use the same origin, we can communicate with the portal at any moment and do things like displaying a beauty preview or loading the rest of the content after the portal is activated.</p>
<h2>Conclusion</h2>
Portals are still a proposal and maybe it's something we won't see in the future. Whatever, if it finally exists, it's going to be useful to preview content, especially, for media.



<h2>References:</h2>
<ul>
	<li><a href="https://wicg.github.io/portals/#the-portalactivateevent-interface">https://wicg.github.io/portals/#the-portalactivateevent-interface</a></li>
	<li><a href="https://web.dev/hands-on-portals">https://web.dev/hands-on-portals</a></li>
	<li><a href="https://github.com/WICG/portals/blob/master/explainer.md">https://github.com/WICG/portals/blob/master/explainer.md</a></li>
</ul>
]]></content:encoded>
            </item>
            <item>
              <title>First steps in WebGL</title>
              <description>Learn what WebGL is and how it works by drawing a triangle.</description>
              <link>https://aralroca.com/blog/first-steps-in-webgl</link>
              <guid isPermaLink="false">https://aralroca.com/blog/first-steps-in-webgl/</guid>
              <pubDate>Mon Jul 20 2020 00:00:00 GMT+0000 (Coordinated Universal Time)</pubDate>
              <content:encoded><![CDATA[<p>In this article we&#39;ll see what WebGL is and how to draw a <strong>triangle</strong> by talking to the graphics processing unit (GPU). Although this simple example could be solved in better ways, such as using a canvas with a 2d context or even with CSS, what we want is to start with WebGL. Like a &quot;hello world&quot;, to understand how it works.</p>
<img src="https://aralroca.com/images/blog-images/triangle.jpeg" alt="Triangle" class="center" />
<small class="center">Photo by: Apurv Das (Unsplash)</small>

<p><strong>We will cover the following:</strong></p>
<ul>
<li><a href="#what-is-webgl">What is WebGL?</a></li>
<li><a href="#creating-a-webgl-canvas">Creating a WebGL Canvas</a></li>
<li><a href="#vertex-coordinates">Vertex coordinates</a></li>
<li><a href="#glsl-and-shaders">GLSL and shaders</a><ul>
<li><a href="#vertex-shader">Vertex shader</a></li>
<li><a href="#fragment-shader">Fragment shader</a></li>
</ul>
</li>
<li><a href="#create-program-from-shaders">Create program from shaders</a></li>
<li><a href="#create-buffers">Create buffers</a></li>
<li><a href="#link-data-from-cpu-to-gpu">Link data from CPU to GPU</a></li>
<li><a href="#drawing-the-triangle">Drawing the triangle</a></li>
<li><a href="#all-the-code-together">All the code together</a></li>
<li><a href="#conclusion">Conclusion</a></li>
<li><a href="#references">References</a></li>
</ul>
<h2 id="what-is-webgl">What is WebGL?</h2>
<p>The literal definition of WebGL is &quot;Web Graphics Library&quot;. However, it is not a 3D library that offers us an easy-to-use API to say: «put a light here, a camera there, draw a character here, etc».</p>
<p>It&#39;s in a low-level that converts <strong>vertices</strong> into <strong>pixels</strong>. We can understand WebGL as a rasterization engine. It&#39;s based on OpenGL ES 3.0 graphical API (WebGL 2.0, unlike the old version that is based on ES 2.0).</p>
<img src="https://aralroca.com/images/blog-images/webgl-schema.png" class="center" alt="WebGL Schema" />

<p>The existing 3d libraries on the web (like <a href="https://threejs.org/">THREE.js</a> or <a href="https://github.com/BabylonJS/Babylon.js">Babylon.js</a>) use WebGL below. They need a way to communicate to the GPU to tell what to draw.</p>
<p>This example could also be directly solved with THREE.js, using the <code>THREE.Triangle</code>. You can see an example <a href="https://stackoverflow.com/a/29843694/4467741">here</a>. However, the purpose of this tutorial is to understand how it works underneath, i.e. how these 3d libraries communicate with the GPU via WebGL. We are going to render a triangle without the help of any 3d library.</p>
<img src="https://aralroca.com/images/blog-images/3dlibraries_underthehood.png" class="center" alt="Let's explore how to do a triangle without 3D libraries" />

<h2 id="creating-a-webgl-canvas">Creating a WebGL canvas</h2>
<p>In order to draw a triangle, we need to define the area where it is going to be rendered via WebGL.</p>
<p>We are going to use the element canvas of HTML5, retrieving the context as <code>webgl2</code>.</p>
<pre><code class="hljs language-jsx"><span class="hljs-keyword">import</span> { useRef, useEffect } <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;preact/hooks&#x27;</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">Triangle</span>(<span class="hljs-params"></span>) {
  <span class="hljs-keyword">const</span> canvas = <span class="hljs-title function_">useRef</span>()

  <span class="hljs-title function_">useEffect</span>(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> bgColor = [<span class="hljs-number">0.47</span>, <span class="hljs-number">0.7</span>, <span class="hljs-number">0.78</span>, <span class="hljs-number">1</span>] <span class="hljs-comment">// r,g,b,a as 0-1</span>
    <span class="hljs-keyword">const</span> gl = canvas.<span class="hljs-property">current</span>.<span class="hljs-title function_">getContext</span>(<span class="hljs-string">&#x27;webgl2&#x27;</span>) <span class="hljs-comment">// WebGL 2.0</span>

    gl.<span class="hljs-title function_">clearColor</span>(bgColor) <span class="hljs-comment">// set canvas background color</span>
    gl.<span class="hljs-title function_">clear</span>(gl.<span class="hljs-property">DEPTH_BUFFER_BIT</span> | gl.<span class="hljs-property">COLOR_BUFFER_BIT</span>) <span class="hljs-comment">// clear buffers</span>
    <span class="hljs-comment">// @todo: Render the triangle...</span>
  }, [])

  <span class="hljs-keyword">return</span> <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">canvas</span> <span class="hljs-attr">style</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">width:</span> &#x27;<span class="hljs-attr">100vw</span>&#x27;, <span class="hljs-attr">height:</span> &#x27;<span class="hljs-attr">100vh</span>&#x27; }} <span class="hljs-attr">ref</span>=<span class="hljs-string">{canvas}</span> /&gt;</span></span>
}
</code></pre><p>The <code>clearColor</code> method sets the background color of the canvas using RGBA (with values from 0 to 1).</p>
<p>Furthermore, the <code>clear</code> method clears buffers to preset values. Used constants values are going to depend on your GPU capacity.</p>
<p>Once we have the canvas created, we are ready to render the inside triangle using WebGL... Let&#39;s see how.</p>
<h2 id="vertex-coordinates">Vertex coordinates</h2>
<p>First of all, we need to know that all these vectors range from -1 to 1.</p>
<p>Corners of the canvas:</p>
<img src="https://aralroca.com/images/blog-images/coordinates-webgl.png" alt="Coordinates" class="center" />

<ul>
<li><strong>(0, 0)</strong> - Center</li>
<li><strong>(1, 1)</strong> - Top right</li>
<li><strong>(1, -1)</strong> - Bottom right</li>
<li><strong>(-1, 1)</strong> - Top left</li>
<li><strong>(-1, -1)</strong> - Bottom left</li>
</ul>
<p>The triangle we want to draw has these three points:</p>
<img src="https://aralroca.com/images/blog-images/triangle_webgl.png" alt="Coordinates" class="center" />

<p><strong>(-1, -1)</strong>, <strong>(0, 1)</strong> and <strong>(1, -1)</strong>. Thus, we are going to store the triangle coordinates into an array:</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">const</span> coordinates = [-<span class="hljs-number">1</span>, -<span class="hljs-number">1</span>, <span class="hljs-number">0</span>, <span class="hljs-number">1</span>, <span class="hljs-number">1</span>, -<span class="hljs-number">1</span>]
</code></pre><h2 id="glsl-and-shaders">GLSL and shaders</h2>
<p>A shader is a type of computer program used in computer graphics to calculate rendering effects with high degree of flexibility. These shaders are coded and run on the GPU, written in OpenGL ES Shading Language (GLSL ES), a language similar to C or C++.</p>
<img src="https://aralroca.com/images/blog-images/shaders.png" class="center" alt="WebGL Shaders" />

<p>Each WebGL program that we are going to run is composed by two shader functions; the <strong>vertex shader</strong> and the <strong>fragment shader</strong>.</p>
<p>Almost all the WebGL API is made to run these two functions (vertex and fragment shaders) in different ways.</p>
<h3 id="vertex-shader">Vertex shader</h3>
<p>The job of the vertex shader is to compute the positions of the vertices. With this result (<strong>gl_Position</strong>) the GPU locates points, lines and triangles on the viewport.</p>
<p>To write the triangle, we are going to create this vertex shader:</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">const</span> vertexShader = <span class="hljs-string">`#version 300 es
  precision mediump float;
  in vec2 position;

  void main () {
      gl_Position = vec4(position.x, position.y, 0.0, 1.0); // x,y,z,w
  }
`</span>
</code></pre><p>We can save it for now in our JavaScript code as a template string.</p>
<p>The first line (<code>#version 300 es</code>) tells the version of GLSL we are using.</p>
<p>The second line (<code>precision mediump float;</code>) determines how much precision the GPU uses to calculate floats. The available options are <code>highp</code>, <code>mediump</code> and <code>lowp</code>), however, some systems don&#39;t support <code>highp</code>.</p>
<p>In the third line (<code>in vec2 position;</code>) we define an input variable for the GPU of 2 dimensions <strong>(X, Y)</strong>. Each vector of the triangle is in two dimensions.</p>
<p>The <code>main</code> function is called at program startup after initialization (like in C / C++). The GPU is going to run its content (<code>gl_Position = vec4(position.x, position.y, 0.0, 1.0);</code>) by saving to the <code>gl_Position</code> the position of the current vertex. The first and second argument are <code>x</code> and <code>y</code> from our <code>vec2</code> position. The third argument is the <code>z</code> axis, in this case is <code>0.0</code> because we are creating a geometry in 2D, not 3D. The last argument is <code>w</code>, by default this should be set to <code>1.0</code>.</p>
<p>The GLSL identifies and uses internally the value of <code>gl_Position</code>.</p>
<p>Once we create the shader, we should compile it:</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">const</span> vs = gl.<span class="hljs-title function_">createShader</span>(gl.<span class="hljs-property">VERTEX_SHADER</span>)

gl.<span class="hljs-title function_">shaderSource</span>(vs, vertexShader)
gl.<span class="hljs-title function_">compileShader</span>(vs)

<span class="hljs-comment">// Catch some possible errors on vertex shader</span>
<span class="hljs-keyword">if</span> (!gl.<span class="hljs-title function_">getShaderParameter</span>(vs, gl.<span class="hljs-property">COMPILE_STATUS</span>)) {
  <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">error</span>(gl.<span class="hljs-title function_">getShaderInfoLog</span>(vs))
}
</code></pre><h3 id="fragment-shader">Fragment shader</h3>
<p>After the &quot;vertex shader&quot;, the &quot;fragment shader&quot; is executed. The job of this shader is to compute the color of each pixel corresponding to each location.</p>
<p>For the triangle, let&#39;s fill with the same color:</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">const</span> fragmentShader = <span class="hljs-string">`#version 300 es
  precision mediump float;
  out vec4 color;

  void main () {
      color = vec4(0.7, 0.89, 0.98, 1.0); // r,g,b,a
  }
`</span>
<span class="hljs-keyword">const</span> fs = gl.<span class="hljs-title function_">createShader</span>(gl.<span class="hljs-property">FRAGMENT_SHADER</span>)

gl.<span class="hljs-title function_">shaderSource</span>(fs, fragmentShader)
gl.<span class="hljs-title function_">compileShader</span>(fs)

<span class="hljs-comment">// Catch some possible errors on fragment shader</span>
<span class="hljs-keyword">if</span> (!gl.<span class="hljs-title function_">getShaderParameter</span>(fs, gl.<span class="hljs-property">COMPILE_STATUS</span>)) {
  <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">error</span>(gl.<span class="hljs-title function_">getShaderInfoLog</span>(fs))
}
</code></pre><p>The syntax is very similar to the previous one, although the <code>vect4</code> we return here refers to the color of each pixel. Since we want to fill the triangle with <code>rgba(179, 229, 252, 1)</code>, we&#39;ll translate it by dividing each RGB number by 255.</p>
<h2 id="create-program-from-shaders">Create program from shaders</h2>
<p>Once we have the shaders compiled, we need to create the program that will run the GPU, adding both shaders.</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">const</span> program = gl.<span class="hljs-title function_">createProgram</span>()
gl.<span class="hljs-title function_">attachShader</span>(program, vs) <span class="hljs-comment">// Attatch vertex shader</span>
gl.<span class="hljs-title function_">attachShader</span>(program, fs) <span class="hljs-comment">// Attatch fragment shader</span>
gl.<span class="hljs-title function_">linkProgram</span>(program) <span class="hljs-comment">// Link both shaders together</span>
gl.<span class="hljs-title function_">useProgram</span>(program) <span class="hljs-comment">// Use the created program</span>

<span class="hljs-comment">// Catch some possible errors on program</span>
<span class="hljs-keyword">if</span> (!gl.<span class="hljs-title function_">getProgramParameter</span>(program, gl.<span class="hljs-property">LINK_STATUS</span>)) {
  <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">error</span>(gl.<span class="hljs-title function_">getProgramInfoLog</span>(program))
}
</code></pre><h2 id="create-buffers">Create buffers</h2>
<p>We are going to use a buffer to allocate memory to GPU, and bind this memory to a channel for CPU-GPU communications. We are going to use this channel to send <a href="#vertex-coordinates">our triangle coordinates</a> to the GPU.</p>
<pre><code class="hljs language-js"><span class="hljs-comment">// allowcate memory to gpu</span>
<span class="hljs-keyword">const</span> buffer = gl.<span class="hljs-title function_">createBuffer</span>()

<span class="hljs-comment">// bind this memory to a channel</span>
gl.<span class="hljs-title function_">bindBuffer</span>(gl.<span class="hljs-property">ARRAY_BUFFER</span>, buffer)

<span class="hljs-comment">// use this channel to send data to the GPU (our triangle coordinates)</span>
gl.<span class="hljs-title function_">bufferData</span>(
  gl.<span class="hljs-property">ARRAY_BUFFER</span>,
  <span class="hljs-keyword">new</span> <span class="hljs-title class_">Float32Array</span>(coordinates),
  <span class="hljs-comment">// In our case is a static triangle, so it&#x27;s better to tell</span>
  <span class="hljs-comment">// how are we going to use the data so the WebGL can optimize</span>
  <span class="hljs-comment">// certain things.</span>
  gl.<span class="hljs-property">STATIC_DRAW</span>
)

<span class="hljs-comment">// desallocate memory after send data to avoid memory leak issues</span>
gl.<span class="hljs-title function_">bindBuffer</span>(gl.<span class="hljs-property">ARRAY_BUFFER</span>, <span class="hljs-literal">null</span>)
</code></pre><img src="https://aralroca.com/images/blog-images/buffer.png" class="center" alt="buffer" />

<h2 id="link-data-from-cpu-to-gpu">Link data from CPU to GPU</h2>
<p>In our <a href="#vertex-shader">vertex shader</a>, we defined an input variable named <code>position</code>. However, we haven&#39;t yet specified that this variable should take the value that we are passing through the buffer. We must indicate it in the following way:</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">const</span> position = gl.<span class="hljs-title function_">getAttribLocation</span>(program, <span class="hljs-string">&#x27;position&#x27;</span>)
gl.<span class="hljs-title function_">enableVertexAttribArray</span>(position)
gl.<span class="hljs-title function_">bindBuffer</span>(gl.<span class="hljs-property">ARRAY_BUFFER</span>, buffer)
gl.<span class="hljs-title function_">vertexAttribPointer</span>(
  position, <span class="hljs-comment">// Location of the vertex attribute</span>
  <span class="hljs-number">2</span>, <span class="hljs-comment">// Dimension - 2D</span>
  gl.<span class="hljs-property">FLOAT</span>, <span class="hljs-comment">// Type of data we are going to send to GPU</span>
  gl.<span class="hljs-property">FALSE</span>, <span class="hljs-comment">// If data should be normalized</span>
  <span class="hljs-number">0</span>, <span class="hljs-comment">// Stride</span>
  <span class="hljs-number">0</span> <span class="hljs-comment">// Offset</span>
)
</code></pre><h2 id="drawing-the-triangle">Drawing the triangle</h2>
<p>Once we have created the program with the shaders for our triangle and created the linked buffer to send data from the CPU to the GPU, we can finally tell the GPU to render the triangle!</p>
<img src="https://aralroca.com/images/blog-images/triangle_result.png" alt="Triangle exercice" class="center" />

<pre><code class="hljs language-js">gl.<span class="hljs-title function_">drawArrays</span>(
  gl.<span class="hljs-property">TRIANGLES</span>, <span class="hljs-comment">// Type of primitive</span>
  <span class="hljs-number">0</span>, <span class="hljs-comment">// Start index in the array of vector points</span>
  <span class="hljs-number">3</span> <span class="hljs-comment">// Number of vertices to be rendered</span>
)
</code></pre><p>This method renders primitives from array data. The primitives are points, lines or triangles. Let&#39;s specify <code>gl.TRIANGLES</code>.</p>
<h2 id="all-the-code-together">All the code together</h2>
<p>I&#39;ve uploaded the article code to CodeSandbox in case you want to explore it.</p>
<iframe
  src="https://codesandbox.io/embed/webgl-triangle-e90o1?fontsize=14&hidenavigation=0&theme=dark"
  style="width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;"
  title="webgl-triangle"
  allow="accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking"
  sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"
></iframe>

<h2 id="conclusion">Conclusion</h2>
<p>With WebGL it is only possible to draw triangles, lines or points because it only rasterizes, so you can only do what the vectors can do. This means that WebGL is conceptually simple, while the process is quite complex... And gets more and more complex depending on what you want to develop. It&#39;s not the same to rasterize a 2D triangle than a 3D videogame with textures, varyings, transformations...</p>
<p>I hope this article has been useful to understand a little bit of how WebGL works. I recommend a reading of the references below.</p>
<h2 id="references">References</h2>
<ul>
<li><a href="https://webglfundamentals.org">https://webglfundamentals.org</a></li>
<li><a href="https://webgl2fundamentals.org/">https://webgl2fundamentals.org/</a></li>
<li><a href="https://developer.mozilla.org/es/docs/Web/API/WebGL_API/Tutorial/">https://developer.mozilla.org/es/docs/Web/API/WebGL_API/Tutorial/</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/WebGL_best_practices">https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/WebGL_best_practices</a></li>
<li><a href="http://vispy.org/modern-gl.html">http://vispy.org/modern-gl.html</a></li>
<li><a href="https://github.com/subhasishdash/webglinternals">https://github.com/subhasishdash/webglinternals</a></li>
</ul>
]]></content:encoded>
            </item>
            <item>
              <title>First steps with WebAssembly in Rust</title>
              <description>Discover how to start with WebAssembly in an easy way with Rust.</description>
              <link>https://aralroca.com/blog/first-steps-webassembly-rust</link>
              <guid isPermaLink="false">https://aralroca.com/blog/first-steps-webassembly-rust/</guid>
              <pubDate>Mon Aug 17 2020 00:00:00 GMT+0000 (Coordinated Universal Time)</pubDate>
              <content:encoded><![CDATA[<p>We&#39;ll see how to <strong>run native code in the browser</strong>, doing faster web applications, being able to reuse old code like retro videogames, and at the same time learning the future of web development.</p>
<p><strong>We&#39;ll cover the following:</strong></p>
<ul>
<li><a href="#what-is-webassembly">What is WebAssembly?</a></li>
<li><a href="#why-in-rust">Why in Rust?</a></li>
<li><a href="#execute-rust-code-from-javascript">Execute Rust code from JavaScript</a><ul>
<li><a href="#rust-code">Rust code</a></li>
<li><a href="#compilation">Compilation</a></li>
<li><a href="#use-the-compiled-code-on-our-js-project">Use the compiled code on our JS project</a></li>
</ul>
</li>
<li><a href="#execute-javascript-code-from-rust">Execute JavaScript code from Rust
</a></li>
<li><a href="#performance---javascript-vs-rust">Performance - JavaScript vs Rust
</a></li>
<li><a href="#debugging">Debugging</a></li>
<li><a href="#publishing-to-npm">Publishing to NPM</a></li>
<li><a href="#code-from-the-article">Code from the article</a></li>
<li><a href="#conclusions">Conclusions</a></li>
<li><a href="#references">References</a></li>
</ul>
<h2 id="what-is-webassembly">What is WebAssembly?</h2>
<p>In all current browsers, there is a JavaScript engine that interprets and executes the code. This has allowed us to implement very rich web applications because JavaScript is getting better and more complete every day. However, it&#39;s a high-level language but still not ideal for some tasks because it has <strong>not been developed to be a fast language</strong> with a lot of performance.</p>
<p>WebAssembly (<strong>WASM</strong>) is a new portable <strong>binary-code format</strong> that can be executed in modern browsers. It is complemented with a <strong>text format</strong> (<strong>WAT</strong>) to make it more <strong>readable/debuggable</strong> for us, in addition, to allow us to code directly in a kind of &quot;assembly&quot; code. It&#39;s an open <a href="https://www.w3.org/TR/wasm-core-1/">W3C standard</a> still in progress that allows us to write <strong>fast and efficient</strong> code for the web in other languages than JavaScript and it runs with a <strong>similar performance to the native language</strong>. It&#39;s not here to replace JavaScript, but to complement it.</p>
<figure align="center">
  <img src="https://aralroca.com/images/blog-images/js-engine.png" alt="JavaScript engine" class="center transparent" />
  <figcaption><small>JavaScript and WASM engine</small></figcaption>
</figure>

<p>Another purpose of WebAssembly is to keep the web <strong>secure</strong>, light and fast, keeping a <strong>small</strong> <code>.wasm</code> <strong>file size</strong> and always maintaining <strong>backwards-compatibility</strong> in new WASM features, so the web doesn&#39;t break.</p>
<p>There are more than <a href="https://github.com/appcypher/awesome-wasm-langs">40 supported languages</a> for WebAssembly, the most common are C, C++, and Rust for their performance and maturity, although you also can write code for WASM with high-level languages like Python, PHP or even JavaScript!</p>
<p>Some <strong>practical uses</strong> of WebAssembly:</p>
<ul>
<li>Encryption</li>
<li>Games that require a lot of assets</li>
<li>Image and video editing</li>
<li>P2P</li>
<li>High-performance algorithms</li>
<li>VR, AR</li>
<li>Visualizations and simulations</li>
<li>A big etc...</li>
</ul>
<figure align="center">
  <img src="https://aralroca.com/images/blog-images/vr.jpg" alt="Virtual Reallity" class="center" />
  <figcaption><small>Photo by XR Expo on Unsplash</small></figcaption>
</figure>

<h2 id="why-in-rust">Why in Rust?</h2>
<p>Perhaps you wonder why choose <a href="https://www.rust-lang.org/">Rust</a>, when we have so many languages available with WebAssembly. There are several reasons for that:</p>
<ul>
<li><strong>Performance</strong>: Rust is free from the non-deterministic garbage collection and it gives to programmers the control over indirection, monomorphization, and memory layout.</li>
<li><strong>Small <code>.wasm</code> sizes</strong>: Rust lacks a runtime, enabling small <code>.wasm</code> size because there is no extra bloat included like a garbage collector. Hence you only pay in code size, for these functions that you&#39;re using.</li>
<li><strong>Integration</strong>: Rust and Webassembly integrates with existing JavaScript tooling (npm, Webpack...).</li>
</ul>
<figure align="center">
  <img src="https://aralroca.com/images/blog-images/rust-performance.jpeg" alt="Rust performance" class="center" />
  <figcaption><small>Rust performance</small></figcaption>
</figure>

<h2 id="execute-rust-code-from-javascript">Execute Rust code from JavaScript</h2>
<p>Assuming you have both <a href="https://www.npmjs.com/">NPM</a> (for JS) and <a href="https://doc.rust-lang.org/cargo/">Cargo</a> (for Rust), another prerequisite we need to install it is <a href="https://github.com/rustwasm/wasm-pack">wasm-pack</a>:</p>
<pre><code>&gt; cargo install wasm-pack
</code></pre><h3 id="rust-code">Rust code</h3>
<p>Let&#39;s create a new Rust project for the &quot;Hello world&quot;:</p>
<pre><code>&gt; cargo new helloworld --lib
</code></pre><p>On <code>Cargo.toml</code> we are going to add the next:</p>
<pre><code class="hljs language-toml"><span class="hljs-section">[package]</span>
<span class="hljs-attr">name</span> = <span class="hljs-string">&quot;helloworld&quot;</span>
<span class="hljs-attr">version</span> = <span class="hljs-string">&quot;0.1.0&quot;</span>
<span class="hljs-attr">authors</span> = [<span class="hljs-string">&quot;Aral Roca Gomez &lt;contact@aralroca.com&gt;&quot;</span>]
<span class="hljs-attr">edition</span> = <span class="hljs-string">&quot;2018&quot;</span>

<span class="hljs-comment">## new things...</span>
<span class="hljs-section">[lib]</span>
<span class="hljs-attr">crate-type</span> = [<span class="hljs-string">&quot;cdylib&quot;</span>]

<span class="hljs-section">[dependencies]</span>
<span class="hljs-attr">wasm-bindgen</span> = <span class="hljs-string">&quot;0.2.67&quot;</span>

<span class="hljs-section">[package.metadata.wasm-pack.profile.release]</span>
<span class="hljs-attr">wasm-opt</span> = [<span class="hljs-string">&quot;-Oz&quot;</span>, <span class="hljs-string">&quot;--enable-mutable-globals&quot;</span>]
</code></pre><ul>
<li><code>cdylib</code> lib for <code>wasm</code> final artifacts.</li>
<li><a href="https://github.com/rustwasm/wasm-bindgen">wasm-bindgen</a> dependency to facilitate high-level interactions between Wasm modules and JavaScript.</li>
</ul>
<blockquote>
<p><strong>Note</strong>: The latest part about <code>--enable-mutable-globals</code> in principle, in upcoming <code>wasm-bindgen</code> releases, should not be needed but for this tutorial it&#39;s necessary. Otherwise <a href="https://github.com/rustwasm/wasm-pack/issues/886#issuecomment-667669802">we can not work with Strings</a>.</p>
</blockquote>
<p>WebAssembly only supports the i32, u32, i64, and u64 types. If you want to work with other types, such as String or Objects, you normally must first encode them. However, <strong>wasm-bindgen</strong> does these bindings for us. There&#39;s no need to worry about it anymore. That said, let&#39;s create our <code>helloworld</code> function to return a String in <code>src/lib.rs</code>:</p>
<pre><code class="hljs language-rs"><span class="hljs-keyword">use</span> wasm_bindgen::prelude::*;

<span class="hljs-meta">#[wasm_bindgen]</span>
<span class="hljs-keyword">pub</span> <span class="hljs-keyword">fn</span> <span class="hljs-title function_">helloworld</span>() <span class="hljs-punctuation">-&gt;</span> <span class="hljs-type">String</span> {
    <span class="hljs-type">String</span>::<span class="hljs-title function_ invoke__">from</span>(<span class="hljs-string">&quot;Hello world from Rust!&quot;</span>)
}
</code></pre><h3 id="compilation">Compilation</h3>
<p>Let&#39;s compile Rust&#39;s code with:</p>
<pre><code>&gt; wasm-pack build --target web
</code></pre><p>We are using the web target, however, there are different targets we can use depending on how we want to use that <code>wasm</code> file:</p>
<ul>
<li><strong>--target bundler</strong> - for bundlers like Webpack, Parcel, or Rollup.</li>
<li><strong>--target web</strong> - for the web as ECMAScript module.</li>
<li><strong>--target no-modules</strong> - for the web without ECMAScript module.</li>
<li><strong>--target nodejs</strong> - for Node.js</li>
</ul>
<p>After executing the above command, a <code>pkg</code> directory will have been created with our JavaScript library containing the code we have made in Rust! It even generates the &quot;types&quot; files of TypeScript.</p>
<pre><code class="hljs language-bh">&gt; ls -l pkg
total 72
-rw-r--r--  1 aralroca  staff    929 Aug 15 13:38 helloworld.d.ts
-rw-r--r--  1 aralroca  staff   3210 Aug 15 13:38 helloworld.js
-rw-r--r--  1 aralroca  staff    313 Aug 15 13:38 helloworld.wasm
-rw-r--r--  1 aralroca  staff    268 Aug 15 13:38 helloworld_bg.d.ts
-rw-r--r--  1 aralroca  staff  15160 Aug 15 13:38 helloworld_bg.wasm
-rw-r--r--  1 aralroca  staff    289 Aug 15 13:38 package.json
</code></pre><p>Now it&#39;s ready as a JavaScript package so we can use it in our project or even upload the package to NPM as we can see later.</p>
<p>The <code>.js</code> file contains the necessary &quot;glue&quot; code to not have to worry about working outside the <code>pkg</code> with buffers, text decoders, etc.</p>
<h3 id="use-the-compiled-code-on-our-js-project">Use the compiled code on our JS project</h3>
<p>In order to use the <code>wasm</code> file in our JavaScript, we can import the generated <code>pkg</code> module to our project. To test it, we can create an <code>index.html</code> on the root of the Rust project with this:</p>
<pre><code class="hljs language-html"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">&quot;UTF-8&quot;</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>&quot;Hello world&quot; in Rust + Webassembly<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">type</span>=<span class="hljs-string">&quot;module&quot;</span>&gt;</span><span class="language-javascript">
      <span class="hljs-keyword">import</span> init, { helloworld } <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;./pkg/helloworld.js&#x27;</span>

      <span class="hljs-keyword">async</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">run</span>(<span class="hljs-params"></span>) {
        <span class="hljs-keyword">await</span> <span class="hljs-title function_">init</span>()
        <span class="hljs-variable language_">document</span>.<span class="hljs-property">body</span>.<span class="hljs-property">textContent</span> = <span class="hljs-title function_">helloworld</span>()
      }

      <span class="hljs-title function_">run</span>()
    </span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>

  <span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre><p>As you can see, before using the <code>helloworld</code> function it&#39;s important to call the asynchronous <code>init</code> function in order to load the <code>wasm</code> file. Then, we can use the public Rust functions more easily!</p>
<p>To test it, you can do <code>npx serve .</code> and open <code>http://localhost:5000</code>.</p>
<figure align="center">
  <img src="https://aralroca.com/images/blog-images/helloworld-result-rust-wasm.png" class="center" alt="Result of hello world in Rust and WASM" />
  <figcaption><small>Result of hello world in Rust and WASM</small></figcaption>
</figure>

<h2 id="execute-javascript-code-from-rust">Execute JavaScript code from Rust</h2>
<p>It is possible to use JavaScript code within Rust, for example, to use <code>window</code> variables, write in the DOM or call internal functions such as <code>console.log</code>. All we have to do is to declare the JavaScript bindings we want to use inside <code>extern &quot;C&quot;</code>.</p>
<p>As an example we are going to use the function <code>console.log</code> inside Rust:</p>
<pre><code class="hljs language-rs"><span class="hljs-keyword">use</span> wasm_bindgen::prelude::*;

<span class="hljs-meta">#[wasm_bindgen]</span>
<span class="hljs-keyword">extern</span> <span class="hljs-string">&quot;C&quot;</span> {
    <span class="hljs-meta">#[wasm_bindgen(js_namespace = console)]</span>
    <span class="hljs-keyword">fn</span> <span class="hljs-title function_">log</span>(s: &amp;<span class="hljs-type">str</span>);
}

<span class="hljs-meta">#[wasm_bindgen]</span>
<span class="hljs-keyword">pub</span> <span class="hljs-keyword">fn</span> <span class="hljs-title function_">example</span>() {
    <span class="hljs-title function_ invoke__">log</span>(<span class="hljs-string">&quot;Log from rust&quot;</span>);
}
</code></pre><p>As we can see, inside the <code>extern &quot;C&quot;</code> we have to indicate the <code>js_namespace</code> (console) declaring the function that we&#39;ll use inside the namespace (log). In this case, we&#39;ve put only one string as a parameter but if we wanted to execute a <code>console.log</code> with multiple parameters they would have to be declared.</p>
<p>And in our JS:</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">import</span> init, { example } <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;./pkg/helloworld.js&#x27;</span>

<span class="hljs-keyword">async</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">run</span>(<span class="hljs-params"></span>) {
  <span class="hljs-keyword">await</span> <span class="hljs-title function_">init</span>()
  <span class="hljs-title function_">example</span>() <span class="hljs-comment">// This will log &quot;Log from rust&quot; to the console</span>
}

<span class="hljs-title function_">run</span>()
</code></pre><h2 id="performance---javascript-vs-rust">Performance - JavaScript vs Rust</h2>
<p>Let&#39;s do a comparison of a slightly more expensive function, such as the <a href="https://en.wikipedia.org/wiki/Fibonacci_sequence">fibonacci</a> function, to see how it performs in both Rust and JavaScript:</p>
<pre><code class="hljs language-rs"><span class="hljs-keyword">use</span> wasm_bindgen::prelude::*;

<span class="hljs-meta">#[wasm_bindgen]</span>
<span class="hljs-keyword">pub</span> <span class="hljs-keyword">fn</span> <span class="hljs-title function_">fibonacci</span>(n: <span class="hljs-type">u32</span>) <span class="hljs-punctuation">-&gt;</span> <span class="hljs-type">u32</span> {
    <span class="hljs-keyword">match</span> n {
        <span class="hljs-number">0</span> | <span class="hljs-number">1</span> =&gt; n,
        _ =&gt; <span class="hljs-title function_ invoke__">fibonacci</span>(n - <span class="hljs-number">1</span>) + <span class="hljs-title function_ invoke__">fibonacci</span>(n - <span class="hljs-number">2</span>),
    }
}
</code></pre><p>Using the <code>console.time</code> function we can measure the performance of each one:</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">import</span> init, { fibonacci } <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;./pkg/helloworld.js&#x27;</span>

<span class="hljs-keyword">function</span> <span class="hljs-title function_">fibonacciInJs</span>(<span class="hljs-params">n</span>) {
  <span class="hljs-keyword">if</span> (n &lt;= <span class="hljs-number">1</span>) <span class="hljs-keyword">return</span> n
  <span class="hljs-keyword">return</span> <span class="hljs-title function_">fibonacciInJs</span>(n - <span class="hljs-number">1</span>) + <span class="hljs-title function_">fibonacciInJs</span>(n - <span class="hljs-number">2</span>)
}

<span class="hljs-keyword">async</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">run</span>(<span class="hljs-params"></span>) {
  <span class="hljs-keyword">await</span> <span class="hljs-title function_">init</span>()
  <span class="hljs-keyword">const</span> num = <span class="hljs-number">20</span>

  <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">time</span>(<span class="hljs-string">&#x27;Fibonnaci in rust&#x27;</span>)
  <span class="hljs-keyword">const</span> fibRust = <span class="hljs-title function_">fibonacci</span>(num)
  <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">timeEnd</span>(<span class="hljs-string">&#x27;Fibonnaci in rust&#x27;</span>)

  <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">time</span>(<span class="hljs-string">&#x27;Fibonnaci in JS&#x27;</span>)
  <span class="hljs-keyword">const</span> fibJS = <span class="hljs-title function_">fibonacciInJs</span>(num)
  <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">timeEnd</span>(<span class="hljs-string">&#x27;Fibonnaci in JS&#x27;</span>)

  <span class="hljs-variable language_">document</span>.<span class="hljs-property">body</span>.<span class="hljs-property">textContent</span> = <span class="hljs-string">`Fib <span class="hljs-subst">${num}</span>:  Rust <span class="hljs-subst">${fibRust}</span> - JS <span class="hljs-subst">${fibJS}</span>`</span>
}

<span class="hljs-title function_">run</span>()
</code></pre><p>And the result:</p>
<figure align="center">
  <img src="https://aralroca.com/images/blog-images/fibonacci-rust-vs-js.png" alt="Performance of fibonnaci function Rust vs JavaScript" class="center" />
  <figcaption><small>Performance of fibonnaci function Rust vs JavaScript</small></figcaption>
</figure>

<ul>
<li>In Rust: 0.13ms</li>
<li>In JS: 1.28ms</li>
</ul>
<p>Around <strong>x10 times faster</strong> in Rust than in JS!</p>
<p>However, it&#39;s important to note that not all functions we implement in Rust will be faster than in JavaScript. But there will be a considerable improvement in many of them that require recursion or loops.</p>
<h2 id="debugging">Debugging</h2>
<p>If in <code>devtools -&gt; source</code> we look inside our files for our <code>.wasm</code> file, we&#39;ll see that instead of binary it shows us the WAT file being more readable and debuggable.</p>
<figure align="center">
  <img src="https://aralroca.com/images/blog-images/debug-wasm.jpg" alt="Debug WASM in Chrome dev tools" class="center" />
  <figcaption><small>Debug WASM in Chrome dev tools</small></figcaption>
</figure>

<p>For a better debugging experience, you can use the <code>--debug</code> flag to display the names of the functions you have used in Rust.</p>
<pre><code>&gt; wasm-pack build --target web --debug
</code></pre><p>For now, with <code>wasm-bindgen</code> it&#39;s not possible to use source-maps to display the code in Rust on devtools. But I suppose in the future it will be available.</p>
<h2 id="publishing-to-npm">Publishing to NPM</h2>
<p>Once we have our pkg directory generated, we can package it with:</p>
<pre><code>&gt;  wasm-pack pack myproject/pkg
</code></pre><p>And publish it at npm with:</p>
<pre><code>&gt; wasm-pack publish
</code></pre><p>They work the same way as with <code>npm pack</code> and <code>npm publish</code>, so we could use the same flags as <code>wasm-pack publish --tag next</code>.</p>
<h2 id="code-from-the-article">Code from the article</h2>
<p>I&#39;ve uploaded the code used in this article to my GitHub:</p>
<ul>
<li><a href="https://github.com/aralroca/helloworld-wasm-rust">https://github.com/aralroca/helloworld-wasm-rust</a></li>
</ul>
<h2 id="conclusions">Conclusions</h2>
<p>In this article, we&#39;ve seen a bit of what WebAssembly is and what is necessary to start creating web applications with Rust.</p>
<p>We have used Rust because is one of the best integrated but it&#39;s possible to use many other languages. This way, we can bring back to life old applications made with languages like C or C++, and implement more futuristic and portable applications for VR or AR. All this thanks to the browser!</p>
<h2 id="references">References</h2>
<ul>
<li><a href="https://www.rust-lang.org/">https://www.rust-lang.org/</a></li>
<li><a href="https://rustwasm.github.io/docs/wasm-pack/">https://rustwasm.github.io/docs/wasm-pack/</a></li>
<li><a href="https://rustwasm.github.io/book/why-rust-and-webassembly.html">https://rustwasm.github.io/book/why-rust-and-webassembly.html</a></li>
<li><a href="https://blog.logrocket.com/webassembly-how-and-why-559b7f96cd71/#:~:text=What%20WebAssembly%20enables%20you%20to,JavaScript%2C%20it%20works%20alongside%20JavaScript">https://blog.logrocket.com/webassembly-how-and-why-559b7f96cd71/#:~:text=What%20WebAssembly%20enables%20you%20to,JavaScript%2C%20it%20works%20alongside%20JavaScript</a>.</li>
</ul>
]]></content:encoded>
            </item>
            <item>
              <title>First steps with TensorFlow.js</title>
              <description>Learn how to start with TensorFlow.js</description>
              <link>https://aralroca.com/blog/first-steps-with-tensorflowjs</link>
              <guid isPermaLink="false">https://aralroca.com/blog/first-steps-with-tensorflowjs/</guid>
              <pubDate>Fri Aug 24 2018 00:00:00 GMT+0000 (Coordinated Universal Time)</pubDate>
              <content:encoded><![CDATA[<p>I would like to do more articles explaining a little bit about all the machine learning and deep learning basics. I&#39;m a beginner in this area, but I&#39;d like to explain soon these concepts to create some interesting AI models. Nevertheless, we don&#39;t need a deep knowledge about machine learning to use some existing models. We can use some libraries like Keras, Tensorflow or TensorFlow.js. We are going to see here how to create basic AI models and use more sophisticated models with TensorFlow.js. Although it&#39;s not required a deep knowledge, we are going to explain few concepts.</p>
<h2 id="what-is-a-model">What is a Model?</h2>
<p>Or maybe a better question would be: &#39;What is the reality?&#39;. Yes, that&#39;s quite complex to answer... We need to simplify it in order to understand it! A way to represent a part of this simplified &quot;reality&quot;  is using a model. So; there are infinity kind of models: world maps, diagrams, etc.</p>
<img src="https://aralroca.com/images/blog-images/11.jpg" alt="model" class="center">
 
<p> It&#39;s easier to understand the models that we can use without machine help. For example, if we want to do a model to represent the price of Barcelona houses regarding the size of the house: First, we can collect some data:</p>
<table style="margin: 0 auto;">

<tbody>

<tr>

<th style="text-align:center;padding:10px;font-weight:400;">Number of rooms</th>

<th style="text-align:center;padding:10px;font-weight:400;">Prices</th>

</tr>

<tr>

<th style="text-align:center;font-weight:100;">3</th>

<th style="text-align:center;padding:10px;font-weight:100;">131.000€</th>

</tr>

<tr>

<th style="text-align:center;padding:10px;font-weight:100;">3</th>

<th style="text-align:center;padding:10px;font-weight:100;">125.000€</th>

</tr>

<tr>

<th style="text-align:center;padding:10px;font-weight:100;">4</th>

<th style="text-align:center;padding:10px;font-weight:100;">235.000€</th>

</tr>

<tr>

<th style="text-align:center;padding:10px;font-weight:100;">4</th>

<th style="text-align:center;padding:10px;font-weight:100;">265.000€</th>

</tr>

<tr>

<th style="text-align:center;padding:10px;font-weight:100;">5</th>

<th style="text-align:center;padding:10px;font-weight:100;">535.000€</th>

</tr>

</tbody>

</table>

<p>Then, we display this data on a 2D graph, 1 dimension for each param (price, rooms):</p>
<img class="center" src="https://aralroca.com/images/blog-images/12.gif" alt="Linear regressor explanation" />

<p>And... voilà! We can now draw a line and start predicting some prices of houses with 6, 7 or more rooms. This model is named linear regression and it&#39;s one of the most simple models to start in the machine learning world. Of course this model is not good enough:</p>
<ol>
<li>There are only 5 examples so it&#39;s not reliable enough.</li>
<li>There are only 2 params (price, rooms), yet there are more factors that could have an effect on the price: district, the age of the house, etc.</li>
</ol>
<p>For the first problem, we can deal with it by adding more examples, e. g. 1.000.000 examples instead of 5. For the second problem, we can add more dimensions... right? With 2D chart we can understand the data and draw a line while in 3D dimensions we could also use a plane:</p>
<img class="center" src="https://aralroca.com/images/blog-images/13.jpeg" alt="plane" />

<p>But, how to deal with more than 3D? 4D or 1000000D? Our mind can&#39;t visualize this on a chart but... good news! We can use maths and calculate hyperplanes in more than 3D and neural networks are a great tool for this! <em>By the way, I have good news for you; using TensorFlow.js you don&#39;t need to be a math expert.</em></p>
<h2 id="what-is-a-neural-network">What is a neural network?</h2>
<p>Before understanding what is a neural network, we need to know what is a neuron. A neuron, in the real world looks similar to this:</p>
<img class="center" src="https://aralroca.com/images/blog-images/14.gif" alt="neuron" />

<p>The most important parts of a neuron are:</p>
<ul>
<li><strong>Dendrites</strong>: It&#39;s the input of the data.</li>
<li><strong>Axon</strong>: It&#39;s the output.</li>
<li><strong>Synapse</strong> (not in the image): It&#39;s the structure that permits a neuron to communicate with another neuron. It is responsible to pass electric signals between the nerve ending of the axon and a dendrite of a near neuron. These synapses are the key to learn because they increase or decrease the electrical activity depending on the usage.</li>
</ul>
<p>A neuron in machine learning (simplified):</p>
<img class="center" src="https://aralroca.com/images/blog-images/15.jpg" alt="neuron in machine learning" />
<br />

<ul>
<li><strong>Inputs</strong>: The parameters of the input.</li>
<li><strong>Weights</strong>: Like synapses, their activity increase or decrease to adjust the neuron in order to establish a better linear regression.</li>
<li><strong>Linear function</strong>: Each neuron is like a linear regression function so for a linear regression model we only need one neuron!</li>
<li><strong>Activation function</strong>: We can apply some activation function to change the output from a scalar to another non-linear function. The more common; sigmoid, RELU and tanh.</li>
<li><strong>Output</strong>: The computed output after applying the activation function.</li>
</ul>
<p>The usage of an activation function is very useful, it&#39;s the power of a neural network. Without any activation function it&#39;s not possible to have a smart neuron network. The reason is that although you have multiple neurons in your network, the output of the neural network is always going to be a linear regression. We need some mechanism to deform this individual linear regressions to be non-linear to solve the non-linear problems. Thanks to activation functions we can transform these linear functions to non-linear functions:</p>
<img class="center" src="https://aralroca.com/images/blog-images/16.jpg" alt="Neural network in machine learning" />
<br />

<h2 id="training-a-model">Training a model</h2>
<p>Drawing a line in our chart, as in the 2D linear regression example, is enough for us to start predicting new data. Nevertheless, the idea of &quot;deep learning&quot; is that our neural network learn to write this line. For a simple line we can use a very simple neural network with only one neuron, but for another models maybe we want to do more complex things like classify two groups of data. In this case, the &quot;training&quot; is going to learn how to draw something like this:</p>
<img class="center" src="https://aralroca.com/images/blog-images/17.png" alt="classification problem" />

<p>Remember that this is not complex because it&#39;s in 2D. Every model is a world, but the concept of &quot;training&quot; is very similar in all of them. The first step is drawing a random line, and improving it in a iteration algorithm, fixing the error in each iteration. This optimization algorithm has the name of Gradient Descent (there are more sophisticated algorithms as SGD or ADAM, with the same concept). In order to understand the Gradient Descent, we need to know that every algorithm (linear regressor, logistic regressor, etc.) has a different cost function to measure this error. The cost functions always converge in some point and can be convex and non-convex functions. The lowest converge point is found on the 0% error. Our aim is to achieve this point.</p>
<img class="center" src="https://aralroca.com/images/blog-images/18.png" alt="convex and non-convex functions" />

<p>When we work with the Gradient Descent algorithm, we start in some random point of this cost function but, we don&#39;t know where is it! Imagine that your are on the mountains, completely blind, and you need to walk down, step by step, to the lowest point. If the land is irregular (like non-convex functions), the descent is going to be more complex.</p>
<img class="center" src="https://aralroca.com/images/blog-images/19.jpg" alt="man walking on a montain" />

<p>I&#39;m not going to explain Gradient Descent algorithm deeply. Just remember that it&#39;s the optimization algorithm to train the AI models to minimize the error of predictions. This algorithm requires time and GPU for matrix multiplications. This converge point is usually hard to achieve in the first execution so we need to fix some hyperparameters like the learning rate (size of the step down the hill) or add some regularization. After the iterations of Gradient Descent we get a closer point to the converge point when the error is close to 0%. At this moment, we already have the model created and we are ready to start predicting!</p>
<img class="center" src="https://aralroca.com/images/blog-images/20.gif" alt="predicting" />
<br />

<h2 id="training-a-model-with-tensorflowjs">Training a model with TensorFlow.js</h2>
<p>TensorFlow.js provides us with an easy way to create neural networks. At first, we are going to create a LinearModel class with a method trainModel. For this kind of model we are going to use a sequential model. A sequential model is any model where the outputs of one layer are the inputs to the next layer, i.e. when the model topology is a simple &#39;stack&#39; of layers, with no branching or skipping. Inside the method trainModel we are going to define the layers (we are going to use only one because it&#39;s enough for a Linear Regression problem):</p>
<pre><code class="hljs language-jsx"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> tf <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;@tensorflow/tfjs&#x27;</span>;

<span class="hljs-comment">/**
* Linear model class
*/</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">LinearModel</span> {
  <span class="hljs-comment">/**
 * Train model
 */</span>
  <span class="hljs-keyword">async</span> <span class="hljs-title function_">trainModel</span>(<span class="hljs-params">xs, ys</span>){
    <span class="hljs-keyword">const</span> layers = tf.<span class="hljs-property">layers</span>.<span class="hljs-title function_">dense</span>({
      <span class="hljs-attr">units</span>: <span class="hljs-number">1</span>, <span class="hljs-comment">// Dimensionality of the output space</span>
      <span class="hljs-attr">inputShape</span>: [<span class="hljs-number">1</span>], <span class="hljs-comment">// Only one param</span>
    });
    <span class="hljs-keyword">const</span> lossAndOptimizer = {
      <span class="hljs-attr">loss</span>: <span class="hljs-string">&#x27;meanSquaredError&#x27;</span>,
      <span class="hljs-attr">optimizer</span>: <span class="hljs-string">&#x27;sgd&#x27;</span>, <span class="hljs-comment">// Stochastic gradient descent</span>
    };

    <span class="hljs-variable language_">this</span>.<span class="hljs-property">linearModel</span> = tf.<span class="hljs-title function_">sequential</span>();
    <span class="hljs-variable language_">this</span>.<span class="hljs-property">linearModel</span>.<span class="hljs-title function_">add</span>(layers); <span class="hljs-comment">// Add the layer</span>
    <span class="hljs-variable language_">this</span>.<span class="hljs-property">linearModel</span>.<span class="hljs-title function_">compile</span>(lossAndOptimizer);

    <span class="hljs-comment">// Start the model training!</span>
    <span class="hljs-keyword">await</span> <span class="hljs-variable language_">this</span>.<span class="hljs-property">linearModel</span>.<span class="hljs-title function_">fit</span>(
      tf.<span class="hljs-title function_">tensor1d</span>(xs),
      tf.<span class="hljs-title function_">tensor1d</span>(ys),
    );
  }

  ...more
}
</code></pre><p>To use this class:</p>
<pre><code class="hljs language-jsx"><span class="hljs-keyword">const</span> model = <span class="hljs-keyword">new</span> <span class="hljs-title class_">LinearModel</span>()

<span class="hljs-comment">// xs and ys -&gt; array of numbers (x-axis and y-axis)</span>
<span class="hljs-keyword">await</span> model.<span class="hljs-title function_">trainModel</span>(xs, ys)
</code></pre><p>After this training, we are ready to start predicting!</p>
<h2 id="predicting-with-tensorflowjs">Predicting with TensorFlow.js</h2>
<p>Predicting normally is the easier part! Training a model requires to define some hyperparameters... but still, predicting is so simple. We are going to write the next method into the LinearRegressor class:</p>
<pre><code class="hljs language-jsx"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> tf <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;@tensorflow/tfjs&#x27;</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">LinearModel</span> {
  ...trainingCode

  <span class="hljs-title function_">predict</span>(<span class="hljs-params">value</span>){
    <span class="hljs-keyword">return</span> <span class="hljs-title class_">Array</span>.<span class="hljs-title function_">from</span>(
      <span class="hljs-variable language_">this</span>.<span class="hljs-property">linearModel</span>
      .<span class="hljs-title function_">predict</span>(tf.<span class="hljs-title function_">tensor2d</span>([value], [<span class="hljs-number">1</span>, <span class="hljs-number">1</span>]))
      .<span class="hljs-title function_">dataSync</span>()
    )
  }
}
</code></pre><p>Now, we can use the prediction method in our code:</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">const</span> prediction = model.<span class="hljs-title function_">predict</span>(<span class="hljs-number">500</span>) <span class="hljs-comment">// Predict for the number 500</span>
<span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(prediction) <span class="hljs-comment">// =&gt; 420.423</span>
</code></pre><img class="center" src="https://aralroca.com/images/blog-images/21.gif" alt="Linear model" />

<p>You can play with the code here:</p>
<ul>
<li><a href="https://stackblitz.com/edit/linearmodel-tensorflowjs-react">https://stackblitz.com/edit/linearmodel-tensorflowjs-react</a></li>
</ul>
<h2 id="use-pre-trained-models-with-tensorflowjs">Use pre-trained models with TensorFlow.js</h2>
<p>Learning to create models is the most difficult part; normalizing the data for training, deciding all the hyperparams correctly,  etc.  If you are a beginner in this area (like me) and you want to play with some models, you can use pre-trained models. There are a lot of pre-trained models that you can use with TensorFlow.js. Moreover, you can import external models, created with TensorFlow or Keras. For example, you can use the <a href="https://github.com/tensorflow/tfjs-models/tree/master/posenet">posenet</a> model (Real-time human pose estimations) for funny projects:</p>
<img class="center" src="https://aralroca.com/images/blog-images/22.gif" alt="posenet" />

<p>📕 Code: <a href="https://github.com/aralroca/posenet-d3">https://github.com/aralroca/posenet-d3</a> It&#39;s very easy to use:</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> posenet <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;@tensorflow-models/posenet&#x27;</span>

<span class="hljs-comment">// Constants</span>
<span class="hljs-keyword">const</span> imageScaleFactor = <span class="hljs-number">0.5</span>
<span class="hljs-keyword">const</span> outputStride = <span class="hljs-number">16</span>
<span class="hljs-keyword">const</span> flipHorizontal = <span class="hljs-literal">true</span>
<span class="hljs-keyword">const</span> weight = <span class="hljs-number">0.5</span>

<span class="hljs-comment">// Load the model</span>
<span class="hljs-keyword">const</span> net = <span class="hljs-keyword">await</span> posenet.<span class="hljs-title function_">load</span>(weight)

<span class="hljs-comment">// Do predictions</span>
<span class="hljs-keyword">const</span> poses = <span class="hljs-keyword">await</span> net.<span class="hljs-title function_">estimateSinglePose</span>(
  imageElement,
  imageScaleFactor,
  flipHorizontal,
  outputStride
)
</code></pre><p><strong>poses</strong> variable is this JSON:</p>
<pre><code class="hljs language-json"><span class="hljs-punctuation">{</span>
  <span class="hljs-attr">&quot;score&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-number">0.32371445304906</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">&quot;keypoints&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span>
    <span class="hljs-punctuation">{</span>
      <span class="hljs-attr">&quot;position&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span>
        <span class="hljs-attr">&quot;y&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-number">76.291801452637</span><span class="hljs-punctuation">,</span>
        <span class="hljs-attr">&quot;x&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-number">253.36747741699</span>
      <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span>
      <span class="hljs-attr">&quot;part&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;nose&quot;</span><span class="hljs-punctuation">,</span>
      <span class="hljs-attr">&quot;score&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-number">0.99539834260941</span>
    <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span>
    <span class="hljs-punctuation">{</span>
      <span class="hljs-attr">&quot;position&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span>
        <span class="hljs-attr">&quot;y&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-number">71.10383605957</span><span class="hljs-punctuation">,</span>
        <span class="hljs-attr">&quot;x&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-number">253.54365539551</span>
      <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span>
      <span class="hljs-attr">&quot;part&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;leftEye&quot;</span><span class="hljs-punctuation">,</span>
      <span class="hljs-attr">&quot;score&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-number">0.98781454563141</span>
    <span class="hljs-punctuation">}</span>
    <span class="hljs-comment">// ...And for: rightEye, leftEar, rightEar, leftShoulder, rightShoulder</span>
    <span class="hljs-comment">// leftElbow, rightElbow, leftWrist, rightWrist, leftHip, rightHip,</span>
    <span class="hljs-comment">// leftKnee, rightKnee, leftAnkle, rightAnkle</span>
  <span class="hljs-punctuation">]</span>
<span class="hljs-punctuation">}</span>
</code></pre><p>Imagine how many funny projects you can develop only with this model!</p>
<img class="center" src="https://aralroca.com/images/blog-images/23.gif" alt="follow the fish example with posenet" />

<p>📕 Code: <a href="https://github.com/aralroca/fishFollow-posenet-tfjs">https://github.com/aralroca/fishFollow-posenet-tfjs</a></p>
<h2 id="importing-models-from-keras">Importing models from Keras</h2>
<p>We can import external models into TensorFlow.js. In this example, we are going to use a Keras model for number recognition (h5 file format). For this, we need the <a href="https://github.com/tensorflow/tfjs-converter"><strong>tfjs_converter</strong></a>.</p>
<pre><code>pip install tensorflowjs
</code></pre><p>Then, use the converter:</p>
<pre><code>tensorflowjs_converter --input_format keras keras/cnn.h5 src/assets
</code></pre><p>Finally, you are ready to import the model into your JS code!</p>
<pre><code class="hljs language-js"><span class="hljs-comment">// Load model</span>
<span class="hljs-keyword">const</span> model = <span class="hljs-keyword">await</span> tf.<span class="hljs-title function_">loadModel</span>(<span class="hljs-string">&#x27;./assets/model.json&#x27;</span>)

<span class="hljs-comment">// Prepare image</span>
<span class="hljs-keyword">let</span> img = tf.<span class="hljs-title function_">fromPixels</span>(imageData, <span class="hljs-number">1</span>)
img = img.<span class="hljs-title function_">reshape</span>([<span class="hljs-number">1</span>, <span class="hljs-number">28</span>, <span class="hljs-number">28</span>, <span class="hljs-number">1</span>])
img = tf.<span class="hljs-title function_">cast</span>(img, <span class="hljs-string">&#x27;float32&#x27;</span>)

<span class="hljs-comment">// Predict</span>
<span class="hljs-keyword">const</span> output = model.<span class="hljs-title function_">predict</span>(img)
</code></pre><p>Few lines of code is enough to enjoy with the number recognition model from Keras into our JS code. Of course, now we can add more logic into this code to do something more useful, like a canvas to draw a number and then capture this image to predict the number.</p>
<img class="center" src="https://aralroca.com/images/blog-images/24.gif" alt="mnist example" />

<p>📕 Code: <a href="https://github.com/aralroca/MNIST_React_TensorFlowJS">https://github.com/aralroca/MNIST_React_TensorFlowJS</a></p>
<h2 id="why-in-the-browser">Why in the browser?</h2>
<p>Training models in the browser can be very inefficient depending on the device. Even thought TensorFlow.js takes advantage of WebGL to train the model behind the scenes, it is 1.5-2x slower than TensorFlow Python. However, before TensorFlow.js, it was impossible to use machine learning models directly in the browser without an API interaction. Now we can train and use models offline in our applications. Also, predictions are much faster because they don&#39;t require the request to the server. Another benefit is the low cost in server because now all these calculations are on client-side.</p>
<img class="center" src="https://aralroca.com/images/blog-images/25.jpeg" alt="why in the browser?" />

<h2 id="conclusion">Conclusion</h2>
<ul>
<li><span class="s1">A model is a way to represent a simplified part of the reality and we can use it to predict things.</span></li>
<li>A good way to create models is using neural networks.</li>
<li>A good and easy tool to create neural networks is TensorFlow.js.</li>
</ul>
<br />
<img class="center" src="https://aralroca.com/images/blog-images/26.jpeg" alt="bye!" />

<h2 id="references">References:</h2>
<ul>
<li><a href="https://js.tensorflow.org">https://js.tensorflow.org</a></li>
<li><a href="https://en.wikipedia.org/wiki/Scientific_modelling">https://en.wikipedia.org/wiki/Scientific_modelling</a></li>
<li><a href="https://www.quantstart.com/articles/Supervised-Learning-for-Document-Classification-with-Scikit-Learn">https://www.quantstart.com/articles/Supervised-Learning-for-Document-Classification-with-Scikit-Learn</a></li>
<li><a href="https://en.wikipedia.org/wiki/Synapse">https://en.wikipedia.org/wiki/Synapse</a></li>
<li><a href="https://medium.com/tensorflow/real-time-human-pose-estimation-in-the-browser-with-tensorflow-js-7dd0bc881cd5">https://medium.com/tensorflow/real-time-human-pose-estimation-in-the-browser-with-tensorflow-js-7dd0bc881cd5</a></li>
<li><a href="https://github.com/tensorflow/tfjs-models/tree/master/posenet">https://github.com/tensorflow/tfjs-models/tree/master/posenet</a></li>
<li><a href="https://www.youtube.com/watch?v=Y_XM3Bu-4yc">https://www.youtube.com/watch?v=Y_XM3Bu-4yc</a></li>
<li><a href="https://ml4a.github.io/demos/confusion_mnist/">https://ml4a.github.io/demos/confusion_mnist/</a></li>
</ul>
]]></content:encoded>
            </item>
            <item>
              <title>Virtual Sommelier, text classifier in the browser</title>
              <description>How to develop a food text classifier to suggest the best wines to pair with the name of a dish or an ingredient.</description>
              <link>https://aralroca.com/blog/food-pairing-classifier</link>
              <guid isPermaLink="false">https://aralroca.com/blog/food-pairing-classifier/</guid>
              <pubDate>Mon May 17 2021 00:00:00 GMT+0000 (Coordinated Universal Time)</pubDate>
              <content:encoded><![CDATA[<h2 id="introduction">Introduction</h2>
<p>At Vinissimus, we have recently launched a <a href="https://www.vinissimus.com/en/virtual-sommelier/">virtual sommelier</a> that suggests wines given a text of a food dish.</p>
<img width="400" height="257" src="https://aralroca.com/images/blog-images/virtual-sommelier.png" alt="example" class="center">

<p>In this article we&#39;ll explore the development of this suggester, trained with machine learning and consumed directly from the browser.</p>
<h2 id="prerequisites">Prerequisites</h2>
<ul>
<li>Have a database with many wines (there are +15000 wines in our database), with food labels (in total we have <strong>+1000 food labels</strong>).</li>
</ul>
<h2 id="requirements">Requirements</h2>
<ul>
<li>Given a text, for example &quot;Wine for paella&quot; (or just &quot;paella&quot;), returns all the labels among the +1000 we have that are related: paella, seafood, rice, shrimp...</li>
<li>Fast to train and use.</li>
</ul>
<h2 id="type-of-problem-to-solve">Type of problem to solve</h2>
<p>Before starting with the project, it&#39;s necessary to know what kind of problem we are facing; regression, binary-class classification, multi-class classification, multi-class multi-label classification... To know this, we must know what each term is.</p>
<h3 id="regression">Regression</h3>
<p>The regression makes sense when the value we want to predict is a numerical value that can give a new value outside the training values.</p>
<p>It&#39;s not the type of problem we want to solve ❌...</p>
<h3 id="classification">Classification</h3>
<p>We use a classification, when the value we want to predict is a value within a set of predefined values (classes).</p>
<p>Okay, this is what we want ✅.</p>
<p>Within the classification, there are:</p>
<ul>
<li><strong>Binary single-label</strong>: predicts a class between two classes <em>(not our case, since we have 1000 classes ❌ )</em>.</li>
<li><strong>Multi-class single-label</strong>: predicts a class between more than two classes <em>(not our case either, since we don&#39;t have to choose 1. For example for paella we can recommend: paella, rice and seafood labels ❌ )</em>.</li>
<li><strong>Multi-class multi-label</strong>: predicts a range of classes between more than two classes <em>(This is what we want ✅ )</em>.</li>
</ul>
<p>It is important to know that our problem is a <strong>multi-class multi-label classification</strong> as this will determine some hyperparameters to use such as the loss function.</p>
<h2 id="exploring-techniquestools">Exploring techniques/tools</h2>
<p>Now that we know that the problem we want to solve is a multi-class multi-label classification, let&#39;s explore a few ways in order to solve the problem, considering that we want to load the model directly from the browser.</p>
<h3 id="tensorflowjs">Tensorflow.js</h3>
<p><em><strong>Spoiler</strong>: we&#39;ll discard it.</em></p>
<p><a href="https://www.tensorflow.org/js">Tensorflow</a> is one of the most used frameworks for deeplearning, it allows you to create neural network models in a simple and declarative way. It also has a JavaScript version that allows us to load an already trained model from the browser to make predictions. So initially this tool could be considerated adequate to solve the problem.</p>
<p>Tensorflow works with tensors (n-dimensional vectors) as a lingua franca, so to work with text we must transform the text into tensors. To do this there are several embedding models, however we&#39;ll use the <a href="https://tfhub.dev/google/universal-sentence-encoder/1">Universal Sentence Encoder</a> that is already optimized to work from the browser, because to make the prediction we must also pass the text to tensor from the browser.</p>
<img src="https://aralroca.com/images/blog-images/example-classification.png" alt="example" class="center transparent">

<p>We can transform our entire dataset into encodings with:</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">import</span> <span class="hljs-string">&#x27;@tensorflow/tfjs-node-gpu&#x27;</span>
<span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> use <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;@tensorflow-models/universal-sentence-encoder&#x27;</span>
<span class="hljs-keyword">import</span> data <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;./data.json&#x27;</span>
<span class="hljs-keyword">import</span> _ <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;lodash&#x27;</span>
<span class="hljs-keyword">import</span> fs <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;node:fs&#x27;</span>

<span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">&#x27;Encoding...&#x27;</span>)
use
  .<span class="hljs-title function_">load</span>()
  .<span class="hljs-title function_">then</span>(<span class="hljs-function">(<span class="hljs-params">model</span>) =&gt;</span>
    model.<span class="hljs-title function_">embed</span>(data.<span class="hljs-title function_">map</span>(<span class="hljs-function">(<span class="hljs-params">{ text }</span>) =&gt;</span> text.<span class="hljs-title function_">trim</span>().<span class="hljs-title function_">toLowerCase</span>()))
  )
  .<span class="hljs-title function_">then</span>(<span class="hljs-function">(<span class="hljs-params">r</span>) =&gt;</span> {
    fs.<span class="hljs-title function_">writeFileSync</span>(
      <span class="hljs-string">&#x27;embeddings.json&#x27;</span>,
      <span class="hljs-title class_">JSON</span>.<span class="hljs-title function_">stringify</span>(_.<span class="hljs-title function_">chunk</span>(<span class="hljs-title class_">Array</span>.<span class="hljs-title function_">from</span>(r.<span class="hljs-title function_">dataSync</span>()), <span class="hljs-number">512</span>))
    )
    <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">&#x27;Saved...&#x27;</span>)
  })
</code></pre><p>And use a network architecture like this:</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> tf <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;@tensorflow/tfjs&#x27;</span>
<span class="hljs-keyword">import</span> <span class="hljs-string">&#x27;@tensorflow/tfjs-node-gpu&#x27;</span>

<span class="hljs-keyword">const</span> model = tf.<span class="hljs-title function_">sequential</span>()

model.<span class="hljs-title function_">add</span>(
  tf.<span class="hljs-property">layers</span>.<span class="hljs-title function_">dense</span>({
    <span class="hljs-attr">inputShape</span>: [<span class="hljs-number">512</span>],
    <span class="hljs-attr">activation</span>: <span class="hljs-string">&#x27;relu&#x27;</span>,
    <span class="hljs-attr">units</span>: <span class="hljs-number">512</span>,
  })
)

<span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">10</span>; i += <span class="hljs-number">1</span>) {
  model.<span class="hljs-title function_">add</span>(
    tf.<span class="hljs-property">layers</span>.<span class="hljs-title function_">dense</span>({
      <span class="hljs-attr">inputShape</span>: [<span class="hljs-number">512</span>],
      <span class="hljs-attr">activation</span>: <span class="hljs-string">&#x27;relu&#x27;</span>,
      <span class="hljs-attr">units</span>: <span class="hljs-number">512</span>,
    })
  )
}

model.<span class="hljs-title function_">add</span>(
  tf.<span class="hljs-property">layers</span>.<span class="hljs-title function_">dense</span>({
    <span class="hljs-attr">activation</span>: <span class="hljs-string">&#x27;sigmoid&#x27;</span>,
    <span class="hljs-attr">units</span>: classes.<span class="hljs-property">length</span>,
  })
)

model.<span class="hljs-title function_">compile</span>({
  <span class="hljs-attr">loss</span>: <span class="hljs-string">&#x27;binaryCrossentropy&#x27;</span>,
  <span class="hljs-attr">optimizer</span>: <span class="hljs-string">&#x27;adam&#x27;</span>,
  <span class="hljs-attr">metrics</span>: [<span class="hljs-string">&#x27;accuracy&#x27;</span>],
})
</code></pre><p>To train the model, pass it the encodings that we have generated:</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">import</span> embeddings <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;./embeddings.json&#x27;</span>
<span class="hljs-keyword">import</span> outputs <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;./outputs.json&#x27;</span>

<span class="hljs-keyword">const</span> dataset = tf.<span class="hljs-property">data</span>
  .<span class="hljs-title function_">generator</span>(<span class="hljs-keyword">async</span> <span class="hljs-keyword">function</span>* <span class="hljs-title function_">gen</span>(<span class="hljs-params"></span>) {
    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i &lt; embeddings.<span class="hljs-property">length</span>; i += <span class="hljs-number">1</span>) {
      <span class="hljs-keyword">yield</span> {
        <span class="hljs-attr">xs</span>: embeddings[i],
        <span class="hljs-attr">ys</span>: outputs[i],
      }
    }
  })
  .<span class="hljs-title function_">batch</span>(<span class="hljs-number">128</span>)

model.<span class="hljs-title function_">fitDataset</span>(dataset, { <span class="hljs-attr">epochs</span>: <span class="hljs-number">600</span> }).<span class="hljs-title function_">then</span>(<span class="hljs-function">(<span class="hljs-params">history</span>) =&gt;</span> {
  <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(history)
  model.<span class="hljs-title function_">save</span>(<span class="hljs-string">&#x27;file://./model&#x27;</span>)
})
</code></pre><p>Of course there are many hyperparameters to play with: number of epochs, batch size, dense layer activation functions, optimizer, etc. However, after spending a lot of time we haven&#39;t found yet the best way to solve two problems that had arisen when we tried to solve the problem with Tensorflow:</p>
<ul>
<li>The time needed to train with +1000 classes and +400000 examples in the dataset made it unfeasible. Around 10 days of training.</li>
<li>Testing with fewer classes and examples works well... But calculating the embeddings with the Universal Sentense encoder is a bit expensive (although the prediction is cheaper). To make the prediction we have to pass the embeddings so it&#39;s a price to pay.</li>
</ul>
<p>One of the requirements (Fast to train and use) was not feasible with Tensorflow.js. We have to <strong>look for other alternatives</strong>!</p>
<h3 id="fasttext">FastText</h3>
<p><em><strong>Spoiler</strong>: This is what we finally use.</em></p>
<p><a href="https://fasttext.cc/">FastText</a> is a Facebook tool that, among other things, is used to train text classification models. Unlike Tensorflow.js, it is more intended to work with text so we don&#39;t need to pass a tensor and we can use the text directly. Training a model with it is much faster and there are fewer hyperparameters. Besides, to use the model from the browser is possible through WebAssembly. So it&#39;s a good alternative to try. Moreover, we can directly use the fastText CLI, which makes it easier to test combinations.</p>
<p>After some tests, we found that fastText met the requirements. The following sections of the article will focus on the use of FastText.</p>
<h2 id="preparing-the-data--data-augmentation">Preparing the data &amp; data augmentation</h2>
<p>FastText expects a text file with different labels and texts with a similar format to this one:</p>
<pre><code>__label__1606 __label__433 rabbit with mushrooms
</code></pre><p>The text <code>rabbit with mushrooms</code> is related to the labels with the id <code>1606</code> <em>(id of the &quot;rabbit with mushrooms&quot; label)</em> and <code>433</code> <em>(id of the &quot;rabbit&quot; label)</em>.</p>
<p>The initial problem is that we don&#39;t start from ready-made sentences because the search engine didn&#39;t exist before, so we have to generate them from each label we have.</p>
<p>Surely we could put more labels on it, for example, white meat, but how do we make all those relationships?</p>
<p>What we did is to save an array with each label in a JSON, and make several scripts for each label to have extra information such as: synonyms, plurals, closest words, relations, etc. For each language we have (en, es, it, fr and de).</p>
<ul>
<li>For <strong>synonyms</strong>, <strong>plurals</strong> and missing translations we used the <a href="https://www.deepl.com/en/docs-api/">API of DeepL</a>.</li>
<li>For <strong>closest words</strong>, FastText has available <a href="https://fasttext.cc/docs/en/pretrained-vectors.html">Wikipedia vectors</a> to search the closest words with k-nearest.</li>
<li>For <strong>relations</strong>, we simply made several iterations in the array applying logics like: all words that have &quot;beef, goat, etc&quot; are marked as children of &quot;red meat&quot;. And so on with all the detected labels that were more generic, such as: fish, rice, pasta, etc.</li>
</ul>
<p>Apart from normalizing each text with this simple JS function:</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">function</span> <span class="hljs-title function_">normalize</span>(<span class="hljs-params">text = <span class="hljs-string">&#x27;&#x27;</span></span>) {
  <span class="hljs-keyword">return</span> text
    .<span class="hljs-title function_">trim</span>()
    .<span class="hljs-title function_">toLowerCase</span>()
    .<span class="hljs-title function_">normalize</span>(<span class="hljs-string">&#x27;NFD&#x27;</span>)
    .<span class="hljs-title function_">replace</span>(<span class="hljs-regexp">/[\u0300-\u036f]/g</span>, <span class="hljs-string">&#x27;&#x27;</span>)
}
</code></pre><p><strong>Example of 2 items of this array:</strong></p>
<pre><code class="hljs language-json"><span class="hljs-punctuation">[</span>
  <span class="hljs-punctuation">{</span>
    <span class="hljs-attr">&quot;id&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;1109&quot;</span><span class="hljs-punctuation">,</span>
    <span class="hljs-attr">&quot;txt&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span>
      <span class="hljs-attr">&quot;es&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;revueltos&quot;</span><span class="hljs-punctuation">,</span>
      <span class="hljs-attr">&quot;fr&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;oeufs brouilles&quot;</span><span class="hljs-punctuation">,</span>
      <span class="hljs-attr">&quot;de&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;ruhreier&quot;</span><span class="hljs-punctuation">,</span>
      <span class="hljs-attr">&quot;it&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;uova strapazzate&quot;</span><span class="hljs-punctuation">,</span>
      <span class="hljs-attr">&quot;en&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;scrambled eggs&quot;</span>
    <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span>
    <span class="hljs-attr">&quot;similar&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span><span class="hljs-string">&quot;fritos&quot;</span><span class="hljs-punctuation">,</span> <span class="hljs-string">&quot;revuelto&quot;</span><span class="hljs-punctuation">,</span> <span class="hljs-string">&quot;egg&quot;</span><span class="hljs-punctuation">,</span> <span class="hljs-string">&quot;huevo&quot;</span><span class="hljs-punctuation">,</span> <span class="hljs-string">&quot;estrellados&quot;</span><span class="hljs-punctuation">]</span><span class="hljs-punctuation">,</span>
    <span class="hljs-attr">&quot;parent&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span><span class="hljs-string">&quot;779&quot;</span><span class="hljs-punctuation">]</span>
  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span>
  <span class="hljs-punctuation">{</span>
    <span class="hljs-attr">&quot;id&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;779&quot;</span><span class="hljs-punctuation">,</span>
    <span class="hljs-attr">&quot;txt&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span>
      <span class="hljs-attr">&quot;es&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;huevos&quot;</span><span class="hljs-punctuation">,</span>
      <span class="hljs-attr">&quot;fr&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;oeuf&quot;</span><span class="hljs-punctuation">,</span>
      <span class="hljs-attr">&quot;de&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;eier&quot;</span><span class="hljs-punctuation">,</span>
      <span class="hljs-attr">&quot;it&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;uova&quot;</span><span class="hljs-punctuation">,</span>
      <span class="hljs-attr">&quot;en&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;eggs&quot;</span>
    <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span>
    <span class="hljs-attr">&quot;similar&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span>
      <span class="hljs-string">&quot;uovo&quot;</span><span class="hljs-punctuation">,</span>
      <span class="hljs-string">&quot;œuf&quot;</span><span class="hljs-punctuation">,</span>
      <span class="hljs-string">&quot;ei&quot;</span><span class="hljs-punctuation">,</span>
      <span class="hljs-string">&quot;kartoffel omelette&quot;</span><span class="hljs-punctuation">,</span>
      <span class="hljs-string">&quot;omelette&quot;</span><span class="hljs-punctuation">,</span>
      <span class="hljs-string">&quot;huevo&quot;</span><span class="hljs-punctuation">,</span>
      <span class="hljs-string">&quot;spiegelei&quot;</span><span class="hljs-punctuation">,</span>
      <span class="hljs-string">&quot;tortilla de patatas&quot;</span><span class="hljs-punctuation">,</span>
      <span class="hljs-string">&quot;tortilla&quot;</span><span class="hljs-punctuation">,</span>
      <span class="hljs-string">&quot;gebraten&quot;</span><span class="hljs-punctuation">,</span>
      <span class="hljs-string">&quot;tortillas&quot;</span><span class="hljs-punctuation">,</span>
      <span class="hljs-string">&quot;fritos&quot;</span><span class="hljs-punctuation">,</span>
      <span class="hljs-string">&quot;frito&quot;</span><span class="hljs-punctuation">,</span>
      <span class="hljs-string">&quot;fichi&quot;</span><span class="hljs-punctuation">,</span>
      <span class="hljs-string">&quot;ous&quot;</span>
    <span class="hljs-punctuation">]</span><span class="hljs-punctuation">,</span>
    <span class="hljs-attr">&quot;parent&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span><span class="hljs-punctuation">]</span>
  <span class="hljs-punctuation">}</span>
<span class="hljs-punctuation">]</span>
</code></pre><p>Preparing this array has been the most laborious part of the whole process. Once this array is ready, then we can generate with the format that FastText is expecting as many food sentences as possible by adding plurals, synonyms, knowing which generic labels to put for each sentence, etc. Besides we can add extra words to the sentences such as &quot;Wine for ...&quot;, &quot;Pairing for ...&quot;, etc.</p>
<p>So we went from 1000 labels, and therefore 1000 possible sentences with 1 label per sentence, to increase to 74,000 sentences and each sentence with several labels.</p>
<h2 id="training">Training</h2>
<p>Once the file with all the sentences and labels has been generated, we can train the model. With FastText we can do this directly with the CLI. After playing a little with the hyperparameters, this was the command that best converged our loss function:</p>
<pre><code>./fasttext supervised -input data/dataset.txt -output model -epoch 50 -lr 0.1 -lrUpdateRate 1000 -minCount 1 -minn 3 -maxn 6 -wordNgrams 2 -dim 100 -neg 20 -loss ova
</code></pre><p>As a <strong>loss function</strong> we use the <strong>ova</strong> (one vs all) which is the one that best suits us for a multi-class multi-label classification problem. Other parameters such as epoch, learning rate, etc, are the result of playing with the hyperparameters so that the loss function is as close to 0 as possible (where there is less error).</p>
<p><strong>minn</strong> and <strong>maxn</strong> are important to avoid misspelings when typing. So if people search for &quot;pizzza&quot;, for example, they will get the same results as &quot;pizza&quot;. On the other hand, it significantly increases the final size of the model. I&#39;ll explain later how to fix this.</p>
<p>If you run the command, you&#39;ll see that the training time is much faster than using Tensorflow, with 20min maximum.</p>
<h2 id="evaluation">Evaluation</h2>
<p>To know how well your model is doing, one of the things to look at during the training, as I said, is how the loss is closer to zero. We can also look how the accuracy is closer to 100. However, once it&#39;s already trained we can evaluate how well the model is doing by looking at two other factors: Recall and precision. To do this, FastText has a <a href="https://fasttext.cc/docs/en/cheatsheet.html#text-classification">test</a> command that can be applied to a set of sentences that have not been used during training.</p>
<h2 id="reducing-the-model-size-quantization">Reducing the model size: Quantization</h2>
<p>One problem we encountered was that the size of the model occupied 400mb, so it was totally unfeasible to be used in the browser... This is the cost we include for avoiding misspelings with minn and maxn parameters.</p>
<p>To solve this, we use a well-known technique in machine learning called quantization, which consists of reducing the memory size reserved for each weight.</p>
<p>Fortunately, FastText has its own implementation to apply quantization in its models. For more details they published a <a href="https://arxiv.org/pdf/1612.03651.pdf">paper</a>.</p>
<p>It&#39;s important to be aware that applying quantization is not a panacea, and that we are likely to lose some model accuracy.</p>
<p>We apply the quantization with this command:</p>
<pre><code>./fasttext quantize -output model -input data/dataset.txt -qnorm -retrain -epoch 1 -cutoff 100000
</code></pre><p>With this, we drop from 400mb to 4mb! 100 times less. 4mb is still big for the browser, but more feasible...</p>
<h2 id="using-the-model-on-the-browser">Using the model on the browser</h2>
<p>To use the model trained with FastText from the browser, it is necessary to load it <a href="https://github.com/facebookresearch/fastText/tree/master/webassembly">via WebAssembly</a>. However, you don&#39;t require a WebAssembly knowledge as you can use the <code>fasttext.js</code> file which has all the glue code.</p>
<p>We can load the model dynamically with the following function:</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">const</span> [model, setModel] = <span class="hljs-title function_">useState</span>()

<span class="hljs-keyword">async</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">onLoadModel</span>(<span class="hljs-params"></span>) {
  <span class="hljs-keyword">const</span> { <span class="hljs-title class_">FastText</span>, addOnPostRun } = <span class="hljs-keyword">await</span> <span class="hljs-keyword">import</span>(<span class="hljs-string">&#x27;./fasttext.js&#x27;</span>)
  <span class="hljs-title function_">addOnPostRun</span>(<span class="hljs-title function_">async</span> () =&gt; {
    <span class="hljs-keyword">const</span> ft = <span class="hljs-keyword">new</span> <span class="hljs-title class_">FastText</span>()
    <span class="hljs-title function_">setModel</span>(<span class="hljs-keyword">await</span> ft.<span class="hljs-title function_">loadModel</span>(<span class="hljs-string">&#x27;./model.ftz&#x27;</span>))
  })
}
</code></pre><p>In the first part of the above example we&#39;ve loaded the fasttext library. Then we&#39;ve loaded the model and saved it, in this case, in the React state, so that we can use it later.</p>
<p>For label prediction through a text, we can use this function:</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">function</span> <span class="hljs-title function_">predictLabelsFromText</span>(<span class="hljs-params">text</span>) {
  <span class="hljs-keyword">const</span> threshold = <span class="hljs-number">0.5</span>
  <span class="hljs-keyword">const</span> predictions = []
  <span class="hljs-keyword">const</span> numLabels = <span class="hljs-number">5</span>
  <span class="hljs-keyword">const</span> res = model.<span class="hljs-title function_">predict</span>(<span class="hljs-title function_">normalize</span>(text), numLabels, <span class="hljs-number">0</span>)

  <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i &lt; res.<span class="hljs-title function_">size</span>(); i += <span class="hljs-number">1</span>) {
    predictions.<span class="hljs-title function_">push</span>(res.<span class="hljs-title function_">get</span>(i))
  }

  <span class="hljs-keyword">return</span> predictions
    .<span class="hljs-title function_">filter</span>(<span class="hljs-function">(<span class="hljs-params">[score]</span>) =&gt;</span> score &gt; threshold)
    .<span class="hljs-title function_">sort</span>(<span class="hljs-function">(<span class="hljs-params">[scoreA], [scoreB]</span>) =&gt;</span> scoreB - scoreA)
    .<span class="hljs-title function_">map</span>(<span class="hljs-function">(<span class="hljs-params">[score, label]</span>) =&gt;</span> label.<span class="hljs-title function_">replace</span>(<span class="hljs-string">&#x27;__label__&#x27;</span>, <span class="hljs-string">&#x27;&#x27;</span>))
}
</code></pre><p>Given a text, this function returns the 5 related labels (if the probability is higher than 50%, controled by the threshold).</p>
<p>Compared to Tensorflow, the prediction here is very fast.</p>
<h2 id="conclusions">Conclusions</h2>
<p>In this article we have seen how to train a text prediction model easily using FastText and how to use it directly from the browser.</p>
<p>The example used in the article is a real example of a project we developed at Vinissimus, in which, given a text about food, relates to the referenced food labels in order to be able to recommend a wine.</p>
<p>You can test the result in:</p>
<ul>
<li><a href="https://www.vinissimus.co.uk/en/virtual-sommelier/">https://www.vinissimus.co.uk/en/virtual-sommelier/</a> (English)</li>
<li><a href="https://www.vinissimus.com/es/virtual-sommelier/">https://www.vinissimus.com/es/virtual-sommelier/</a> (Spanish)</li>
<li><a href="https://www.italvinus.it/it/virtual-sommelier/">https://www.italvinus.it/it/virtual-sommelier/</a> (Italian)</li>
<li><a href="https://www.vinissimus.fr/fr/virtual-sommelier/">https://www.vinissimus.fr/fr/virtual-sommelier/</a> (French)</li>
<li><a href="https://www.hispavinus.de/de/virtual-sommelier/">https://www.hispavinus.de/de/virtual-sommelier/</a> (German)</li>
</ul>
]]></content:encoded>
            </item>
            <item>
              <title>From Determinism to Probabilism: The AI Paradigm Shift</title>
              <description>The shift from deterministic to probabilistic computing isn't just a technical upgrade. It's a change in how knowledge gets created.</description>
              <link>https://aralroca.com/blog/from-determinism-to-probabilism-ai-paradigm-shift</link>
              <guid isPermaLink="false">https://aralroca.com/blog/from-determinism-to-probabilism-ai-paradigm-shift/</guid>
              <pubDate>Thu Apr 30 2026 00:00:00 GMT+0000 (Coordinated Universal Time)</pubDate>
              <content:encoded><![CDATA[<p>The other day I had a bug in production. Something was rendering wrong in the UI and I couldn&#39;t figure out where it was coming from. I gave Claude the URL, it opened Chrome, inspected the HTML in the browser, cross-referenced what it saw in the DOM with my source code, and found the exact line where the error was. Not the file; the line. It navigated between the rendered output and the codebase, matched what was broken on screen to the component that produced it, and pointed me to the fix.</p>
<p>That moment stuck with me. Not because AI saved me time (it does that daily), but because of what it revealed about the nature of the answer. The model didn&#39;t <em>know</em> where the bug was. It inferred it. It observed the rendered HTML, estimated which parts of the code could produce that output, and surfaced the most probable origin. That probabilistic inference across two different representations of the same system; browser and codebase; was more effective than my deterministic debugging would have been.</p>
<p>We&#39;re living through something bigger than most people realize. The shift from deterministic to probabilistic computing isn&#39;t just a technical upgrade. It&#39;s a change in how knowledge gets created.</p>
<h2 id="the-first-paradigm-shift-analog-to-digital">The first paradigm shift: analog to digital</h2>
<p><img src="https://aralroca.com/images/blog-images/determinism-probabilism-analog.webp" alt="Vintage audio equipment with VU meters and patch cables; the analog world where signals degraded with every copy"></p>
<p>The move from analog to digital was the defining technology transition of the late 20th century. It converted continuous signals into discrete data. Suddenly you could copy information without degradation. Transmit it globally. Store it efficiently. The internet, distributed systems, modern software; all of it descends from that single insight: continuous signals can be represented as sequences of ones and zeros.</p>
<p>But there was something that transition left untouched: the process of creation itself.</p>
<p>Digital software is deterministic. Given the same input, it produces the same output. Every line of code, every system, every product had to be explicitly designed, written, and maintained by a human. The computer executed instructions. It didn&#39;t generate anything it hadn&#39;t been told to generate. A <a href="https://kitmul.com/en/developer/sql-formatter">SQL formatter</a> formats SQL because someone wrote exact rules for how SQL should be formatted. A <a href="https://kitmul.com/en/developer/password-generator">password generator</a> produces random strings because someone implemented <a href="https://en.wikipedia.org/wiki/Cryptographically_secure_pseudorandom_number_generator">CSPRNG</a> algorithms that define precisely how randomness gets produced.</p>
<p>Deterministic systems are predictable, testable, and reliable. They&#39;re also fundamentally limited: they can only do what someone has already imagined and coded.</p>
<h2 id="the-second-paradigm-shift-deterministic-to-probabilistic">The second paradigm shift: deterministic to probabilistic</h2>
<p><img src="https://aralroca.com/images/blog-images/determinism-probabilism-paradigm-chart.webp" alt="Area chart showing global information generated from 1986 to 2025, split across three paradigms: analog (teal), deterministic digital (blue), and probabilistic/AI (amber). The analog-to-digital crossover happened in 2002; human-to-AI content crossover in 2024. Logarithmic scale from 10 EB to 100 ZB."></p>
<p>With large language models and deep learning, we entered a new phase. Systems that don&#39;t execute rigid instructions but generate results based on probability distributions.</p>
<p>The difference is structural:</p>
<ul>
<li>We no longer describe exactly <em>what</em> to do. We train models to learn <em>how</em> to do it.</li>
<li>We don&#39;t generate information manually. We infer it.</li>
<li>We produce answers, content, and decisions that were never explicitly programmed.</li>
</ul>
<p>Think about what an <a href="https://kitmul.com/en/ai/ai-content-detector">AI content detector</a> does. It doesn&#39;t have a list of &quot;AI-written sentences&quot; to match against. It computes statistical properties of text; <a href="https://en.wikipedia.org/wiki/Zipf%27s_law">Zipf&#39;s law</a> conformity, punctuation entropy, sentence length distributions; and estimates a probability that the text was machine-generated. The detector itself is a probabilistic system analyzing the output of another probabilistic system. That&#39;s a sentence that would have been meaningless ten years ago.</p>
<p>Or consider <a href="https://kitmul.com/en/ai/automatic-subtitle-generator">automatic subtitle generation</a>. <a href="https://openai.com/index/whisper/">OpenAI&#39;s Whisper model</a> doesn&#39;t follow if-then rules to transcribe speech. It processes audio spectrograms and predicts the most probable sequence of tokens that corresponds to what was said. It gets it right most of the time. Not all of the time. That &quot;most of the time&quot; is the defining characteristic of probabilistic systems.</p>
<p>This shift has a direct impact on the most valuable resource that exists: time. AI reduces the effort required to create, analyze, and predict by orders of magnitude.</p>
<h2 id="knowledge-generation-without-precedent">Knowledge generation without precedent</h2>
<p>The key difference is that probabilistic systems can work with the unknown. From learned patterns, they can:</p>
<ul>
<li>Generate text, images, or code that has never existed before.</li>
<li>Predict future outcomes from incomplete data.</li>
<li>Find relationships that were never explicitly defined.</li>
</ul>
<p>This breaks a historical constraint: we no longer need to write every possible case. The system can generalize.</p>
<p>Consider the <a href="https://kitmul.com/en/agile-project-management/monte-carlo-forecaster">Monte Carlo forecaster</a>. Classical project management asked teams to estimate how long tasks would take and then added the numbers up. Monte Carlo simulation does something smarter: it runs thousands of scenarios using historical data and gives you a probability distribution of delivery dates. &quot;There&#39;s an 85% chance you&#39;ll finish by March 15&quot; is more useful than &quot;the estimate is March 10.&quot; But here&#39;s a nuance that matters: Monte Carlo is deterministic code. Statistical formulas executed with perfect precision. There&#39;s no inference; there&#39;s simulation. It&#39;s probabilistic thinking implemented on deterministic infrastructure. Today an LLM could make the same prediction without any of that code; you pass it the team&#39;s historical data and it gives you a reasonable estimate. But &quot;reasonable&quot; isn&#39;t &quot;reliable.&quot; Until models reach 99.99% accuracy, hand-coded statistical simulations remain the safe bet. Monte Carlo is exactly the kind of tool that marks the transition: probabilistic thinking that still needs deterministic crutches.</p>
<p>The same principle applies everywhere. A <a href="https://kitmul.com/en/ai/background-remover">background remover</a> running a neural network in the browser doesn&#39;t have rules about what counts as &quot;background.&quot; It has learned probability distributions over millions of segmented images and applies those distributions to your photo. A <a href="https://kitmul.com/en/ai/prompt-generator">prompt generator</a> doesn&#39;t store pre-written prompts; it structures natural language patterns that probabilistically produce better model outputs.</p>
<p>Even tools that seem purely deterministic are being reshaped. <a href="https://kitmul.com/en/writing/html-to-markdown">HTML to Markdown conversion</a> is deterministic; the same HTML always produces the same Markdown. But the <em>reason</em> that tool exists is probabilistic: people need clean Markdown because feeding raw HTML to an LLM <a href="https://kitmul.com/en/blog/html-to-markdown-llm-tokens">wastes 60-80% of tokens on structural noise</a>. A deterministic tool serving a probabilistic ecosystem.</p>
<h2 id="the-current-limitations-why-its-not-perfect-yet">The current limitations: why it&#39;s not perfect yet</h2>
<p>Despite the potential, current AI has real constraints:</p>
<p><strong>Inference time.</strong> Generating responses means processing enormous quantities of tokens. A complex reasoning chain in a frontier model can take 30-60 seconds. That&#39;s fast compared to human analysis, but slow compared to a database query. The latency gap between &quot;compute a hash&quot; (nanoseconds) and &quot;reason about a bug&quot; (seconds) is six orders of magnitude.</p>
<p><strong>Probabilistic errors.</strong> Models don&#39;t &quot;know&quot; in the classical sense. They estimate. As of April 2026, <a href="https://openai.com/index/introducing-gpt-5-5/">GPT-5.5</a>, <a href="https://www.anthropic.com/news/claude-opus-4-7">Claude Opus 4.7</a>, and <a href="https://deepmind.google/models/gemini/">Gemini 3.1 Pro</a> score between 89% and 92% on <a href="https://www.vals.ai/benchmarks/mmlu_pro">MMLU-Pro</a>; the harder benchmark replacing the original MMLU. Each generation climbs a few points, but the numbers are still statistical. A <a href="https://kitmul.com/en/visualizers-logic/graph-traversal-animator">graph traversal animator</a> will always find the shortest path because BFS is deterministic. An LLM asked to find the shortest path will <em>probably</em> find it, but it might hallucinate an edge that doesn&#39;t exist.</p>
<p><strong>Classical infrastructure.</strong> These models run on hardware designed for deterministic computation: CPUs, GPUs, TPUs. <a href="https://www.nvidia.com/en-us/data-center/h100/">NVIDIA&#39;s H100</a> is optimized for parallel matrix multiplication, which is what transformers need, but the underlying architecture is still classical. We&#39;re solving probabilistic problems with deterministic machines.</p>
<h2 id="the-trajectory-approaching-100-accuracy">The trajectory: approaching 100% accuracy</h2>
<p>The trend line is clear. Each new model generation improves on benchmarks, reduces error rates, and expands generalization capacity. <a href="https://deepmind.google/technologies/gemini/">Google&#39;s Gemini</a>, <a href="https://www.anthropic.com/claude">Anthropic&#39;s Claude</a>, and <a href="https://openai.com/">OpenAI&#39;s GPT</a> families are converging toward accuracy levels that make the distinction between &quot;correct&quot; and &quot;highly probable&quot; practically meaningless for many tasks.</p>
<p>When models reach 99.99% accuracy on routine cognitive tasks:</p>
<ul>
<li>Trust in AI systems will match or exceed trust in human judgment.</li>
<li>Most intellectual tasks that follow learnable patterns will be delegated entirely.</li>
<li>The marginal cost of generating knowledge will approach zero.</li>
</ul>
<p>We&#39;re not there yet. But the distance is shrinking with every model release.</p>
<h2 id="the-bottleneck-classical-compute-vs-quantum-compute">The bottleneck: classical compute vs. quantum compute</h2>
<p><img src="https://aralroca.com/images/blog-images/determinism-probabilism-quantum.webp" alt="IBM Quantum System One installed at the Fraunhofer Institute; the world's first circuit-based commercial quantum computer, inside its airtight glass enclosure"></p>
<p>Here&#39;s an idea worth sitting with: we&#39;re solving a fundamentally probabilistic problem using deterministic tools.</p>
<p>GPUs and TPUs parallelize massive calculations, but they operate under classical principles. This creates real constraints:</p>
<ul>
<li>High energy consumption. Training GPT-5 class models required <a href="https://www.cometapi.com/how-many-gpus-to-train-gpt-5/">tens of thousands of NVIDIA H100 GPUs</a> for months, at costs exceeding $100 million.</li>
<li>Expensive scaling. More parameters means more hardware, more cooling, more electricity.</li>
<li>Significant latency on large models.</li>
</ul>
<p>The theoretical alternative is <a href="https://en.wikipedia.org/wiki/Quantum_computing">quantum computing</a>.</p>
<p>Companies like <a href="https://www.ibm.com/quantum">IBM</a>, <a href="https://quantumai.google/">Google</a>, and <a href="https://www.dwavesys.com/">D-Wave Systems</a> are exploring QPUs (Quantum Processing Units) that work directly with probabilistic states through <a href="https://en.wikipedia.org/wiki/Quantum_superposition">superposition</a> and <a href="https://en.wikipedia.org/wiki/Quantum_entanglement">entanglement</a>.</p>
<p>In theory, this would allow:</p>
<ul>
<li>Solving certain calculations exponentially faster.</li>
<li>Modeling probabilistic systems natively instead of simulating them on deterministic hardware.</li>
<li>Drastically reducing the computational cost of AI inference.</li>
</ul>
<p>If you want to see what quantum circuits actually look like, the <a href="https://kitmul.com/en/visualizers-logic/quantum-circuit-simulator">Quantum Circuit Simulator</a> lets you build and run circuits in the browser. Two lines of code create a <a href="https://en.wikipedia.org/wiki/Bell_state">Bell State</a>; a maximally entangled pair of qubits where measurement of one instantly determines the other. That kind of native probabilistic behavior is exactly what current AI infrastructure lacks.</p>
<h2 id="the-hard-problem-quantum-error-correction">The hard problem: quantum error correction</h2>
<p>Quantum computing isn&#39;t ready for this role yet. The main obstacle is <a href="https://en.wikipedia.org/wiki/Quantum_error_correction">Quantum Error Correction</a>.</p>
<p>Quantum systems are extremely sensitive to noise and interference. Every interaction with the environment can collapse a qubit&#39;s superposition, corrupting the computation. Current quantum processors have error rates that make them impractical for the sustained, reliable computation that AI inference requires.</p>
<p>For a QPU to be viable at scale, three things need to happen:</p>
<ol>
<li><strong>Error rates must drop dramatically.</strong> Current physical qubits have error rates around 0.1-1%. Useful quantum computation needs rates below 0.0001%.</li>
<li><strong>Stable qubit counts must scale.</strong> IBM&#39;s current roadmap targets <a href="https://research.ibm.com/blog/ibm-quantum-roadmap-2025">100,000 qubits by 2033</a>. That&#39;s ambitious, but the engineering challenges at each step are enormous.</li>
<li><strong>Fault-tolerant architectures must mature.</strong> <a href="https://en.wikipedia.org/wiki/Toric_code">Surface codes</a> and other error correction schemes work in principle but require thousands of physical qubits per logical qubit. The overhead is still prohibitive.</li>
</ol>
<p>What&#39;s at stake isn&#39;t just speed. It&#39;s energy.</p>
<p>Data centers consumed <a href="https://www.iea.org/reports/energy-and-ai/energy-demand-from-ai">around 415 TWh in 2024</a>; 1.5% of global electricity. The IEA estimates they&#39;ll exceed <a href="https://www.iea.org/news/ai-is-set-to-drive-surging-electricity-demand-from-data-centres-while-offering-the-potential-to-transform-how-the-energy-sector-works">1,000 TWh by 2026</a>, with AI as the main growth driver. Training a frontier model consumes the electricity equivalent of thousands of households for a year. Every inference query burns <a href="https://www.bcs.org/articles-opinion-and-research/is-ai-or-quantum-computing-more-energy-intensive/">over 33 Wh on long prompts</a>; ten times what a Google search uses. And this scales. More models, more agents, more robotics with embedded AI; every layer adds energy demand.</p>
<p>The day quantum error correction gets solved, that equation changes radically. Current QPUs consume <a href="https://patentpc.com/blog/quantum-computing-energy-consumption-how-sustainable-is-it-latest-data">around 25 kW</a>, most of it on cryogenic cooling; not computation. But quantum computing works with the probabilistic problem natively instead of simulating it with trillions of matrix multiplications. <a href="https://www.arquimea.com/blog/how-will-quantum-technologies-improve-ai-power-consumption/">Quantum compression algorithms already demonstrate 84% energy efficiency gains</a> on specific AI tasks. And partial error correction is enabling <a href="https://phys.org/news/2025-12-quantum-machine-nears-partial-error.html">quantum models to maintain high accuracy with thousands of qubits instead of millions</a>.</p>
<p>When quantum error correction matures, we won&#39;t just have faster AI. We&#39;ll have AI that consumes orders of magnitude less energy per inference. That&#39;s what turns quantum computing from a lab curiosity into viable infrastructure for the billions of agents the future requires.</p>
<p>Until that happens, we&#39;ll continue running probabilistic AI on deterministic hardware. Which, honestly, works remarkably well for how fundamentally mismatched the paradigms are.</p>
<h2 id="what-this-means-practically">What this means practically</h2>
<p>This isn&#39;t abstract philosophy. The shift from determinism to probabilism changes how you build, how you work, and how you think about tools.</p>
<p>A <a href="https://kitmul.com/en/visualizers-logic/binary-search-tree-lab">binary search tree lab</a> teaches deterministic algorithms. Insert a node, traverse the tree, get the same result every time. That kind of certainty is still valuable. Databases still need B-trees. Routing still needs Dijkstra. <a href="https://kitmul.com/en/random/blue-noise-generator">Blue noise generators</a> still need deterministic sampling algorithms to produce well-distributed random points.</p>
<p>But the layer above those deterministic primitives is increasingly probabilistic. The database query is deterministic; the AI agent that decides <em>which</em> query to run is probabilistic. The algorithm is deterministic; the model that selects <em>which</em> algorithm fits the problem is probabilistic. The text is deterministic once written; the system that <a href="https://kitmul.com/en/writing/image-to-text">extracts text from images</a> using OCR neural networks is probabilistic.</p>
<p>We&#39;re building a stack where deterministic systems execute and probabilistic systems decide. That&#39;s new. And it&#39;s only going to accelerate.</p>
<h2 id="the-next-step-isnt-better-ai-its-a-composable-compute-stack">The next step isn&#39;t better AI; it&#39;s a composable compute stack</h2>
<p><img src="https://aralroca.com/images/blog-images/determinism-probabilism-hybrid-stack.webp" alt="Physics and mathematics equations on a black background; the formal layer that unifies deterministic, probabilistic, and quantum computation"></p>
<p>What comes next doesn&#39;t replace one paradigm with another. It composes them.</p>
<p>Think about how modern infrastructure already works. CPUs execute deterministic logic; database transactions, cryptographic verification, your operating system&#39;s kernel. GPUs and TPUs solve probabilistic problems; they train models, run inference, process distributions across millions of parameters. Each layer does what the other can&#39;t. Nobody suggests replacing CPUs with GPUs. You combine them.</p>
<p>QPUs complete the third layer. They solve a class of problems that classical hardware simulates poorly: combinatorial optimization, high-dimensional distribution sampling, search across exponential spaces. AI inference doesn&#39;t just get faster; it becomes viable at scales that are currently intractable.</p>
<p>The stack looks like this: deterministic systems execute and verify. Probabilistic systems propose and generate. Quantum systems optimize what neither of the other two can touch.</p>
<p>But there&#39;s a piece almost nobody is talking about yet.</p>
<h2 id="models-talking-to-machines-talking-to-models">Models talking to machines talking to models</h2>
<p>So far we think of AI as something that receives a prompt and returns text. That&#39;s like thinking the internet is email. The next layer is communication between probabilistic models; and between those models and the deterministic hardware they control.</p>
<p>A language model analyzes sensor data and decides it needs more information from a specific area. It communicates that decision to a vision model controlling a drone. The drone moves, collects data, processes it through another specialized model, and returns the results to the first one. No human intervened in the cycle. No human decided that area was interesting. The system inferred it.</p>
<p>That&#39;s not automation. Automation executes what a human designed. This is different: probabilistic systems deciding what data to collect, how to collect it, and what to do with what they find. Robots with AI models that choose to explore information sources that wouldn&#39;t have occurred to us.</p>
<p>Think about a marine drone with chemical sensors and a model trained on oceanic biodiversity. It doesn&#39;t follow a preprogrammed route. It detects an anomaly in water composition, infers it could indicate an unknown microbial community, adjusts its trajectory, and takes samples. It finds something no marine biologist would have looked for because none would have predicted it would be there. Another model analyzes the samples, identifies compounds with pharmaceutical potential, and asks the drone to return to the same zone with different sensors.</p>
<p>That&#39;s genuinely new knowledge. Not extracted from existing data or inferred from human text. Generated by a system that decided to go look for it.</p>
<h2 id="when-prediction-takes-nanoseconds">When prediction takes nanoseconds</h2>
<p>The current limitations are real. Inference takes seconds. Accuracy hovers around 86-95% depending on the benchmark. But those are the limitations of the first generation of a paradigm that&#39;s barely getting started. The trajectory points toward models with 99.99% accuracy and nanosecond response times.</p>
<p>When that happens, the world reorganizes in ways that are hard to imagine from where we stand now.</p>
<p>A self-driving car that takes 30 milliseconds to decide is a car that brakes late. One that decides in nanoseconds reacts before the obstacle finishes appearing. A network of medical models with 99.99% accuracy doesn&#39;t assist the doctor; it diagnoses with a reliability no human can match. A supply chain where every node has a predictive model communicating with all the others doesn&#39;t need quarterly planning; it reoptimizes in real time, every millisecond.</p>
<p>But the really important thing is what happens when probabilistic inference becomes so fast and accurate that it&#39;s functionally indistinguishable from deterministic certainty. The distinction between &quot;computing&quot; and &quot;inferring&quot; disappears. Your operating system doesn&#39;t need to distinguish between an arithmetic operation and a prediction. The compiler doesn&#39;t need to know if the result comes from formal logic or a 400B parameter model. It&#39;s all computation; part rule-based, part distribution-based, part quantum-optimized. Seamlessly integrated.</p>
<p>AI stops being a tool you open and becomes an infrastructure layer you don&#39;t even notice. Like electricity. Like TCP/IP. It&#39;s everywhere and you don&#39;t think about it.</p>
<h2 id="knowledge-beyond-the-human">Knowledge beyond the human</h2>
<p>Robots with AI models don&#39;t just automate what we used to do. They perceive what we can&#39;t.</p>
<p>An ultrasonic sensor coupled with a materials model detects microfractures in a wind turbine that no visual inspection would find. A portable spectrometer with a chemical model identifies contaminants at concentrations human protocols don&#39;t measure. A hydrophone array with an acoustic model classifies marine species by sound patterns no biologist has cataloged yet.</p>
<p>These aren&#39;t incremental efficiency improvements. They&#39;re new sources of knowledge. Data that existed in the physical world but was invisible to us because we didn&#39;t have the right sensors coupled with the right intelligence to interpret them.</p>
<p>And here&#39;s where the loop closes. Those robots don&#39;t just collect data; the models inside them decide what data is worth collecting. But the cycle goes further than that. An AI analyzing ocean temperature anomalies determines that the existing sensor grid is too coarse; it needs readings at depths and frequencies no current instrument covers. So it designs a new sensor specification. A manufacturing robot builds it. A deployment robot installs it on a fleet of underwater drones. Those drones collect data that no previous system could capture, and that data trains a better model, which identifies the next gap in sensing capability, and the cycle begins again.</p>
<p>No human decided what to measure. No human designed the sensor. No human chose where to deploy it. The entire chain; from identifying a knowledge gap to filling it with new physical hardware; was driven by probabilistic inference.</p>
<p>When millions of such systems operate simultaneously, each one designing sensors for the others, deploying instruments that didn&#39;t exist a cycle ago, and feeding the results back into shared models, humanity gains access to a layer of reality it literally couldn&#39;t perceive before. It&#39;s not an improvement on what we already knew. It&#39;s access to what we didn&#39;t know we didn&#39;t know.</p>
<h2 id="the-full-stack">The full stack</h2>
<p>Thirty years ago the question was &quot;can you code?&quot;. Ten years ago it was &quot;can you use APIs?&quot;. Today it&#39;s &quot;can you direct models?&quot;. Tomorrow it&#39;ll be irrelevant; models will direct each other.</p>
<p>Deterministic systems execute and verify. Probabilistic systems propose, generate, and decide. Quantum systems optimize the intractable. Sensors and robots extend all of this into the physical world. And communication between models closes the loop: systems that discover what they don&#39;t know, decide how to find out, and act to get it. Without human intervention. Without anyone telling them where to look.</p>
<p>If the analog-to-digital shift redefined how we store information, and the deterministic-to-probabilistic shift is redefining how we generate knowledge, the full integration; models, hardware, sensors, robots, quantum computing; redefines the limits of what&#39;s possible to perceive and understand.</p>
<p>We won&#39;t be using better tools. We&#39;ll be surrounded by an intelligence that sees what we don&#39;t see, seeks what we don&#39;t seek, and finds what we didn&#39;t know existed.</p>
]]></content:encoded>
            </item>
            <item>
              <title>From Node to Deno</title>
              <description>Learn how to use Node ecosystem in Deno.</description>
              <link>https://aralroca.com/blog/from-node-to-deno</link>
              <guid isPermaLink="false">https://aralroca.com/blog/from-node-to-deno/</guid>
              <pubDate>Sun May 17 2020 00:00:00 GMT+0000 (Coordinated Universal Time)</pubDate>
              <content:encoded><![CDATA[<p>Last week I published an article about Deno, and how to create a <a href="https://aralroca.com/blog/learn-deno-chat-app">Chat app with Deno and Preact</a>. Since then, many doubts have arisen. Mostly of them are about how to do the same thing we did in Node, but with the new Deno ecosystem.</p>
<p>I&#39;ve tried to collect some of the most used topics in Node, and looked for their alternative with Deno. First of all, I would like to make it clear that we can use many of the current Node.js modules. There is no need to look for an alternative for everything, as many modules are reusable. You can visit <a href="https://www.pika.dev/about">pika.dev</a> to look for modules to use in Deno. That said, let&#39;s start with the list:</p>
<p><strong>We will cover the following:</strong></p>
<ul>
<li><a href="#electron">Electron</a></li>
<li><a href="#forever--pm2">Forever / PM2</a></li>
<li><a href="#express--koa">Express / Koa</a></li>
<li><a href="#mongodb">MongoDB</a></li>
<li><a href="#postgressql">PostgresSQL</a></li>
<li><a href="#mysql--mariadb">MySQL / MariaDB</a></li>
<li><a href="#redis">Redis</a></li>
<li><a href="#nodemon">Nodemon</a></li>
<li><a href="#jest-jasmine-ava">Jest, Jasmine, Ava...</a></li>
<li><a href="#webpack-parcel-rollup">Webpack, Parcel, Rollup...</a></li>
<li><a href="#prettier">Prettier</a></li>
<li><a href="#npm-scripts">NPM Scripts</a></li>
<li><a href="#nvm">Nvm</a></li>
<li><a href="#npx">Npx</a></li>
<li><a href="#run-on-a-docker">Run on a Docker</a></li>
<li><a href="#run-as-a-lambda">Run as a lambda</a></li>
<li><a href="#conclusion">Conclusion</a></li>
</ul>
<h2 id="electron">Electron</h2>
<p>With Node.js we can create desktop applications using <a href="https://github.com/electron/electron">Electron</a>. Electron uses Chromium as interface to run a web environment. But, can we use Electron with Deno? Are there alternatives?</p>
<img src="https://aralroca.com/images/blog-images/55.png" alt="Electron logo" class="center transparent keepcolor" />

<p>Well, right now Electron is far from being able to be executed under Deno. We must look for alternatives. Since Deno is made with Rust, we can use <a href="https://github.com/Boscop/web-view">web-view rust bindings</a> to run Destkop application in Deno.</p>
<p>This way, we can use the native OS webview to run as many webviews as we want.</p>
<p><strong>Repo</strong>: <a href="https://github.com/eliassjogreen/deno_webview">https://github.com/eliassjogreen/deno_webview</a></p>
<pre><code class="hljs language-js"><span class="hljs-keyword">import</span> { <span class="hljs-title class_">WebView</span> } <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;https://deno.land/x/webview/mod.ts&#x27;</span>

<span class="hljs-keyword">const</span> sharedOptions = {
  <span class="hljs-attr">width</span>: <span class="hljs-number">400</span>,
  <span class="hljs-attr">height</span>: <span class="hljs-number">200</span>,
  <span class="hljs-attr">resizable</span>: <span class="hljs-literal">true</span>,
  <span class="hljs-attr">debug</span>: <span class="hljs-literal">true</span>,
  <span class="hljs-attr">frameless</span>: <span class="hljs-literal">false</span>,
}

<span class="hljs-keyword">const</span> webview1 = <span class="hljs-keyword">new</span> <span class="hljs-title class_">WebView</span>({
  <span class="hljs-attr">title</span>: <span class="hljs-string">&#x27;Multiple deno_webview example&#x27;</span>,
  <span class="hljs-attr">url</span>: <span class="hljs-string">`data:text/html,
    &lt;html&gt;
    &lt;body&gt;
      &lt;h1&gt;1&lt;/h1&gt;
    &lt;/body&gt;
    &lt;/html&gt;
    `</span>,
  ...sharedOptions,
})

<span class="hljs-keyword">const</span> webview2 = <span class="hljs-keyword">new</span> <span class="hljs-title class_">WebView</span>({
  <span class="hljs-attr">title</span>: <span class="hljs-string">&#x27;Multiple deno_webview example&#x27;</span>,
  <span class="hljs-attr">url</span>: <span class="hljs-string">`data:text/html,
    &lt;html&gt;
    &lt;body&gt;
      &lt;h1&gt;2&lt;/h1&gt;
    &lt;/body&gt;
    &lt;/html&gt;
    `</span>,
  ...sharedOptions,
})

<span class="hljs-keyword">await</span> <span class="hljs-title class_">Promise</span>.<span class="hljs-title function_">all</span>([webview1.<span class="hljs-title function_">run</span>(), webview2.<span class="hljs-title function_">run</span>()])
</code></pre><img src="https://aralroca.com/images/blog-images/40.jpg" alt="Deno desktop app" class="center" />
<br />

<h2 id="forever--pm2">Forever / PM2</h2>
<p><a href="https://github.com/foreversd/forever">Forever</a> and <a href="https://github.com/Unitech/pm2">PM2</a> are CLI tools for ensuring that a given script runs continuously as a daemon. Unlike Forever, PM2 is more complete and also serves as load balancer. Both are very useful in Node, but can we use them in Deno?</p>
<p>Forever is intended for Node only, so using it is not feasible. On the other hand, with PM2 we can use an interpreter.</p>
<img src="https://aralroca.com/images/blog-images/56.png" alt="PM2 logo" class="center transparent keepcolor" />

<pre><code>➜ pm2 start app.ts --interpreter=&quot;deno&quot; --interpreter-args=&quot;run --allow-net&quot; 
</code></pre><br />

<h2 id="express--koa">Express / Koa</h2>
<p><a href="https://github.com/expressjs/express">Express</a> and <a href="https://github.com/koajs/koa">Koa</a> are the best known Node frameworks. They&#39;re known for their robust routing system and their HTTP helpers (redirection, caching, etc). Can we use them in Deno? The answer is not... But there are some alternatives.</p>
<br />
<img src="https://aralroca.com/images/blog-images/42.png" alt="Express and Koa logo" class="center transparent" />

<h3 id="http-std-lib">Http (std lib)</h3>
<p>Deno&#39;s own STD library already covers many of the needs provided by Express or Koa. <a href="https://deno.land/std/http/">https://deno.land/std/http/</a>.</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">import</span> { <span class="hljs-title class_">ServerRequest</span> } <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;https://deno.land/std/http/server.ts&#x27;</span>
<span class="hljs-keyword">import</span> { getCookies } <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;https://deno.land/std/http/cookie.ts&#x27;</span>

<span class="hljs-keyword">let</span> request = <span class="hljs-keyword">new</span> <span class="hljs-title class_">ServerRequest</span>()
request.<span class="hljs-property">headers</span> = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Headers</span>()
request.<span class="hljs-property">headers</span>.<span class="hljs-title function_">set</span>(<span class="hljs-string">&#x27;Cookie&#x27;</span>, <span class="hljs-string">&#x27;full=of; tasty=chocolate&#x27;</span>)

<span class="hljs-keyword">const</span> cookies = <span class="hljs-title function_">getCookies</span>(request)
<span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">&#x27;cookies:&#x27;</span>, cookies)
</code></pre><p>However, the way to declare routes is not very attractive. So let&#39;s look at some more alternatives.</p>
<h3 id="oak-third-party-lib">Oak (Third party lib)</h3>
<p>One of the most elegant solutions right now, very inspired by Koa. <a href="https://github.com/oakserver/oak">https://github.com/oakserver/oak</a></p>
<pre><code class="hljs language-js"><span class="hljs-keyword">import</span> { <span class="hljs-title class_">Application</span> } <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;https://deno.land/x/oak/mod.ts&#x27;</span>

<span class="hljs-keyword">const</span> app = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Application</span>()

app.<span class="hljs-title function_">use</span>(<span class="hljs-function">(<span class="hljs-params">ctx</span>) =&gt;</span> {
  ctx.<span class="hljs-property">response</span>.<span class="hljs-property">body</span> = <span class="hljs-string">&#x27;Hello World!&#x27;</span>
})

<span class="hljs-keyword">await</span> app.<span class="hljs-title function_">listen</span>({ <span class="hljs-attr">port</span>: <span class="hljs-number">8000</span> })
</code></pre><h3 id="abc-third-party-lib">Abc (Third party lib)</h3>
<p>Similar to Oak. <a href="https://deno.land/x/abc">https://deno.land/x/abc</a>.</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">import</span> { <span class="hljs-title class_">Application</span> } <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;https://deno.land/x/abc/mod.ts&#x27;</span>

<span class="hljs-keyword">const</span> app = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Application</span>()

app.<span class="hljs-title function_">static</span>(<span class="hljs-string">&#x27;/static&#x27;</span>, <span class="hljs-string">&#x27;assets&#x27;</span>)

app.<span class="hljs-title function_">get</span>(<span class="hljs-string">&#x27;/hello&#x27;</span>, <span class="hljs-function">(<span class="hljs-params">c</span>) =&gt;</span> <span class="hljs-string">&#x27;Hello!&#x27;</span>).<span class="hljs-title function_">start</span>({ <span class="hljs-attr">port</span>: <span class="hljs-number">8080</span> })
</code></pre><h3 id="deno-express-third-party-lib">Deno-express (Third party lib)</h3>
<p>Maybe the most similar alternative to Express Framework. <a href="https://github.com/NMathar/deno-express">https://github.com/NMathar/deno-express</a>.</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> exp <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;https://raw.githubusercontent.com/NMathar/deno-express/master/mod.ts&#x27;</span>

<span class="hljs-keyword">const</span> port = <span class="hljs-number">3000</span>
<span class="hljs-keyword">const</span> app = <span class="hljs-keyword">new</span> exp.<span class="hljs-title class_">App</span>()

app.<span class="hljs-title function_">use</span>(exp.<span class="hljs-title function_">static_</span>(<span class="hljs-string">&#x27;./public&#x27;</span>))
app.<span class="hljs-title function_">use</span>(exp.<span class="hljs-property">bodyParser</span>.<span class="hljs-title function_">json</span>())

app.<span class="hljs-title function_">get</span>(<span class="hljs-string">&#x27;/api/todos&#x27;</span>, <span class="hljs-title function_">async</span> (req, res) =&gt; {
  <span class="hljs-keyword">await</span> res.<span class="hljs-title function_">json</span>([{ <span class="hljs-attr">name</span>: <span class="hljs-string">&#x27;Buy some milk&#x27;</span> }])
})

<span class="hljs-keyword">const</span> server = <span class="hljs-keyword">await</span> app.<span class="hljs-title function_">listen</span>(port)
<span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">`app listening on port <span class="hljs-subst">${server.port}</span>`</span>)
</code></pre><h2 id="mongodb">MongoDB</h2>
<p><a href="https://github.com/mongodb/mongo">MongoDB</a> is a document database with a huge scability and flexibility. In the JavaScript ecosystem has been widely used, with many stacks like MEAN or MERN that use it. It&#39;s very popular.</p>
<br />
<img src="https://aralroca.com/images/blog-images/43.png" alt="MongoDB logo" class="center transparent keepcolor" />

<p>So yes, we can use MongoDB with Deno. To do this, we can use this driver: <a href="https://github.com/manyuanrong/deno_mongo">https://github.com/manyuanrong/deno_mongo</a>.</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">import</span> { init, <span class="hljs-title class_">MongoClient</span> } <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;https://deno.land/x/mongo@v0.6.0/mod.ts&#x27;</span>

<span class="hljs-comment">// Initialize the plugin</span>
<span class="hljs-keyword">await</span> <span class="hljs-title function_">init</span>()

<span class="hljs-keyword">const</span> client = <span class="hljs-keyword">new</span> <span class="hljs-title class_">MongoClient</span>()
client.<span class="hljs-title function_">connectWithUri</span>(<span class="hljs-string">&#x27;mongodb://localhost:27017&#x27;</span>)

<span class="hljs-keyword">const</span> db = client.<span class="hljs-title function_">database</span>(<span class="hljs-string">&#x27;test&#x27;</span>)
<span class="hljs-keyword">const</span> users = db.<span class="hljs-title function_">collection</span>(<span class="hljs-string">&#x27;users&#x27;</span>)

<span class="hljs-comment">// insert</span>
<span class="hljs-keyword">const</span> insertId = <span class="hljs-keyword">await</span> users.<span class="hljs-title function_">insertOne</span>({
  <span class="hljs-attr">username</span>: <span class="hljs-string">&#x27;user1&#x27;</span>,
  <span class="hljs-attr">password</span>: <span class="hljs-string">&#x27;pass1&#x27;</span>,
})

<span class="hljs-comment">// findOne</span>
<span class="hljs-keyword">const</span> user1 = <span class="hljs-keyword">await</span> users.<span class="hljs-title function_">findOne</span>({ <span class="hljs-attr">_id</span>: insertId })

<span class="hljs-comment">// find</span>
<span class="hljs-keyword">const</span> users = <span class="hljs-keyword">await</span> users.<span class="hljs-title function_">find</span>({ <span class="hljs-attr">username</span>: { <span class="hljs-attr">$ne</span>: <span class="hljs-literal">null</span> } })

<span class="hljs-comment">// aggregation</span>
<span class="hljs-keyword">const</span> docs = <span class="hljs-keyword">await</span> users.<span class="hljs-title function_">aggregation</span>([
  { <span class="hljs-attr">$match</span>: { <span class="hljs-attr">username</span>: <span class="hljs-string">&#x27;many&#x27;</span> } },
  { <span class="hljs-attr">$group</span>: { <span class="hljs-attr">_id</span>: <span class="hljs-string">&#x27;$username&#x27;</span>, <span class="hljs-attr">total</span>: { <span class="hljs-attr">$sum</span>: <span class="hljs-number">1</span> } } },
])

<span class="hljs-comment">// updateOne</span>
<span class="hljs-keyword">const</span> { matchedCount, modifiedCount, upsertedId } = <span class="hljs-keyword">await</span> users.<span class="hljs-title function_">updateOne</span>(
  (<span class="hljs-attr">username</span>: { <span class="hljs-attr">$ne</span>: <span class="hljs-literal">null</span> }),
  {
    <span class="hljs-attr">$set</span>: { <span class="hljs-attr">username</span>: <span class="hljs-string">&#x27;USERNAME&#x27;</span> },
  }
)

<span class="hljs-comment">// deleteOne</span>
<span class="hljs-keyword">const</span> deleteCount = <span class="hljs-keyword">await</span> users.<span class="hljs-title function_">deleteOne</span>({ <span class="hljs-attr">_id</span>: insertId })
</code></pre><h2 id="postgressql">PostgresSQL</h2>
<img src="https://aralroca.com/images/blog-images/44.png" alt="PostgresSQL logo" class="center transparent keepcolor" />

<p>Like MongoDB, there is also a driver for <a href="https://github.com/postgres/postgres/">PostgresSQL</a>.</p>
<ul>
<li><a href="https://github.com/buildondata/deno-postgres">https://github.com/buildondata/deno-postgres</a>.</li>
</ul>
<pre><code class="hljs language-js"><span class="hljs-keyword">import</span> { <span class="hljs-title class_">Client</span> } <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;https://deno.land/x/postgres/mod.ts&#x27;</span>

<span class="hljs-keyword">const</span> client = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Client</span>({
  <span class="hljs-attr">user</span>: <span class="hljs-string">&#x27;user&#x27;</span>,
  <span class="hljs-attr">database</span>: <span class="hljs-string">&#x27;test&#x27;</span>,
  <span class="hljs-attr">hostname</span>: <span class="hljs-string">&#x27;localhost&#x27;</span>,
  <span class="hljs-attr">port</span>: <span class="hljs-number">5432</span>,
})
<span class="hljs-keyword">await</span> client.<span class="hljs-title function_">connect</span>()
<span class="hljs-keyword">const</span> result = <span class="hljs-keyword">await</span> client.<span class="hljs-title function_">query</span>(<span class="hljs-string">&#x27;SELECT * FROM people;&#x27;</span>)
<span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(result.<span class="hljs-property">rows</span>)
<span class="hljs-keyword">await</span> client.<span class="hljs-title function_">end</span>()
</code></pre><h2 id="mysql--mariadb">MySQL / MariaDB</h2>
<img src="https://aralroca.com/images/blog-images/45.png" alt="MySQL and MariaDB logo" class="center transparent keepcolor" />

<p>As with MongoDB and PostgresSQL, there is also a driver for <a href="https://github.com/mysqljs/mysql">MySQL</a> / <a href="https://github.com/mariadb-corporation/mariadb-connector-nodejs">MariaDB</a>.</p>
<ul>
<li><a href="https://github.com/manyuanrong/deno_mysql">https://github.com/manyuanrong/deno_mysql</a></li>
</ul>
<pre><code class="hljs language-js"><span class="hljs-keyword">import</span> { <span class="hljs-title class_">Client</span> } <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;https://deno.land/x/mysql/mod.ts&#x27;</span>

<span class="hljs-keyword">const</span> client = <span class="hljs-keyword">await</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Client</span>().<span class="hljs-title function_">connect</span>({
  <span class="hljs-attr">hostname</span>: <span class="hljs-string">&#x27;127.0.0.1&#x27;</span>,
  <span class="hljs-attr">username</span>: <span class="hljs-string">&#x27;root&#x27;</span>,
  <span class="hljs-attr">db</span>: <span class="hljs-string">&#x27;dbname&#x27;</span>,
  <span class="hljs-attr">poolSize</span>: <span class="hljs-number">3</span>, <span class="hljs-comment">// connection limit</span>
  <span class="hljs-attr">password</span>: <span class="hljs-string">&#x27;password&#x27;</span>,
})

<span class="hljs-keyword">let</span> result = <span class="hljs-keyword">await</span> client.<span class="hljs-title function_">execute</span>(<span class="hljs-string">`INSERT INTO users(name) values(?)`</span>, [
  <span class="hljs-string">&#x27;aralroca&#x27;</span>,
])
<span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(result)
<span class="hljs-comment">// { affectedRows: 1, lastInsertId: 1 }</span>
</code></pre><h2 id="redis">Redis</h2>
<img src="https://aralroca.com/images/blog-images/46.png" alt="Redis logo" class="center transparent keepcolor" />

<p><a href="https://github.com/NodeRedis/node-redis">Redis</a>, the best known database for caching, has also a driver for Deno.</p>
<ul>
<li><a href="https://github.com/keroxp/deno-redis">https://github.com/keroxp/deno-redis</a></li>
</ul>
<pre><code class="hljs language-js"><span class="hljs-keyword">import</span> { connect } <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;https://denopkg.com/keroxp/deno-redis/mod.ts&#x27;</span>

<span class="hljs-keyword">const</span> redis = <span class="hljs-keyword">await</span> <span class="hljs-title function_">connect</span>({
  <span class="hljs-attr">hostname</span>: <span class="hljs-string">&#x27;127.0.0.1&#x27;</span>,
  <span class="hljs-attr">port</span>: <span class="hljs-number">6379</span>,
})
<span class="hljs-keyword">const</span> ok = <span class="hljs-keyword">await</span> redis.<span class="hljs-title function_">set</span>(<span class="hljs-string">&#x27;example&#x27;</span>, <span class="hljs-string">&#x27;this is an example&#x27;</span>)
<span class="hljs-keyword">const</span> example = <span class="hljs-keyword">await</span> redis.<span class="hljs-title function_">get</span>(<span class="hljs-string">&#x27;example&#x27;</span>)
</code></pre><h2 id="nodemon">Nodemon</h2>
<img src="https://aralroca.com/images/blog-images/47.png" alt="Nodemon logo" class="center transparent keepcolor" />

<p><a href="https://github.com/remy/nodemon">Nodemon</a> is used in development environment to monitor any changes in your files, automatically restarting the server. This makes node development much more enjoyable, without having to manually stop and restart the server to see the applied changes. Can it be used in Deno?</p>
<p>Sorry, but you can&#39;t... but still, there is an alternative: Denon.</p>
<ul>
<li><a href="https://github.com/eliassjogreen/denon">https://github.com/eliassjogreen/denon</a></li>
</ul>
<p>We can use Denon as we use <code>deno run</code> to execute scripts.</p>
<pre><code>➜ denon server.ts
</code></pre><h2 id="jest-jasmine-ava">Jest, Jasmine, Ava...</h2>
<img src="https://aralroca.com/images/blog-images/48.png" alt="Jasmine, Jest, Ava, Mocha logos" class="center" />

<p>In the Node.js ecosystem there are a lot of alternatives for test runners. However, there isn&#39;t one official way to test the Node.js code.</p>
<p>In Deno, there is an official way, you can use the testing std library.</p>
<ul>
<li><a href="https://deno.land/std/testing">https://deno.land/std/testing</a></li>
</ul>
<pre><code class="hljs language-js"><span class="hljs-keyword">import</span> { assertStrictEq } <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;https://deno.land/std/testing/asserts.ts&#x27;</span>

<span class="hljs-title class_">Deno</span>.<span class="hljs-title function_">test</span>(<span class="hljs-string">&#x27;My first test&#x27;</span>, <span class="hljs-title function_">async</span> () =&gt; {
  <span class="hljs-title function_">assertStrictEq</span>(<span class="hljs-literal">true</span>, <span class="hljs-literal">false</span>)
})
</code></pre><p>To run the tests:</p>
<pre><code>➜  deno test
</code></pre><h2 id="webpack-parcel-rollup">Webpack, Parcel, Rollup...</h2>
<img src="https://aralroca.com/images/blog-images/52.png" alt="Webpack, Parcel, Rollup logos" class="center transparent keepcolor" />

<p>One of the strengths of Deno is that we can use ESmodules with TypeScript without the need for a bundler such as <a href="https://github.com/webpack/webpack">Webpack</a>, <a href="https://github.com/parcel-bundler/parcel">Parcel</a> or <a href="https://github.com/rollup/rollup">Rollup</a>.</p>
<p>However, probably you wonder if given a tree of files, we can make a bundle to put everything in one file to run it on the web.</p>
<p>Well, it&#39;s possible, yes. We can do it with Deno&#39;s CLI. Thus, there&#39;s no need for a third-party bundler.</p>
<pre><code>➜ deno bundle myLib.ts myLib.bundle.js
</code></pre><p>Now it&#39;s ready to be loaded in the browser:</p>
<pre><code class="hljs language-html"><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">type</span>=<span class="hljs-string">&quot;module&quot;</span>&gt;</span><span class="language-javascript">
  <span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> myLib <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;myLib.bundle.js&#x27;</span>
</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
</code></pre><h2 id="prettier">Prettier</h2>
<img src="https://aralroca.com/images/blog-images/49.png" alt="Prettier logo" class="center transparent keepcolor" />

<p>In the last few years <a href="https://prettier.io/">Prettier</a> has become quite well known within the JavaScript ecosystem because with it you don&#39;t have to worry about formatting the files.</p>
<p>And the truth is, it can still be used on Deno but it loses its meaning, because Deno has its own formatter.</p>
<p>You can format your files using this command:</p>
<pre><code>➜  deno fmt
</code></pre><h2 id="npm-scripts">NPM Scripts</h2>
<img src="https://aralroca.com/images/blog-images/50.png" alt="Npm scripts logo" class="center transparent" />

<p>With Deno, the <code>package.json</code> no longer exists. One of the things I really miss are the scripts that were declared in the <code>package.json</code>.</p>
<p>A simple solution would be to use a <code>makefile</code> and execute it with <code>make</code>. However, if you miss the npm syntax, there is an npm-style script runner for Deno:</p>
<ul>
<li><a href="https://github.com/umbopepato/velociraptor">https://github.com/umbopepato/velociraptor</a></li>
</ul>
<p>You can define a file with your scripts:</p>
<pre><code class="hljs language-yaml"><span class="hljs-comment"># scripts.yaml</span>
<span class="hljs-attr">scripts:</span>
  <span class="hljs-attr">start:</span> <span class="hljs-string">deno</span> <span class="hljs-string">run</span> <span class="hljs-string">--allow-net</span> <span class="hljs-string">server.ts</span>
  <span class="hljs-attr">test:</span> <span class="hljs-string">deno</span> <span class="hljs-string">test</span> <span class="hljs-string">--allow-net</span> <span class="hljs-string">server_test.ts</span>
</code></pre><p>Execute with:</p>
<pre><code>➜  vr run &lt;SCRIPT&gt;
</code></pre><p>Another alternative is <a href="https://github.com/BentoumiTech/denox">denox</a>, very similar to Velociraptor.</p>
<h2 id="nvm">Nvm</h2>
<img src="https://aralroca.com/images/blog-images/51.png" alt="Version semantics" class="center transparent" />

<p><a href="https://github.com/nvm-sh/nvm">Nvm</a> is a CLI to manage multiple active Node versions, to easy upgrade or downgrade versions depending on your projects.</p>
<p>A <code>nvm</code> equivalent in Deno is <code>dvm</code>.</p>
<ul>
<li><a href="https://github.com/axetroy/dvm">https://github.com/axetroy/dvm</a></li>
</ul>
<pre><code class="hljs language-bh">➜  dvm use 1.0.0
</code></pre><h2 id="npx">Npx</h2>
<p><a href="https://github.com/npm/npx">Npx</a> in recent years has become very popular to execute npm packages without having to install them. Now many projects won&#39;t exist within npm because Deno is a separate ecosystem. So, how can we execute Deno modules without having to install them with <code>deno install https://url-of-module.ts</code>?</p>
<p>In the same way that we run our project, instead of a file we put the URL of the module:</p>
<pre><code>➜  deno run https://deno.land/std/examples/welcome.ts
</code></pre><p>As you can see, not only we have to remember the name of the module, but the whole URL, which makes it a little more difficult to use. On the other hand it gives a lot more flexibility as we can run any file, not just what&#39;s specified as a binary in the <code>package.json</code> like <code>npx</code>.</p>
<h2 id="run-on-a-docker">Run on a Docker</h2>
<img src="https://aralroca.com/images/blog-images/53.png" alt="Docker logo" class="center transparent keepcolor" />

<p>To run Deno inside a Docker, we can create this Dockerfile:</p>
<pre><code class="hljs language-dockerfile"><span class="hljs-keyword">FROM</span> hayd/alpine-deno:<span class="hljs-number">1.0</span>.<span class="hljs-number">0</span>

<span class="hljs-keyword">EXPOSE</span> <span class="hljs-number">1993</span>  <span class="hljs-comment"># Port.</span>

<span class="hljs-keyword">WORKDIR</span><span class="language-bash"> /app</span>

<span class="hljs-keyword">USER</span> deno

<span class="hljs-keyword">COPY</span><span class="language-bash"> deps.ts .</span>
<span class="hljs-keyword">RUN</span><span class="language-bash"> deno cache deps.ts <span class="hljs-comment"># Cache the deps</span></span>

<span class="hljs-keyword">ADD</span><span class="language-bash"> . .</span>
<span class="hljs-keyword">RUN</span><span class="language-bash"> deno cache main.ts <span class="hljs-comment"># main entrypoint.</span></span>

<span class="hljs-keyword">CMD</span><span class="language-bash"> [<span class="hljs-string">&quot;--allow-net&quot;</span>, <span class="hljs-string">&quot;main.ts&quot;</span>]</span>
</code></pre><p>To build + run it:</p>
<pre><code>➜  docker build -t app . &amp;&amp; docker run -it --init -p 1993:1993 app
</code></pre><p>Repo: <a href="https://github.com/hayd/deno-docker">https://github.com/hayd/deno-docker</a></p>
<h2 id="run-as-a-lambda">Run as a lambda</h2>
<img src="https://aralroca.com/images/blog-images/54.png" alt="Lambda symbol" class="center transparent" />

<p>To use Deno as a lambda, there is a module in Deno STD library. <a href="https://deno.land/x/lambda">https://deno.land/x/lambda</a>.</p>
<pre><code class="hljs language-ts"><span class="hljs-keyword">import</span> {
  <span class="hljs-title class_">APIGatewayProxyEvent</span>,
  <span class="hljs-title class_">APIGatewayProxyResult</span>,
  <span class="hljs-title class_">Context</span>,
} <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;https://deno.land/x/lambda/mod.ts&#x27;</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">handler</span>(<span class="hljs-params">
  <span class="hljs-attr">event</span>: <span class="hljs-title class_">APIGatewayProxyEvent</span>,
  <span class="hljs-attr">context</span>: <span class="hljs-title class_">Context</span>
</span>): <span class="hljs-title class_">Promise</span>&lt;<span class="hljs-title class_">APIGatewayProxyResult</span>&gt; {
  <span class="hljs-keyword">return</span> {
    <span class="hljs-attr">body</span>: <span class="hljs-string">`Welcome to deno <span class="hljs-subst">${Deno.version.deno}</span> 🦕`</span>,
    <span class="hljs-attr">headers</span>: { <span class="hljs-string">&#x27;content-type&#x27;</span>: <span class="hljs-string">&#x27;text/html;charset=utf8&#x27;</span> },
    <span class="hljs-attr">statusCode</span>: <span class="hljs-number">200</span>,
  }
}
</code></pre><p>Interesting references:</p>
<ul>
<li>Deno in Vercel: <a href="https://github.com/lucacasonato/now-deno">https://github.com/lucacasonato/now-deno</a></li>
<li>Deno in AWS: <a href="https://blog.begin.com/deno-runtime-support-for-architect-805fcbaa82c3">https://blog.begin.com/deno-runtime-support-for-architect-805fcbaa82c3</a></li>
</ul>
<h2 id="conclusion">Conclusion</h2>
<p>I&#39;m sure I forgot some Node topics and their Deno alternative, let me know if there&#39;s anything I missed that you&#39;d like me to explain. I hope this article helps you break the ice with Deno.</p>
<p>To explore all libraries you can use with Deno:</p>
<ul>
<li><a href="https://deno.land/std">https://deno.land/std</a></li>
<li><a href="https://deno.land/x">https://deno.land/x</a></li>
<li><a href="https://www.pika.dev/">https://www.pika.dev/</a></li>
</ul>
]]></content:encoded>
            </item>
            <item>
              <title>GitHub action to publish your blog post to dev.to</title>
              <description>Learn how to automatize publishing to dev.to from your GitHub repo.</description>
              <link>https://aralroca.com/blog/ghaction-devto</link>
              <guid isPermaLink="false">https://aralroca.com/blog/ghaction-devto/</guid>
              <pubDate>Wed Sep 02 2020 00:00:00 GMT+0000 (Coordinated Universal Time)</pubDate>
              <content:encoded><![CDATA[<p>I started writing when I joined <a href="https://dev.to/">dev.to</a> in 2017, joining the community motivated me.</p>
<p>After a few articles I decided to create my own personal <a href="https://aralroca.com/blog">blog</a>. However, I&#39;ve always wanted to continue contributing to dev.to. That&#39;s why I post articles on my personal blog and then share them on dev.to with the canonical. I suppose it&#39;s a standard practice and more than one of you are doing it.</p>
<p>In order to make my life a little easier, I&#39;ve recently made a GitHub action that posts directly to dev.to when it detects a new article on my blog.</p>
<h2 id="how-i-detect-a-new-post">How I detect a new post</h2>
<p>To know if the article is new and needs to be published, you can use the markdown metadata to find out. In my case, I keep the <strong>date of publication</strong> as metadata (in case I want to publish it another day even if it&#39;s merged to master).</p>
<p>Then, once it&#39;s posted to dev.to with the GitHub action I create another metadata so it gets tagged as published.</p>
<p>Why? Because the GitHub action will run:</p>
<ul>
<li>Whenever something is <strong>pushed to master</strong>.</li>
<li><strong>Every day</strong> at 17:00 UTC.</li>
</ul>
<p>This way, marking the post as already published, we avoid publishing it twice if we push an article to master at 16:00.</p>
<figure align="center">
  <img src="https://aralroca.com/images/blog-images/ghaction-devto.png" alt="GH action diagram to publish to dev.to" class="center transparent" />
  <figcaption><small>GH action diagram to publish to dev.to</small></figcaption>
</figure>

<h2 id="github-action-in-action">GitHub action in action</h2>
<pre><code class="hljs language-yml"><span class="hljs-attr">name:</span> <span class="hljs-string">Publishing</span> <span class="hljs-string">post</span>

<span class="hljs-attr">on:</span>
  <span class="hljs-attr">push:</span>
    <span class="hljs-attr">branches:</span> [<span class="hljs-string">master</span>]
  <span class="hljs-attr">schedule:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">cron:</span> <span class="hljs-string">&#x27;0 17 */1 * *&#x27;</span>

<span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">build:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>

    <span class="hljs-attr">strategy:</span>
      <span class="hljs-attr">matrix:</span>
        <span class="hljs-attr">node-version:</span> [<span class="hljs-number">14.</span><span class="hljs-string">x</span>]

    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v2</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Publishing</span> <span class="hljs-string">post</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/setup-node@v1</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">node-version:</span> <span class="hljs-string">${{</span> <span class="hljs-string">matrix.node-version</span> <span class="hljs-string">}}</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">run:</span> <span class="hljs-string">yarn</span> <span class="hljs-string">install</span> <span class="hljs-string">--pure-lockfile</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">run:</span> <span class="hljs-string">yarn</span> <span class="hljs-string">run</span> <span class="hljs-string">publish:post</span>
        <span class="hljs-attr">env:</span>
          <span class="hljs-attr">DEV_TO:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.DEV_TO</span> <span class="hljs-string">}}</span> 
      <span class="hljs-bullet">-</span> <span class="hljs-attr">run:</span> <span class="hljs-string">|
          git config user.name aralroca
          git config user.email aral-rg@hotmail.com
          git add -A
          git diff --quiet &amp;&amp; git diff --staged --quiet || git commit -m &quot;[bot] Published to dev.to&quot;
          git push origin master</span>
</code></pre><p>What does it do?</p>
<ol>
<li>Programs the action on <strong>push to master</strong> and <strong>every day at 17:00</strong> UTC using a cron.</li>
<li>Installs dependencies with <code>yarn install --pure-lockfile</code></li>
<li>Sets environment variable <code>DEV_TO</code> using <a href="https://docs.github.com/en/actions/configuring-and-managing-workflows/creating-and-storing-encrypted-secrets">GitHub secrets</a>. This is required for our script.</li>
<li>Runs our script to publish to dev.to</li>
<li>Commits and pushes to master only when there are changes.</li>
</ol>
<h2 id="script-to-publish-to-devto">Script to publish to dev.to</h2>
<p>In our <code>package.json</code> file we have to indicate that the script runs our node file:</p>
<pre><code class="hljs language-json"><span class="hljs-punctuation">{</span>
  <span class="hljs-attr">&quot;scripts&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span>
    <span class="hljs-attr">&quot;publish:post&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;node ./publish/index.js&quot;</span>
  <span class="hljs-punctuation">}</span>
<span class="hljs-punctuation">}</span>
</code></pre><p>This is the content of our script that publishes articles to <strong>dev.to</strong>:</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">async</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">deploy</span>(<span class="hljs-params"></span>) {
  <span class="hljs-keyword">const</span> post = <span class="hljs-title function_">getNewPost</span>()

  <span class="hljs-keyword">if</span> (!post) {
    <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">&#x27;No new post detected to publish.&#x27;</span>)
    process.<span class="hljs-title function_">exit</span>()
  }

  <span class="hljs-keyword">await</span> <span class="hljs-title function_">deployToDevTo</span>(post)
}

<span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">&#x27;Start publishing&#x27;</span>)
<span class="hljs-title function_">deploy</span>()
  .<span class="hljs-title function_">then</span>(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">&#x27;Published!&#x27;</span>)
    process.<span class="hljs-title function_">exit</span>()
  })
  .<span class="hljs-title function_">catch</span>(<span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> {
    <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">&#x27;ERROR publishing:&#x27;</span>, e)
    process.<span class="hljs-title function_">exit</span>(<span class="hljs-number">1</span>)
  })
</code></pre><p>The <code>getNewPost</code> function returns the post already formatted in the way <strong>dev.to</strong> needs, <code>null</code> in case that there aren&#39;t new posts:</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">const</span> fs = <span class="hljs-built_in">require</span>(<span class="hljs-string">&#x27;fs&#x27;</span>)
<span class="hljs-keyword">const</span> path = <span class="hljs-built_in">require</span>(<span class="hljs-string">&#x27;path&#x27;</span>)
<span class="hljs-keyword">const</span> matter = <span class="hljs-built_in">require</span>(<span class="hljs-string">&#x27;gray-matter&#x27;</span>)

<span class="hljs-keyword">const</span> deployToDevTo = <span class="hljs-built_in">require</span>(<span class="hljs-string">&#x27;./dev-to&#x27;</span>)

<span class="hljs-keyword">function</span> <span class="hljs-title function_">getNewPost</span>(<span class="hljs-params"></span>) {
  <span class="hljs-keyword">const</span> today = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Date</span>()

  <span class="hljs-keyword">return</span> (
    fs
      .<span class="hljs-title function_">readdirSync</span>(<span class="hljs-string">&#x27;posts&#x27;</span>)
      .<span class="hljs-title function_">map</span>(<span class="hljs-function">(<span class="hljs-params">slug</span>) =&gt;</span> {
        <span class="hljs-keyword">const</span> post = <span class="hljs-title function_">matter</span>(fs.<span class="hljs-title function_">readFileSync</span>(path.<span class="hljs-title function_">join</span>(<span class="hljs-string">&#x27;posts&#x27;</span>, slug)))
        <span class="hljs-keyword">return</span> { ...post, slug }
      })
      .<span class="hljs-title function_">filter</span>(<span class="hljs-function">(<span class="hljs-params">p</span>) =&gt;</span> {
        <span class="hljs-keyword">const</span> created = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Date</span>(p.<span class="hljs-property">data</span>.<span class="hljs-property">created</span>)

        <span class="hljs-keyword">return</span> (
          !p.<span class="hljs-property">data</span>.<span class="hljs-property">published_devto</span> &amp;&amp;
          created.<span class="hljs-title function_">getDate</span>() === today.<span class="hljs-title function_">getDate</span>() &amp;&amp;
          created.<span class="hljs-title function_">getMonth</span>() === today.<span class="hljs-title function_">getMonth</span>() &amp;&amp;
          created.<span class="hljs-title function_">getFullYear</span>() === today.<span class="hljs-title function_">getFullYear</span>()
        )
      })
      .<span class="hljs-title function_">map</span>(<span class="hljs-function">(<span class="hljs-params">{ slug, data, content }</span>) =&gt;</span> {
        <span class="hljs-keyword">const</span> id = slug.<span class="hljs-title function_">replace</span>(<span class="hljs-string">&#x27;.md&#x27;</span>, <span class="hljs-string">&#x27;&#x27;</span>)
        <span class="hljs-keyword">const</span> canonical = <span class="hljs-string">`https://aralroca.com/blog/<span class="hljs-subst">${id}</span>`</span>
        <span class="hljs-keyword">const</span> body = <span class="hljs-string">`***Original article: <span class="hljs-subst">${canonical}</span>***\n<span class="hljs-subst">${content}</span>`</span>

        <span class="hljs-keyword">return</span> {
          <span class="hljs-attr">body_markdown</span>: body,
          <span class="hljs-attr">canonical_url</span>: canonical,
          <span class="hljs-attr">created</span>: data.<span class="hljs-property">created</span>,
          <span class="hljs-attr">description</span>: data.<span class="hljs-property">description</span>,
          <span class="hljs-attr">main_image</span>: data.<span class="hljs-property">cover_image</span>,
          <span class="hljs-attr">published</span>: <span class="hljs-literal">true</span>,
          <span class="hljs-attr">series</span>: data.<span class="hljs-property">series</span>,
          slug,
          <span class="hljs-attr">tags</span>: data.<span class="hljs-property">tags</span>,
          <span class="hljs-attr">title</span>: data.<span class="hljs-property">title</span>,
        }
      })[<span class="hljs-number">0</span>] || <span class="hljs-literal">null</span>
  )
}
</code></pre><p>I use the <code>gray-matter</code> library to retrieve the markdown metadata and its content.</p>
<p>Here&#39;s the <code>deployToDevTo</code> function used in our script:</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">const</span> fetch = <span class="hljs-built_in">require</span>(<span class="hljs-string">&#x27;isomorphic-unfetch&#x27;</span>)
<span class="hljs-keyword">const</span> path = <span class="hljs-built_in">require</span>(<span class="hljs-string">&#x27;path&#x27;</span>)
<span class="hljs-keyword">const</span> fs = <span class="hljs-built_in">require</span>(<span class="hljs-string">&#x27;fs&#x27;</span>)

<span class="hljs-keyword">function</span> <span class="hljs-title function_">createPost</span>(<span class="hljs-params">article</span>) {
  <span class="hljs-keyword">return</span> <span class="hljs-title function_">fetch</span>(<span class="hljs-string">&#x27;https://dev.to/api/articles&#x27;</span>, {
    <span class="hljs-attr">method</span>: <span class="hljs-string">&#x27;POST&#x27;</span>,
    <span class="hljs-attr">headers</span>: {
      <span class="hljs-string">&#x27;api-key&#x27;</span>: process.<span class="hljs-property">env</span>.<span class="hljs-property">DEV_TO</span>,
      <span class="hljs-string">&#x27;content-type&#x27;</span>: <span class="hljs-string">&#x27;application/json&#x27;</span>,
    },
    <span class="hljs-attr">body</span>: <span class="hljs-title class_">JSON</span>.<span class="hljs-title function_">stringify</span>({ article }),
  })
    .<span class="hljs-title function_">then</span>(<span class="hljs-function">(<span class="hljs-params">r</span>) =&gt;</span> r.<span class="hljs-title function_">json</span>())
    .<span class="hljs-title function_">then</span>(<span class="hljs-function">(<span class="hljs-params">res</span>) =&gt;</span> {
      <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">&#x27;dev.to -&gt; OK&#x27;</span>, <span class="hljs-string">`https://dev.to/aralroca/<span class="hljs-subst">${res.slug}</span>`</span>)
      <span class="hljs-keyword">return</span> res.<span class="hljs-property">slug</span>
    })
    .<span class="hljs-title function_">catch</span>(<span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> {
      <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">&#x27;dev.to -&gt; KO&#x27;</span>, e)
    })
}

<span class="hljs-keyword">async</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">deployToDevTo</span>(<span class="hljs-params">article</span>) {
  <span class="hljs-keyword">const</span> devToId = <span class="hljs-keyword">await</span> <span class="hljs-title function_">createPost</span>(article)

  <span class="hljs-keyword">if</span> (!devToId) <span class="hljs-keyword">return</span>

  <span class="hljs-keyword">const</span> postPath = path.<span class="hljs-title function_">join</span>(<span class="hljs-string">&#x27;posts&#x27;</span>, article.<span class="hljs-property">slug</span>)
  <span class="hljs-keyword">const</span> post = fs.<span class="hljs-title function_">readFileSync</span>(postPath).<span class="hljs-title function_">toString</span>()
  <span class="hljs-keyword">let</span> occurrences = <span class="hljs-number">0</span>

  <span class="hljs-comment">// Write &#x27;published_devto&#x27; metadata before the second occurrence of ---</span>
  fs.<span class="hljs-title function_">writeFileSync</span>(
    postPath,
    post.<span class="hljs-title function_">replace</span>(<span class="hljs-regexp">/---/g</span>, <span class="hljs-function">(<span class="hljs-params">m</span>) =&gt;</span> {
      occurrences += <span class="hljs-number">1</span>
      <span class="hljs-keyword">if</span> (occurrences === <span class="hljs-number">2</span>) <span class="hljs-keyword">return</span> <span class="hljs-string">`published_devto: true\n<span class="hljs-subst">${m}</span>`</span>
      <span class="hljs-keyword">return</span> m
    })
  )
}
</code></pre><p>We request to the <a href="https://docs.dev.to/api/">dev.to API</a> to upload the article and then modify our markdown file to add the <code>published_devto: true</code> metadata. This way, our GitHub action will detect that there are changes to upload to master.</p>
<h2 id="conclusions">Conclusions</h2>
<p>In this short article we&#39;ve seen how to create a GitHub action to post automatically our personal blog new articles to <a href="https://dev.to">dev.to</a>. I hope you find it useful.</p>
]]></content:encoded>
            </item>
            <item>
              <title>How to draw gears in WebGL</title>
              <description>Learn how to draw dynamic 2D complex shapes in WebGL by drawing gears.</description>
              <link>https://aralroca.com/blog/how-to-draw-gears-in-webgl</link>
              <guid isPermaLink="false">https://aralroca.com/blog/how-to-draw-gears-in-webgl/</guid>
              <pubDate>Sun Aug 09 2020 00:00:00 GMT+0000 (Coordinated Universal Time)</pubDate>
              <content:encoded><![CDATA[<p>In this article we continue what we started in &quot;<a href="https://aralroca.com/blog/first-steps-in-webgl">First steps in WebGL</a>&quot;, where we saw <a href="https://aralroca.com/blog/first-steps-in-webgl#what-is-webgl">what it is and how it works</a> internally: the <a href="https://aralroca.com/blog/first-steps-in-webgl#glsl-and-shaders">shaders</a>, the <a href="https://aralroca.com/blog/first-steps-in-webgl#create-program-from-shaders">program</a>, <a href="https://aralroca.com/blog/first-steps-in-webgl#create-buffers">buffers</a>, how to <a href="https://aralroca.com/blog/first-steps-in-webgl#create-buffers">link data from CPU to GPU</a>, and finally how to <a href="https://aralroca.com/blog/first-steps-in-webgl#drawing-the-triangle">render a triangle</a>. To understand all this well, I recommend first reading the previous chapter.</p>
<p>Here, instead of rendering a triangle, we&#39;ll see how to render more complex structures and how to give it movement. To do that we&#39;ll implement three <strong>dynamic gears</strong>:</p>
<figure align="center">
  <img src="https://aralroca.com/images/blog-images/gears.gif" alt="Gears" class="center" />
  <figcaption><small>Fig 1: gears generated by us in this article</small></figcaption>
</figure>

<p><strong>We will cover the following:</strong></p>
<ul>
<li><a href="#identifying-shapes">Identifying shapes</a><ul>
<li><a href="#circle-with-border">Circle with border</a></li>
<li><a href="#circle-with-filled-color">Circle with filled color</a></li>
<li><a href="#circle-with-teeth">Circle with teeth</a></li>
</ul>
</li>
<li><a href="#identifying-data-to-draw">Identifying data to draw</a></li>
<li><a href="#how-we-will-implement-the-rotation">How we will implement the rotation</a></li>
<li><a href="#lets-implement-it">Let&#39;s implement it!</a><ul>
<li><a href="#initialize-program-with-shaders">Initialize program with shaders</a></li>
<li><a href="#draw-each-frame--calculate-rotation-angles">Draw each frame + calculate rotation angles</a></li>
<li><a href="#draw-gears">Draw gears</a></li>
</ul>
</li>
<li><a href="#show-me-all-the-code">Show me all the code</a></li>
<li><a href="#conclusion">Conclusion</a></li>
<li><a href="#references">References</a></li>
</ul>
<h2 id="identifying-shapes">Identifying shapes</h2>
<p>The gears we want to draw are composed of <strong>circles</strong>. Among these circles there are certain varieties: a circle with teeth, a circle with colored border and circle filled with a color.</p>
<figure align="center">
  <img src="https://aralroca.com/images/blog-images/gear-shapes.png" alt="Indentifying gear shapes" class="center" />
  <figcaption><small>Fig 2: three different circles</small></figcaption>
</figure>

<p>Therefore, this confirms that we can draw these gears by drawing circles but, as we saw in the previous article, in WebGL you can only rasterize triangles, points, and lines... So, what&#39;s the difference between these circles and how can we make each of them?</p>
<h3 id="circle-with-border">Circle with border</h3>
<p>To draw a circle with a border, we&#39;ll use multiple <strong>points</strong>:</p>
<figure align="center">
  <img src="https://aralroca.com/images/blog-images/stroke-circle.gif" alt="Circle with points" class="center" />
  <figcaption><small>Fig 3: mouting a circle with 360 points</small></figcaption>
</figure>

<h3 id="circle-with-filled-color">Circle with filled color</h3>
<p>To draw a circle with a filled color, we&#39;ll use multiple <strong>triangles</strong>:</p>
<figure align="center">
  <img src="https://aralroca.com/images/blog-images/circle_triangle.gif" alt="Filled circle with triangle strip" class="center" />
  <figcaption><small>Fig 4: filled circle with triangle strip</small></figcaption>
</figure>

<p>The drawing mode needed for this is <strong>Triangle strip</strong>:</p>
<blockquote>
<p>A triangle strip is a series of connected triangles from the triangle mesh, sharing vertices, allowing for more efficient memory usage for computer graphics. They are more efficient than triangle lists without indexing, but usually equally fast or slower than indexed triangle lists. The primary reason to use triangle strips is to reduce the amount of data needed to create a series of triangles. The number of vertices stored in memory is reduced from 3N to N+2, where N is the number of triangles to be drawn. This allows for less use of disk space, as well as making them faster to load into RAM.
<small>Source: <a href="ttps://en.wikipedia.org/wiki/Triangle_strip#:~:text=A%20triangle%20strip%20is%20a,slower%20than%20indexed%20triangle%20lists.">Wikipedia</a></small></p>
</blockquote>
<h3 id="circle-with-teeth">Circle with teeth</h3>
<p>For the gear teeth, we&#39;ll also use <strong>triangles</strong>. This time, without the &quot;strip&quot; mode. This way we&#39;ll draw triangles that go from the center of the circumference to the outside.</p>
<figure align="center">
  <img src="https://aralroca.com/images/blog-images/teeth-shape.gif" alt="Teeth of gear" class="center" />
  <figcaption><small>Fig 5: gear teeth are triangles</small></figcaption>
</figure>

<p>While we build the teeth, it&#39;s important that we create another circle inside filled with color to make the effect that the teeth are coming out of the circle itself.</p>
<h2 id="identifying-data-to-draw">Identifying data to draw</h2>
<p>One thing these 3 types of figures have in common is that we can calculate their coordinates from 2 variables:</p>
<ul>
<li>Center of the circle (<strong>x</strong> and <strong>y</strong>)</li>
<li>Radius</li>
</ul>
<p>As seen in the previous article, the coordinates within webGL go from -1 to 1. So let&#39;s locate the center of each piece of gear and its radius:</p>
<br />
<figure align="center">
  <img src="https://aralroca.com/images/blog-images/gears_coordinates.png" alt="Gears coordinates" class="center" />
  <figcaption><small>Fig 6: sketch of the coordinates of the center and the radius of each gear</small></figcaption>
</figure>

<p>In addition, we have optional variables for specific figures such as:</p>
<ul>
<li>Number of teeth</li>
<li>Stroke color <em>(color of the border)</em></li>
<li>Fill color</li>
<li>Children <em>(more pieces of the same gear with the same data structure)</em></li>
<li>Direction of the rotation <em>(only valid for the parent)</em></li>
</ul>
<p>At the end, in JavaScript, we&#39;ll have this array with the data of the three gears and all their pieces:</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">const</span> x1 = <span class="hljs-number">0.1</span>
<span class="hljs-keyword">const</span> y1 = -<span class="hljs-number">0.2</span>

<span class="hljs-keyword">const</span> x2 = -<span class="hljs-number">0.42</span>
<span class="hljs-keyword">const</span> y2 = <span class="hljs-number">0.41</span>

<span class="hljs-keyword">const</span> x3 = <span class="hljs-number">0.56</span>
<span class="hljs-keyword">const</span> y3 = <span class="hljs-number">0.28</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> gears = [
  {
    <span class="hljs-attr">center</span>: [x1, y1],
    <span class="hljs-attr">direction</span>: <span class="hljs-string">&#x27;counterclockwise&#x27;</span>,
    <span class="hljs-attr">numberOfTeeth</span>: <span class="hljs-number">20</span>,
    <span class="hljs-attr">radius</span>: <span class="hljs-number">0.45</span>,
    <span class="hljs-attr">fillColor</span>: [<span class="hljs-number">0.878</span>, <span class="hljs-number">0.878</span>, <span class="hljs-number">0.878</span>],
    <span class="hljs-attr">children</span>: [
      {
        <span class="hljs-attr">center</span>: [x1, y1],
        <span class="hljs-attr">radius</span>: <span class="hljs-number">0.4</span>,
        <span class="hljs-attr">strokeColor</span>: [<span class="hljs-number">0.682</span>, <span class="hljs-number">0.682</span>, <span class="hljs-number">0.682</span>],
      },
      {
        <span class="hljs-attr">center</span>: [x1, y1],
        <span class="hljs-attr">radius</span>: <span class="hljs-number">0.07</span>,
        <span class="hljs-attr">fillColor</span>: [<span class="hljs-number">0.741</span>, <span class="hljs-number">0.741</span>, <span class="hljs-number">0.741</span>],
        <span class="hljs-attr">strokeColor</span>: [<span class="hljs-number">0.682</span>, <span class="hljs-number">0.682</span>, <span class="hljs-number">0.682</span>],
      },
      {
        <span class="hljs-attr">center</span>: [x1 - <span class="hljs-number">0.23</span>, y1],
        <span class="hljs-attr">radius</span>: <span class="hljs-number">0.12</span>,
        <span class="hljs-attr">fillColor</span>: [<span class="hljs-number">1</span>, <span class="hljs-number">1</span>, <span class="hljs-number">1</span>],
        <span class="hljs-attr">strokeColor</span>: [<span class="hljs-number">0.682</span>, <span class="hljs-number">0.682</span>, <span class="hljs-number">0.682</span>],
      },
      {
        <span class="hljs-attr">center</span>: [x1, y1 - <span class="hljs-number">0.23</span>],
        <span class="hljs-attr">radius</span>: <span class="hljs-number">0.12</span>,
        <span class="hljs-attr">fillColor</span>: [<span class="hljs-number">1</span>, <span class="hljs-number">1</span>, <span class="hljs-number">1</span>],
        <span class="hljs-attr">strokeColor</span>: [<span class="hljs-number">0.682</span>, <span class="hljs-number">0.682</span>, <span class="hljs-number">0.682</span>],
      },
      {
        <span class="hljs-attr">center</span>: [x1 + <span class="hljs-number">0.23</span>, y1],
        <span class="hljs-attr">radius</span>: <span class="hljs-number">0.12</span>,
        <span class="hljs-attr">fillColor</span>: [<span class="hljs-number">1</span>, <span class="hljs-number">1</span>, <span class="hljs-number">1</span>],
        <span class="hljs-attr">strokeColor</span>: [<span class="hljs-number">0.682</span>, <span class="hljs-number">0.682</span>, <span class="hljs-number">0.682</span>],
      },
      {
        <span class="hljs-attr">center</span>: [x1, y1 + <span class="hljs-number">0.23</span>],
        <span class="hljs-attr">radius</span>: <span class="hljs-number">0.12</span>,
        <span class="hljs-attr">fillColor</span>: [<span class="hljs-number">1</span>, <span class="hljs-number">1</span>, <span class="hljs-number">1</span>],
        <span class="hljs-attr">strokeColor</span>: [<span class="hljs-number">0.682</span>, <span class="hljs-number">0.682</span>, <span class="hljs-number">0.682</span>],
      },
    ],
  },
  {
    <span class="hljs-attr">center</span>: [x2, y2],
    <span class="hljs-attr">direction</span>: <span class="hljs-string">&#x27;clockwise&#x27;</span>,
    <span class="hljs-attr">numberOfTeeth</span>: <span class="hljs-number">12</span>,
    <span class="hljs-attr">radius</span>: <span class="hljs-number">0.3</span>,
    <span class="hljs-attr">fillColor</span>: [<span class="hljs-number">0.741</span>, <span class="hljs-number">0.741</span>, <span class="hljs-number">0.741</span>],
    <span class="hljs-attr">children</span>: [
      {
        <span class="hljs-attr">center</span>: [x2, y2],
        <span class="hljs-attr">radius</span>: <span class="hljs-number">0.25</span>,
        <span class="hljs-attr">strokeColor</span>: [<span class="hljs-number">0.682</span>, <span class="hljs-number">0.682</span>, <span class="hljs-number">0.682</span>],
      },
      {
        <span class="hljs-attr">center</span>: [x2, y2],
        <span class="hljs-attr">radius</span>: <span class="hljs-number">0.1</span>,
        <span class="hljs-attr">fillColor</span>: [<span class="hljs-number">0.682</span>, <span class="hljs-number">0.682</span>, <span class="hljs-number">0.682</span>],
        <span class="hljs-attr">strokeColor</span>: [<span class="hljs-number">0.6</span>, <span class="hljs-number">0.6</span>, <span class="hljs-number">0.6</span>],
      },
    ],
  },
  {
    <span class="hljs-attr">center</span>: [x3, y3],
    <span class="hljs-attr">direction</span>: <span class="hljs-string">&#x27;clockwise&#x27;</span>,
    <span class="hljs-attr">numberOfTeeth</span>: <span class="hljs-number">6</span>,
    <span class="hljs-attr">radius</span>: <span class="hljs-number">0.15</span>,
    <span class="hljs-attr">fillColor</span>: [<span class="hljs-number">0.741</span>, <span class="hljs-number">0.741</span>, <span class="hljs-number">0.741</span>],
    <span class="hljs-attr">children</span>: [
      {
        <span class="hljs-attr">center</span>: [x3, y3],
        <span class="hljs-attr">radius</span>: <span class="hljs-number">0.1</span>,
        <span class="hljs-attr">strokeColor</span>: [<span class="hljs-number">0.682</span>, <span class="hljs-number">0.682</span>, <span class="hljs-number">0.682</span>],
      },
      {
        <span class="hljs-attr">center</span>: [x3, y3],
        <span class="hljs-attr">radius</span>: <span class="hljs-number">0.02</span>,
        <span class="hljs-attr">fillColor</span>: [<span class="hljs-number">0.682</span>, <span class="hljs-number">0.682</span>, <span class="hljs-number">0.682</span>],
        <span class="hljs-attr">strokeColor</span>: [<span class="hljs-number">0.6</span>, <span class="hljs-number">0.6</span>, <span class="hljs-number">0.6</span>],
      },
    ],
  },
]
</code></pre><p>For the colors, a little reminder: they go from 0 to 1, instead of 0 to 255, or 0 to F, as we are accustomed in CSS. For example <code>[0.682, 0.682, 0.682]</code> would be equivalent to <code>rgb(174, 174, 174)</code> and <code>#AEAEAE</code>.</p>
<h2 id="how-we-will-implement-the-rotation">How we will implement the rotation</h2>
<p>Before we begin the implementation, we need to know how to implement the rotation of each gear.</p>
<p>In order to understand the rotation and other linear transformations, I highly recommend the serie about <strong>linear algebra</strong> from <a href="https://www.youtube.com/watch?v=fNk_zzaMoSs&list=PLZHQObOWTQDPD3MizzM2xVFitgF8hE_ab"><strong>3blue1brown</strong></a> YouTube channel. In special, this video explains it very well:</p>


<p>To sum up, if we multiply our positions by any matrix, it receives a transformation. We have to multiply each gear position by the rotation matrix. We need to add every &quot;transformation&quot; in front of it. If we want to rotate, we will do <code>rotation * positions</code> instead of <code>positions * rotation</code>.</p>
<p>We can create the rotation matrix by knowing the angle in radians:</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">function</span> <span class="hljs-title function_">rotation</span>(<span class="hljs-params">angleInRadians = <span class="hljs-number">0</span></span>) {
  <span class="hljs-keyword">const</span> c = <span class="hljs-title class_">Math</span>.<span class="hljs-title function_">cos</span>(angleInRadians)
  <span class="hljs-keyword">const</span> s = <span class="hljs-title class_">Math</span>.<span class="hljs-title function_">sin</span>(angleInRadians)

  <span class="hljs-keyword">return</span> [
    c, -s, <span class="hljs-number">0</span>, 
    s, c, <span class="hljs-number">0</span>, 
    <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">1</span>
  ]
}
</code></pre><p>This way we can make each gear turn differently by multiplying the positions of each gear with its respective rotation matrix. To have a real rotation effect, in each frame we must increase the angle a little bit until it gives the complete turn and the angle returns to 0.</p>
<p>However, it&#39;s not enough to simply multiply our positions with this matrix. If you do it, you&#39;ll get this:</p>
<pre><code class="hljs language-glsl">rotationMatrix * positionMatrix <span class="hljs-comment">// This is not what we want.</span>
</code></pre><figure align="center">
  <img src="https://aralroca.com/images/blog-images/rotation-incorrect.gif" class="center" alt="incorrect rotation" />
  <figcaption><small>Fig 7: rotation on the canvas (not what we want)</small></figcaption>
</figure>

<p>We&#39;ve got every gear doing its rotation but the axis of rotation is always the center of the canvas, and that&#39;s incorrect. We want them to rotate on their own center.</p>
<p>In order to fix this, first, we&#39;ll use a transformation named <code>translate</code> to move our gear to the center of the canvas. Then we&#39;ll apply the right rotation (the axis will be the center of the canvas again, but in this case, it&#39;s also the center of the gear), and finally, we&#39;ll move the gear back to its original position (by using <code>translate</code> again).</p>
<p>The translation matrix can be defined as follows:</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">function</span> <span class="hljs-title function_">translation</span>(<span class="hljs-params">tx, ty</span>) {
  <span class="hljs-keyword">return</span> [
    <span class="hljs-number">1</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, 
    <span class="hljs-number">0</span>, <span class="hljs-number">1</span>, <span class="hljs-number">0</span>, 
    tx, ty, <span class="hljs-number">1</span>
  ]
}
</code></pre><p>We&#39;ll create two translation matrices: <code>translation(centerX, centerY)</code> and <code>translation(-centerX, -centerY)</code>. Their center must be the center of each gear.</p>
<p>To get that, we&#39;ll do this matrix multiplication:</p>
<pre><code class="hljs language-glsl"><span class="hljs-comment">// Now they will turn on their axis</span>
translationMatrix * rotationMatrix * translationToOriginMatrix * positionMatrix
</code></pre><figure align="center">
  <img src="https://aralroca.com/images/blog-images/rotation-ok.gif" class="center" alt="correct rotation" />
  <figcaption><small>Fig 8: self-rotation (what we want)</small></figcaption>
</figure>

<p>You are probably wondering how to do that each gear spins at its own speed.</p>
<p>There&#39;s a simple formula to calculate the speed according to the number of teeth:</p>
<pre><code>(Speed A * Number of teeth A) = (Speed B * Number of teeth B)
</code></pre><p>This way, in each frame we can add a different angle step to each gear and everyone spins at the speed that they&#39;re physically supposed to.</p>
<h2 id="lets-implement-it">Let&#39;s implement it!</h2>
<p>Having reached this section, we now know:</p>
<ul>
<li>What figures we should draw and how.</li>
<li>We have the coordinates of each gear and its parts.</li>
<li>We know how to rotate each gear.</li>
</ul>
<p>Let&#39;s see how to do it with JavaScript and GLSL.</p>
<h3 id="initialize-program-with-shaders">Initialize program with shaders</h3>
<p>Let&#39;s write the <strong>vertex shader</strong> to compute the positions of the vertices:</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">const</span> vertexShader = <span class="hljs-string">`#version 300 es
precision mediump float;
in vec2 position;
uniform mat3 u_rotation;
uniform mat3 u_translation;
uniform mat3 u_moveOrigin;

void main () {
  vec2 movedPosition = (u_translation * u_rotation * u_moveOrigin * vec3(position, 1)).xy;
  gl_Position = vec4(movedPosition, 0.0, 1.0);
  gl_PointSize = 1.0;
}
`</span>
</code></pre><p>Unlike the vertex shader we used in the previous article, we&#39;ll pass the <code>u_translation</code>, <code>u_rotation</code>, and <code>u_moveOrigin</code> matrices, so the <code>gl_Position</code> will be the product of the four matrices (along with the position matrix). This way we <strong>apply the rotation</strong> as we have seen in the previous section. In addition, we&#39;ll <strong>define the size of each point</strong> we draw (which will be useful for the circle with the border) using <code>gl_PointSize</code>.</p>
<blockquote>
<p><strong>Note</strong>: Matrix multiplication is something we could do directly on the CPU with JavaScript and already pass the final matrix here, but the truth is that the GPU is made precisely for matrix operations so it&#39;s much better for performance to do it on the shader. Besides, from JavaScript, we would need a helper to do this multiplication, since we can&#39;t multiply arrays directly.</p>
</blockquote>
<p>Let&#39;s write the <strong>fragment shader</strong> to compute the color of each pixel corresponding to each location:</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">const</span> fragmentShader = <span class="hljs-string">`#version 300 es
precision mediump float;
out vec4 color;
uniform vec3 inputColor;

void main () {
   color = vec4(inputColor, 1.0);
}
`</span>
</code></pre><p>As we can see there is no magic added to this fragment, it&#39;s the same as in the previous article. Given a defined color in the CPU with JavaScript, we&#39;ll pass it to the GPU to color our figures.</p>
<p>Now we can create our program with the shaders, adding the lines to get the uniform locations that we defined in the vertex shader. This way, later while running our script we can send each matrix to each uniform location per each frame.</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">const</span> gl = <span class="hljs-title function_">getGLContext</span>(canvas)
<span class="hljs-keyword">const</span> vs = <span class="hljs-title function_">getShader</span>(gl, vertexShader, gl.<span class="hljs-property">VERTEX_SHADER</span>)
<span class="hljs-keyword">const</span> fs = <span class="hljs-title function_">getShader</span>(gl, fragmentShader, gl.<span class="hljs-property">FRAGMENT_SHADER</span>)
<span class="hljs-keyword">const</span> program = <span class="hljs-title function_">getProgram</span>(gl, vs, fs)
<span class="hljs-keyword">const</span> rotationLocation = gl.<span class="hljs-title function_">getUniformLocation</span>(program, <span class="hljs-string">&#x27;u_rotation&#x27;</span>)
<span class="hljs-keyword">const</span> translationLocation = gl.<span class="hljs-title function_">getUniformLocation</span>(program, <span class="hljs-string">&#x27;u_translation&#x27;</span>)
<span class="hljs-keyword">const</span> moveOriginLocation = gl.<span class="hljs-title function_">getUniformLocation</span>(program, <span class="hljs-string">&#x27;u_moveOrigin&#x27;</span>)

<span class="hljs-title function_">run</span>() <span class="hljs-comment">// Let&#x27;s see this in the next section</span>
</code></pre><p>The <code>getGLContext</code>, <code>getShader</code> and <code>getProgram</code> helpers do what we saw in the <a href="https://aralroca.com/blog/first-steps-in-webgl#create-program-from-shaders">previous article</a>. I put them down here:</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">function</span> <span class="hljs-title function_">getGLContext</span>(<span class="hljs-params">canvas, bgColor</span>) {
  <span class="hljs-keyword">const</span> gl = canvas.<span class="hljs-title function_">getContext</span>(<span class="hljs-string">&#x27;webgl2&#x27;</span>)
  <span class="hljs-keyword">const</span> defaultBgColor = [<span class="hljs-number">1</span>, <span class="hljs-number">1</span>, <span class="hljs-number">1</span>, <span class="hljs-number">1</span>]

  gl.<span class="hljs-title function_">clearColor</span>(...(bgColor || defaultBgColor))
  gl.<span class="hljs-title function_">clear</span>(gl.<span class="hljs-property">DEPTH_BUFFER_BIT</span> | gl.<span class="hljs-property">COLOR_BUFFER_BIT</span>)

  <span class="hljs-keyword">return</span> gl
}

<span class="hljs-keyword">function</span> <span class="hljs-title function_">getShader</span>(<span class="hljs-params">gl, shaderSource, shaderType</span>) {
  <span class="hljs-keyword">const</span> shader = gl.<span class="hljs-title function_">createShader</span>(shaderType)

  gl.<span class="hljs-title function_">shaderSource</span>(shader, shaderSource)
  gl.<span class="hljs-title function_">compileShader</span>(shader)

  <span class="hljs-keyword">if</span> (!gl.<span class="hljs-title function_">getShaderParameter</span>(shader, gl.<span class="hljs-property">COMPILE_STATUS</span>)) {
    <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">error</span>(gl.<span class="hljs-title function_">getShaderInfoLog</span>(shader))
  }

  <span class="hljs-keyword">return</span> shader
}

<span class="hljs-keyword">function</span> <span class="hljs-title function_">getProgram</span>(<span class="hljs-params">gl, vs, fs</span>) {
  <span class="hljs-keyword">const</span> program = gl.<span class="hljs-title function_">createProgram</span>()

  gl.<span class="hljs-title function_">attachShader</span>(program, vs)
  gl.<span class="hljs-title function_">attachShader</span>(program, fs)
  gl.<span class="hljs-title function_">linkProgram</span>(program)
  gl.<span class="hljs-title function_">useProgram</span>(program)

  <span class="hljs-keyword">if</span> (!gl.<span class="hljs-title function_">getProgramParameter</span>(program, gl.<span class="hljs-property">LINK_STATUS</span>)) {
    <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">error</span>(gl.<span class="hljs-title function_">getProgramInfoLog</span>(program))
  }

  <span class="hljs-keyword">return</span> program
}
</code></pre><h3 id="draw-each-frame--calculate-rotation-angles">Draw each frame + calculate rotation angles</h3>
<p>The <code>run</code> function we have seen called in the previous section will be responsible for the gears being drawn at a different angle in each frame.</p>
<pre><code class="hljs language-js"><span class="hljs-comment">// step for a gear of 1 tooth</span>
<span class="hljs-comment">// gears with more teeth will be calculated with this formula:</span>
<span class="hljs-comment">// realRotationStep = rotationStep / numberOfTeeth</span>
<span class="hljs-keyword">const</span> rotationStep = <span class="hljs-number">0.2</span>

<span class="hljs-comment">// Angles are all initialized to 0</span>
<span class="hljs-keyword">const</span> angles = <span class="hljs-title class_">Array</span>.<span class="hljs-title function_">from</span>({ <span class="hljs-attr">length</span>: gears.<span class="hljs-property">length</span> }).<span class="hljs-title function_">map</span>(<span class="hljs-function">(<span class="hljs-params">v</span>) =&gt;</span> <span class="hljs-number">0</span>)

<span class="hljs-keyword">function</span> <span class="hljs-title function_">run</span>(<span class="hljs-params"></span>) {
  <span class="hljs-comment">// Calculate the angles of this frame, for each gear</span>
  gears.<span class="hljs-title function_">forEach</span>(<span class="hljs-function">(<span class="hljs-params">gear, index</span>) =&gt;</span> {
    <span class="hljs-keyword">const</span> direction = gear.<span class="hljs-property">direction</span> === <span class="hljs-string">&#x27;clockwise&#x27;</span> ? <span class="hljs-number">1</span> : -<span class="hljs-number">1</span>
    <span class="hljs-keyword">const</span> step = direction * (rotationStep / gear.<span class="hljs-property">numberOfTeeth</span>)

    angles[index] = (angles[index] + step) % <span class="hljs-number">360</span>
  })

  <span class="hljs-title function_">drawGears</span>() <span class="hljs-comment">// Let&#x27;s see this in the next section</span>

  <span class="hljs-comment">// Render next frame</span>
  <span class="hljs-variable language_">window</span>.<span class="hljs-title function_">requestAnimationFrame</span>(run)
}
</code></pre><p>Given the data we have in the <code>gears</code> array, we know the number of <strong>teeth</strong> and in which <strong>direction</strong> each gear rotates. With this we can <strong>calculate the angle of each gear</strong> on each frame. Once we save the new calculated angles, we call the function <code>drawGears</code> to draw each gear with the correct angle. Then we&#39;ll recursively call the <code>run</code> function again (wrapped with <code>window.requestAnimationFrame</code> to make sure that it&#39;s called again only in the next animation cycle).</p>
<p>You will probably be wondering why we don&#39;t implicitly tell to <strong>clean the canvas</strong> before each frame. It&#39;s because WebGL does it automatically when drawing. If it detects that we change the input variables, by default it will clean the previous buffer. If for some reason <em>(<strong>not this case</strong>)</em> we want the canvas not to be cleaned, then we should have obtained the context with an additional parameter <code>const gl = canvas.getContext(&#39;webgl&#39;, { preserveDrawingBuffer: true });</code>.</p>
<h3 id="draw-gears">Draw gears</h3>
<p>For each gear in each frame, we&#39;ll pass to the GPU the necessary matrices for the rotation: <code>u_translation</code>, <code>u_rotation</code> and <code>u_moveOrigin</code>. Then, we&#39;ll start drawing each of the pieces of the gear:</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">function</span> <span class="hljs-title function_">drawGears</span>(<span class="hljs-params"></span>) {
  gears.<span class="hljs-title function_">forEach</span>(<span class="hljs-function">(<span class="hljs-params">gear, index</span>) =&gt;</span> {
    <span class="hljs-keyword">const</span> [centerX, centerY] = gear.<span class="hljs-property">center</span>

    <span class="hljs-comment">// u_translation</span>
    gl.<span class="hljs-title function_">uniformMatrix3fv</span>(
      translationLocation,
      <span class="hljs-literal">false</span>,
      <span class="hljs-title function_">translation</span>(centerX, centerY)
    )

    <span class="hljs-comment">// u_rotation</span>
    gl.<span class="hljs-title function_">uniformMatrix3fv</span>(rotationLocation, <span class="hljs-literal">false</span>, <span class="hljs-title function_">rotation</span>(angles[index]))

    <span class="hljs-comment">// u_moveOrigin</span>
    gl.<span class="hljs-title function_">uniformMatrix3fv</span>(
      moveOriginLocation,
      <span class="hljs-literal">false</span>,
      <span class="hljs-title function_">translation</span>(-centerX, -centerY)
    )

    <span class="hljs-comment">// Render the gear + each gear piece</span>
    <span class="hljs-title function_">renderGearPiece</span>(gear)
    <span class="hljs-keyword">if</span> (gear.<span class="hljs-property">children</span>) gear.<span class="hljs-property">children</span>.<span class="hljs-title function_">forEach</span>(renderGearPiece)
  })
}
</code></pre><p>We will draw each piece of the gear with the same function:</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">function</span> <span class="hljs-title function_">renderGearPiece</span>(<span class="hljs-params">{
  center,
  radius,
  fillColor,
  strokeColor,
  numberOfTeeth,
}</span>) {
  <span class="hljs-keyword">const</span> { <span class="hljs-variable constant_">TRIANGLE_STRIP</span>, <span class="hljs-variable constant_">POINTS</span>, <span class="hljs-variable constant_">TRIANGLES</span> } = gl
  <span class="hljs-keyword">const</span> coords = <span class="hljs-title function_">getCoords</span>(gl, center, radius)

  <span class="hljs-keyword">if</span> (fillColor) <span class="hljs-title function_">drawShape</span>(coords, fillColor, <span class="hljs-variable constant_">TRIANGLE_STRIP</span>)
  <span class="hljs-keyword">if</span> (strokeColor) <span class="hljs-title function_">drawShape</span>(coords, strokeColor, <span class="hljs-variable constant_">POINTS</span>)
  <span class="hljs-keyword">if</span> (numberOfTeeth) {
    <span class="hljs-title function_">drawShape</span>(
      <span class="hljs-title function_">getCoords</span>(gl, center, radius, numberOfTeeth),
      fillColor,
      <span class="hljs-variable constant_">TRIANGLES</span>
    )
  }
}
</code></pre><ul>
<li>If it&#39;s a circle with a border (<a href="#circle-with-border">Fig 3.</a>) --&gt; we&#39;ll use <code>POINTS</code>.</li>
<li>If it&#39;s a color-filled circle (<a href="#circle-with-filled-color">Fig 4.</a>) --&gt; we&#39;ll use <code>TRIANGLE_STRIP</code>.</li>
<li>If it&#39;s a circle with teeth (<a href="#circle-with-teeth">Fig 5.</a>) --&gt; we&#39;ll use <code>TRIANGLES</code>.</li>
</ul>
<p>Implemented with various &quot;ifs&quot;, it allows us to create a circle filled with one color but with the border in another color, or a circle filled with color and with teeth. That means more flexibility.</p>
<p>The coordinates of the filled circle and the circle with border, even if one is made with triangles and the other with points, are exactly the same. The one that does have different coordinates is the circle with teeth, but we&#39;ll use the same helper to get the coordinates:</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">getCoords</span>(<span class="hljs-params">gl, center, radiusX, teeth = <span class="hljs-number">0</span></span>) {
  <span class="hljs-keyword">const</span> toothSize = teeth ? <span class="hljs-number">0.05</span> : <span class="hljs-number">0</span>
  <span class="hljs-keyword">const</span> step = teeth ? <span class="hljs-number">360</span> / (teeth * <span class="hljs-number">3</span>) : <span class="hljs-number">1</span>
  <span class="hljs-keyword">const</span> [centerX, centerY] = center
  <span class="hljs-keyword">const</span> positions = []
  <span class="hljs-keyword">const</span> radiusY = (radiusX / gl.<span class="hljs-property">canvas</span>.<span class="hljs-property">height</span>) * gl.<span class="hljs-property">canvas</span>.<span class="hljs-property">width</span>

  <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i &lt;= <span class="hljs-number">360</span>; i += step) {
    positions.<span class="hljs-title function_">push</span>(
      centerX,
      centerY,
      centerX + (radiusX + toothSize) * <span class="hljs-title class_">Math</span>.<span class="hljs-title function_">cos</span>(<span class="hljs-number">2</span> * <span class="hljs-title class_">Math</span>.<span class="hljs-property">PI</span> * (i / <span class="hljs-number">360</span>)),
      centerY + (radiusY + toothSize) * <span class="hljs-title class_">Math</span>.<span class="hljs-title function_">sin</span>(<span class="hljs-number">2</span> * <span class="hljs-title class_">Math</span>.<span class="hljs-property">PI</span> * (i / <span class="hljs-number">360</span>))
    )
  }

  <span class="hljs-keyword">return</span> positions
}
</code></pre><p>What we still need to know would be the helper <code>drawShape</code>, although it&#39;s the same code we saw in the previous article: It passes the coordinates and color to paint to the GPU, and calls the function <code>drawArrays</code> indicating the mode (if triangles, points...).</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">function</span> <span class="hljs-title function_">drawShape</span>(<span class="hljs-params">coords, color, drawingMode</span>) {
  <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Float32Array</span>(coords)
  <span class="hljs-keyword">const</span> buffer = <span class="hljs-title function_">createAndBindBuffer</span>(gl, gl.<span class="hljs-property">ARRAY_BUFFER</span>, gl.<span class="hljs-property">STATIC_DRAW</span>, data)

  gl.<span class="hljs-title function_">useProgram</span>(program)
  <span class="hljs-title function_">linkGPUAndCPU</span>(gl, { program, buffer, <span class="hljs-attr">gpuVariable</span>: <span class="hljs-string">&#x27;position&#x27;</span> })

  <span class="hljs-keyword">const</span> inputColor = gl.<span class="hljs-title function_">getUniformLocation</span>(program, <span class="hljs-string">&#x27;inputColor&#x27;</span>)
  gl.<span class="hljs-title function_">uniform3fv</span>(inputColor, color)
  gl.<span class="hljs-title function_">drawArrays</span>(drawingMode, <span class="hljs-number">0</span>, coords.<span class="hljs-property">length</span> / <span class="hljs-number">2</span>)
}
</code></pre><p>And voila! We got it.</p>
<figure align="center">
  <img src="https://aralroca.com/images/blog-images/we-got-it.jpg" alt="We got it!" class="center" />
  <figcaption><small>Photo by Clay Banks on Unsplash</small></figcaption>
</figure>

<h2 id="show-me-all-the-code">Show me all the code</h2>
<p>I&#39;ve uploaded all the code for this article to my GitHub. I have implemented it with <a href="https://preactjs.com/">Preact</a>. All the code can be found inside the hook <code>useGears</code>:</p>
<ul>
<li><a href="https://github.com/aralroca/webgl-gears">https://github.com/aralroca/webgl-gears</a></li>
</ul>
<p>You can also see the demo here:</p>
<ul>
<li><a href="https://webgl-gears.vercel.app/">https://webgl-gears.vercel.app/</a></li>
</ul>
<h2 id="conclusion">Conclusion</h2>
<p>We have seen how to generate more complex figures using triangles and points. We have even given them movement with matrix multiplications.</p>
<p>There is a drawing mode we haven&#39;t seen yet, <strong>lines</strong>. That&#39;s because the lines that can be made with it are very thin, and they wouldn&#39;t fit the teeth of the gear. You can&#39;t change the thickness of the line easily, to do it you have to make a rectangle (2 triangles). These lines have very little flexibility and most figures are drawn with triangles. Anyway, at this point, you should be able to use the <code>gl.LINES</code> given 2 coordinates.</p>
<p>This article was the second part of &quot;First steps with WebGL&quot;. <a href="https://aralroca.us8.list-manage.com/subscribe/post?u=29d99171aa3f671bde658475a&id=9f1a0b31e3">Stay tuned</a> because in next articles of this series we&#39;ll see: textures, image processing, framebuffers, 3d objects, and more.</p>
<h2 id="references">References</h2>
<ul>
<li><a href="http://www.corehtml5.com/trianglestripfundamentals.php">http://www.corehtml5.com/trianglestripfundamentals.php</a></li>
<li><a href="https://mattdesl.svbtle.com/drawing-lines-is-hard">https://mattdesl.svbtle.com/drawing-lines-is-hard</a></li>
<li><a href="https://stackoverflow.com/a/54585370/4467741">https://stackoverflow.com/a/54585370/4467741</a></li>
<li><a href="https://webgl2fundamentals.org/webgl/lessons/webgl-2d-matrices.html">https://webgl2fundamentals.org/webgl/lessons/webgl-2d-matrices.html</a></li>
<li><a href="https://webgl2fundamentals.org/webgl/lessons/webgl-2d-rotation.html">https://webgl2fundamentals.org/webgl/lessons/webgl-2d-rotation.html</a></li>
<li><a href="https://www.youtube.com/watch?v=nlNOSNlTXEQ">https://www.youtube.com/watch?v=nlNOSNlTXEQ</a></li>
</ul>
]]></content:encoded>
            </item>
            <item>
              <title>HTML Streaming and DOM Diffing Algorithm</title>
              <description>Explore HTML Streaming and its benefits covering topics from initial loading to advanced cases like navigation, RPCs, and DOM diffing algorithms during streaming</description>
              <link>https://aralroca.com/blog/html-node-streaming</link>
              <guid isPermaLink="false">https://aralroca.com/blog/html-node-streaming/</guid>
              <pubDate>Wed Feb 21 2024 00:00:00 GMT+0000 (Coordinated Universal Time)</pubDate>
              <content:encoded><![CDATA[<p>In recent years browsers have supported streaming HTML and JavaScript. In this article we will talk about the benefits of this, and what else we can do that browsers don&#39;t do automatically to take full advantage of streaming.</p>
<h2 id="streaming-html">Streaming HTML</h2>
<p>During the initial load, we don&#39;t have to worry much because the browsers do it automatically. When they receive the HTML chunks during the streaming they print the content.</p>
<p>To activate the streaming from the server you have to adapt the headers, for example:</p>
<pre><code class="hljs language-json"><span class="hljs-punctuation">{</span>
  <span class="hljs-attr">&quot;transfer-encoding&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;chunked&quot;</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">&quot;vary&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;Accept-Encoding&quot;</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">&quot;content-type&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;text/html; charset=utf-8&quot;</span>
<span class="hljs-punctuation">}</span>
</code></pre><p>And in the response use a <a href="https://bun.sh/docs/api/streams"><code>ReadableStream</code></a>. This would be the example with <a href="https://bun.sh/">Bun</a>:</p>
<pre><code class="hljs language-ts"><span class="hljs-keyword">const</span> encoder = <span class="hljs-keyword">new</span> <span class="hljs-title class_">TextEncoder</span>()

<span class="hljs-comment">// ...</span>

<span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Response</span>(
  <span class="hljs-keyword">new</span> <span class="hljs-title class_">ReadableStream</span>({
    <span class="hljs-title function_">start</span>(<span class="hljs-params">controller</span>) {
      controller.<span class="hljs-title function_">enqueue</span>(encoder.<span class="hljs-title function_">encode</span>(<span class="hljs-string">&#x27;&lt;html lang=&quot;en&quot;&gt;&#x27;</span>))
      controller.<span class="hljs-title function_">enqueue</span>(encoder.<span class="hljs-title function_">encode</span>(<span class="hljs-string">&#x27;&lt;head /&gt;&#x27;</span>))
      controller.<span class="hljs-title function_">enqueue</span>(encoder.<span class="hljs-title function_">encode</span>(<span class="hljs-string">&#x27;&lt;body&gt;&#x27;</span>))
      controller.<span class="hljs-title function_">enqueue</span>(encoder.<span class="hljs-title function_">encode</span>(<span class="hljs-string">&#x27;&lt;div class=&quot;foo&quot;&gt;Bar&lt;/div&gt;&#x27;</span>))
      controller.<span class="hljs-title function_">enqueue</span>(encoder.<span class="hljs-title function_">encode</span>(<span class="hljs-string">&#x27;&lt;/body&gt;&#x27;</span>))
      controller.<span class="hljs-title function_">enqueue</span>(encoder.<span class="hljs-title function_">encode</span>(<span class="hljs-string">&#x27;&lt;/html&gt;&#x27;</span>))
      controller.<span class="hljs-title function_">close</span>()
    },
  })
)
</code></pre><p>Each string inside <code>enqueue</code> is a chunk that the browser will receive.</p>
<h2 id="changing-the-html-content-during-streaming">Changing the HTML content during streaming</h2>
<p>One of the practices that is being done a lot because it has many performance benefits is to change the HTML content during streaming. A clear example is <a href="https://react.dev/reference/react/Suspense">React Suspense</a>. The idea is to show empty content (placeholder, skeleton, or spinner) while loading the rest of the HTML and in the meantime, it is loading the missing content. Once the server has the missing content then in streaming-time it changes it!</p>
<p>Perhaps the first question that comes to your mind is how can you modify a part of the HTML that has already been sent and processed by the browser 🤔?</p>
<p>Well, browsers are smart enough to execute small JS scripts during streaming. However, they have to be scripts without being <code>module</code> type, because they always wait for all the HTML to be loaded before executing. In this case, we are not interested.</p>
<p>This would be an example to make it visually understandable <em>(it&#39;s normally more complex than this)</em>:</p>
<pre><code class="hljs language-ts"><span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Response</span>(
  <span class="hljs-keyword">new</span> <span class="hljs-title class_">ReadableStream</span>({
    <span class="hljs-keyword">async</span> <span class="hljs-title function_">start</span>(<span class="hljs-params">controller</span>) {
      <span class="hljs-keyword">const</span> suspensePromises = []

      controller.<span class="hljs-title function_">enqueue</span>(encoder.<span class="hljs-title function_">encode</span>(<span class="hljs-string">&#x27;&lt;html lang=&quot;en&quot;&gt;&#x27;</span>))
      controller.<span class="hljs-title function_">enqueue</span>(encoder.<span class="hljs-title function_">encode</span>(<span class="hljs-string">&#x27;&lt;head&gt;&#x27;</span>))
      <span class="hljs-comment">// Load the code to allow &quot;unsuspense&quot;</span>
      controller.<span class="hljs-title function_">enqueue</span>(
        enconder.<span class="hljs-title function_">encode</span>(<span class="hljs-string">&#x27;&lt;script src=&quot;unsuspense.js&quot;&gt;&lt;/script&gt;&#x27;</span>)
      )
      controller.<span class="hljs-title function_">enqueue</span>(encoder.<span class="hljs-title function_">encode</span>(<span class="hljs-string">&#x27;&lt;/head&gt;&#x27;</span>))
      controller.<span class="hljs-title function_">enqueue</span>(encoder.<span class="hljs-title function_">encode</span>(<span class="hljs-string">&#x27;&lt;body&gt;&#x27;</span>))

      <span class="hljs-comment">// Add a placeholder (suspense)</span>
      controller.<span class="hljs-title function_">enqueue</span>(
        encoder.<span class="hljs-title function_">encode</span>(<span class="hljs-string">&#x27;&lt;div id=&quot;suspensed:1&quot;&gt;Loading...&lt;/div&gt;&#x27;</span>)
      )

      <span class="hljs-comment">// Load the content - without &quot;await&quot; (IMPORTANT)</span>
      suspensePromises.<span class="hljs-title function_">push</span>(
        <span class="hljs-title function_">computeExpensiveChunk</span>().<span class="hljs-title function_">then</span>(<span class="hljs-function">(<span class="hljs-params">content</span>) =&gt;</span> {
          <span class="hljs-comment">// enqueue the real content</span>
          controller.<span class="hljs-title function_">enqueue</span>(
            encoder.<span class="hljs-title function_">encode</span>(
              <span class="hljs-string">`&lt;template id=&quot;suspensed-content:1&quot;&gt;<span class="hljs-subst">${content}</span>&lt;/template&gt;`</span>
            )
          )
          <span class="hljs-comment">// enqueue the script to replace the suspensed content to the real one</span>
          controller.<span class="hljs-title function_">enqueue</span>(encoder.<span class="hljs-title function_">encode</span>(<span class="hljs-string">`&lt;script&gt;unsuspense(&#x27;1&#x27;)&lt;/script&gt;`</span>))
        })
      )

      controller.<span class="hljs-title function_">enqueue</span>(encoder.<span class="hljs-title function_">encode</span>(<span class="hljs-string">&#x27;&lt;div class=&quot;foo&quot;&gt;Bar&lt;/div&gt;&#x27;</span>))
      controller.<span class="hljs-title function_">enqueue</span>(encoder.<span class="hljs-title function_">encode</span>(<span class="hljs-string">&#x27;&lt;/body&gt;&#x27;</span>))
      controller.<span class="hljs-title function_">enqueue</span>(encoder.<span class="hljs-title function_">encode</span>(<span class="hljs-string">&#x27;&lt;/html&gt;&#x27;</span>))

      <span class="hljs-comment">// Wait for all suspended content before closing the stream</span>
      <span class="hljs-keyword">await</span> <span class="hljs-title class_">Promise</span>.<span class="hljs-title function_">all</span>(suspensePromises)

      controller.<span class="hljs-title function_">close</span>()
    },
  })
)
</code></pre><p>Where the <code>unsuspense.js</code> file is the one that exposes the <code>window.unsuspense</code> in this case so that it can be executed during the streaming and it replaces the content of the <code>suspensed:1</code> by the content of the <code>suspensed-content:1</code> template. In this case, the user will see the <code>Loading...</code> text and also the <code>div</code> with the <code>Bar</code> text. Once the content has been processed, the <code>Loading...</code> content will be changed to the real content.</p>
<p>Thinking of benefits, everything is done with a <strong>single request</strong> and the user instantly sees the HTML and the changes to it without having to make extra requests. In the past years, these requests were made from the client, for example in React using a <code>useEffect</code>, making this not executed until all the HTML was loaded, making another extra request to the server and complicating the life of the developers.</p>
<p>By being able to modify the HTML content during streaming, it now allows you to use <code>async</code> components and you can directly use <code>fetch</code> or make database requests in conjunction with <code>Suspense</code>.</p>
<h2 id="html-streaming-in-runtime">HTML Streaming in runtime</h2>
<p>We have talked about the practicality of HTML streaming for the initial loading of HTML, but apart from the initial loading, are there other scenarios where we want to stream HTML?</p>
<p>Yes, there are two cases:</p>
<ul>
<li><a href="#navigation-view-transitions-api">Navigation (View Transitions API)</a></li>
<li><a href="#rpcs-ex-server-actions">RPCs (ex: server actions)</a></li>
</ul>
<h3 id="navigation-view-transitions-api">Navigation (View Transitions API)</h3>
<p>Since 2023 Chrome announced the <a href="https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API">View Transition API</a>, and it looks like Safari is also going to support it <a href="https://github.com/WebKit/WebKit/commit/7d082967d8f5667853d07abe11396426417c5e68">soon</a>.</p>
<p>The View Transitions API provides a mechanism for easily creating animated transitions between different DOM states simulating single-page apps (SPAs) while also updating the DOM contents in a single step.</p>
<p>During navigation, we usually want to replace all HTML content with other content. However, we must take advantage of streaming so that we don&#39;t have to wait.</p>
<pre><code class="hljs language-js"><span class="hljs-variable language_">window</span>.<span class="hljs-property">navigation</span>.<span class="hljs-title function_">addEventListener</span>(<span class="hljs-string">&#x27;navigate&#x27;</span>, navigate)

<span class="hljs-keyword">function</span> <span class="hljs-title function_">navigate</span>(<span class="hljs-params">event</span>) {
  <span class="hljs-keyword">const</span> url = <span class="hljs-keyword">new</span> <span class="hljs-title function_">URL</span>(event.<span class="hljs-property">destination</span>.<span class="hljs-property">url</span>)
  <span class="hljs-keyword">const</span> decoder = <span class="hljs-keyword">new</span> <span class="hljs-title class_">TextDecoder</span>()

  <span class="hljs-comment">// Only intercept navigations within the same origin for security</span>
  <span class="hljs-keyword">if</span> (location.<span class="hljs-property">origin</span> !== url.<span class="hljs-property">origin</span>) <span class="hljs-keyword">return</span>

  event.<span class="hljs-title function_">intercept</span>({
    <span class="hljs-keyword">async</span> <span class="hljs-title function_">handler</span>(<span class="hljs-params"></span>) {
      <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> <span class="hljs-title function_">fetch</span>(url.<span class="hljs-property">pathname</span>)
      <span class="hljs-comment">// Creates a new &quot;sandbox&quot; HTML Document</span>
      <span class="hljs-keyword">const</span> doc = <span class="hljs-variable language_">document</span>.<span class="hljs-property">implementation</span>.<span class="hljs-title function_">createHTMLDocument</span>()
      <span class="hljs-keyword">const</span> stream = res.<span class="hljs-property">body</span>.<span class="hljs-title function_">getReader</span>()

      <span class="hljs-comment">// Transition to the new document with smooth scrolling</span>
      <span class="hljs-keyword">await</span> <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">startViewTransition</span>(<span class="hljs-function">() =&gt;</span> {
        <span class="hljs-comment">// Connect the current document&#x27;s DOM with the sandbox&#x27;s DOM</span>
        <span class="hljs-variable language_">document</span>.<span class="hljs-property">documentElement</span>.<span class="hljs-title function_">replaceWith</span>(doc.<span class="hljs-property">documentElement</span>)
        <span class="hljs-variable language_">document</span>.<span class="hljs-property">documentElement</span>.<span class="hljs-property">scrollTop</span> = <span class="hljs-number">0</span>
      }).<span class="hljs-property">ready</span>

      <span class="hljs-comment">// Process the response body in chunks</span>
      <span class="hljs-keyword">while</span> (<span class="hljs-literal">true</span>) {
        <span class="hljs-keyword">const</span> { done, value } = <span class="hljs-keyword">await</span> stream.<span class="hljs-title function_">read</span>()

        <span class="hljs-keyword">if</span> (done) <span class="hljs-keyword">break</span> <span class="hljs-comment">// Exit the loop when the stream is finished</span>

        <span class="hljs-comment">// Inject decoded content into the sandbox document</span>
        <span class="hljs-comment">// within a transition. The sandbox treats the string and</span>
        <span class="hljs-comment">// parses it, then during the transition it is put into the</span>
        <span class="hljs-comment">// real DOM.</span>
        <span class="hljs-keyword">await</span> <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">startViewTransition</span>(<span class="hljs-function">() =&gt;</span>
          doc.<span class="hljs-title function_">write</span>(decoder.<span class="hljs-title function_">decode</span>(value))
        ).<span class="hljs-property">ready</span>
      }
    },
  })
}
</code></pre><p>In this example, <a href="https://developer.mozilla.org/en-US/docs/Web/API/DOMImplementation/createHTMLDocument"><code>document.implementation.createHTMLDocument</code></a> is used in conjunction with <code>doc.write</code> to process the stream, making each chunk a different transition. The use of <code>doc.write</code> should not raise an alert, in this context, it is well used. For more details of this trick <a href="https://www.youtube.com/watch?v=LLRig4s1_yA&t=1286s">this Chrome&#39;s video</a> explains it.</p>
<p>The above example serves only to replace all HTML content with other content, but what if we wanted to have more control over the HTML nodes during streaming? Perhaps to filter out specific nodes during this streaming.</p>
<p>To make this possible, we can use the <a href="https://github.com/aralroca/parse-html-stream"><strong><code>parse-html-stream</code></strong></a> library to make it easy to transform a stream reader to an HTML node generator. In this case, we could use it like this:</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">import</span> parseHTMLStream <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;parse-html-stream&#x27;</span> <span class="hljs-comment">// Import it</span>

<span class="hljs-variable language_">window</span>.<span class="hljs-property">navigation</span>.<span class="hljs-title function_">addEventListener</span>(<span class="hljs-string">&#x27;navigate&#x27;</span>, navigate)

<span class="hljs-keyword">function</span> <span class="hljs-title function_">navigate</span>(<span class="hljs-params">event</span>) {
  <span class="hljs-keyword">const</span> url = <span class="hljs-keyword">new</span> <span class="hljs-title function_">URL</span>(event.<span class="hljs-property">destination</span>.<span class="hljs-property">url</span>)
  <span class="hljs-keyword">const</span> decoder = <span class="hljs-keyword">new</span> <span class="hljs-title class_">TextDecoder</span>()

  <span class="hljs-keyword">if</span> (location.<span class="hljs-property">origin</span> !== url.<span class="hljs-property">origin</span>) <span class="hljs-keyword">return</span>

  event.<span class="hljs-title function_">intercept</span>({
    <span class="hljs-keyword">async</span> <span class="hljs-title function_">handler</span>(<span class="hljs-params"></span>) {
      <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> <span class="hljs-title function_">fetch</span>(url.<span class="hljs-property">pathname</span>)
      <span class="hljs-keyword">const</span> doc = <span class="hljs-variable language_">document</span>.<span class="hljs-property">implementation</span>.<span class="hljs-title function_">createHTMLDocument</span>()
      <span class="hljs-keyword">const</span> stream = res.<span class="hljs-property">body</span>.<span class="hljs-title function_">getReader</span>()

      <span class="hljs-keyword">await</span> <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">startViewTransition</span>(<span class="hljs-function">() =&gt;</span> {
        <span class="hljs-variable language_">document</span>.<span class="hljs-property">documentElement</span>.<span class="hljs-title function_">replaceWith</span>(doc.<span class="hljs-property">documentElement</span>)
        <span class="hljs-variable language_">document</span>.<span class="hljs-property">documentElement</span>.<span class="hljs-property">scrollTop</span> = <span class="hljs-number">0</span>
      }).<span class="hljs-property">ready</span>

      <span class="hljs-comment">// Use it:</span>
      <span class="hljs-keyword">for</span> <span class="hljs-title function_">await</span> (<span class="hljs-keyword">const</span> node <span class="hljs-keyword">of</span> <span class="hljs-title function_">parseHTMLStream</span>(reader)) {
        <span class="hljs-comment">// You have full control of each HTML Node during the streaming</span>
        <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(node.<span class="hljs-property">nodeName</span>)
      }
    },
  })
}
</code></pre><p>However, it is much more complex now to implement adding the node in the right place in the main document, isn&#39;t it? So does all this make sense?</p>
<p>To understand the benefit of this, read on.</p>
<h3 id="rpcs-ex-server-actions">RPCs (ex: server actions)</h3>
<p>A remote procedure call (<a href="https://en.wikipedia.org/wiki/Remote_procedure_call">RPC</a>) is used so that developers do not have to implement an endpoint and all the communication logic between browser-sever and server-browser is handled by the RPC. An example of RPC would be <a href="https://react.dev/reference/react/use-server#server-actions-in-forms">React server actions</a>, which can trigger updates to the DOM during streaming. However, React here is using a <a href="https://demystifying-rsc.vercel.app/static-content/2/">virtual DOM</a> instead of transferring hypermedia (HTML) and using the diff algorithm with the real DOM.</p>
<p>In this article, I will not explain the technical details of how to implement an RPC, but to understand how server actions would work in a different context than React, I invite you to watch this video with an example of making events like <code>onClick</code> work on server components, i.e. when the user clicks a button in the browser, then the <code>onClick</code> function is executed on the server.</p>
<p>{% twitter 1753061161866572213 %}</p>
<p>The video of this tweet is about an experimental framework I&#39;m doing, I hope in a few months to make it public and give more details.</p>
<p>The grace is that from the server you can modify the DB or whatever you want and see the HTML changes reflected in the browser.</p>
<p>{% twitter 1760028908647559375 %}</p>
<p>To make this possible, from the RPC client code (200B) a request is made to the action and if it is the first time, another request is made to download the lazy client code (1 kb) to process the response. This last code will be responsible for processing the HTML and updating the DOM only for the parts that have changed using the <a href="#dom-diffing-algorithm">DOM Diffing algorithm</a>.</p>
<p>To show you the benefit of this, think that you could build a SPA with almost no JS on the client, just the RPC. However, server actions make sense for interactions that involve server. For purely client interactions or that you need the Web API then you can use web components.</p>
<p>Another benefit of updating only the modified parts of the DOM, instead of the entire DOM, is its excellent integration with Web components. Web components, particularly those utilizing signals to react to changes, can maintain their internal state without losing synchronization.</p>
<h3 id="dom-diffing-algorithm">DOM Diffing algorithm</h3>
<p>React to update the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model">Document Object Model</a> (DOM) uses the diffing algorithm but with a <a href="https://demystifying-rsc.vercel.app/static-content/2/">virtual DOM</a>, so it transfers the virtual DOM instead of hypermedia (HTML) directly. It does this to have more control over the client components since they are not reactive to the DOM. On the other hand, if our client components are web components with signals their properties are reactive and if the element is updated by adding a new attribute, then the web component without losing its internal state will react to the changes. This allows us to transfer directly HTML and work directly with the DOM.</p>
<p>Currently there are several open-source DOM diff algorithms, some examples:</p>
<ul>
<li><a href="https://github.com/patrick-steele-idem/morphdom">morphdom</a></li>
<li><a href="https://github.com/DylanPiercey/set-dom">set-dom</a></li>
<li><a href="https://diffhtml.org/">diffhtml</a></li>
<li><a href="https://github.com/fiduswriter/diffDOM">diffDOM</a></li>
<li><a href="https://github.com/choojs/nanomorph">nanomorph</a></li>
<li><a href="https://google.github.io/incremental-dom/">incremental-dom</a></li>
</ul>
<p>Most implementations use <a href="https://en.wikipedia.org/wiki/Breadth-first_search">breadth-first search</a> to traverse the DOM tree and update it.</p>
<figure align="center">
  <img src="https://aralroca.com/images/blog-images/BFS.gif" alt="Animated example of a breadth-first search" class="center" />
  <figcaption><small>Animated example of a breadth-first search</small></figcaption>
</figure>

<p>However during HTML Streaming the nodes arrives as <a href="https://en.wikipedia.org/wiki/Depth-first_search">depth-first search</a> (DFS):</p>
<figure align="center">
  <img src="https://aralroca.com/images/blog-images/DFS.gif" alt="Animated example of a depth-first search" class="center" />
  <figcaption><small>Animated example of a depth-first search</small></figcaption>
</figure>

<p>If the server can start sending HTML as soon as possible and the browser does not wait for the entire response to arrive, it can process the HTML code in fragments as it arrives.</p>
<figure align="center">
  <img src="https://aralroca.com/images/blog-images/html-parsing-tasks.png" alt="Parsing and rendering of the HTML code provided by the server" class="center" />
  <figcaption><small>Parsing and rendering of the HTML code provided by the server</small></figcaption>
</figure>

<p>The last image is how browsers <a href="https://web.dev/articles/client-side-rendering-of-html-and-interactivity">process streaming</a> HTML, but the same must be done to support the DOM Diffing algorithm for streaming.</p>
<p>The rendering work is incremental to present the page changes to the user as quickly as possible. This approach generates a better <a href="https://web.dev/articles/inp">Interaction to Next Paint (INP)</a> score for the page.</p>
<p>Even the chunks arrive in DFS order, the browser cannot render an element until it has received all the information that defines it.</p>
<p>If you remember, when we talked about <a href="#navigation-view-transitions-api">navigation</a>, we gave an example that did the following:</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">for</span> <span class="hljs-title function_">await</span> (<span class="hljs-keyword">const</span> node <span class="hljs-keyword">of</span> <span class="hljs-title function_">parseHTMLStream</span>(reader)) {
  <span class="hljs-comment">// You have full control of each HTML Node during the streaming</span>
  <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(node.<span class="hljs-property">nodeName</span>)
}
</code></pre><p>At this point, the nodes always arrive in DFS order. But it is not enough, because we need to walk through the tree inside the DOM diffing algorithm. That is, visit the <code>firstChild</code>, <code>nextSibling</code>, the <code>parentNode</code>, etc, and compare it with the real DOM nodes.</p>
<p>To do this, the <a href="https://github.com/aralroca/parse-html-stream"><code>parse-html-stream</code></a> library that I have recently put in open-source, supports also walking through the tree of nodes during streaming, allowing you to do incremental rendering, while you walk through the tree the library is parsing the HTML chunks in the order they arrive, and while you have the parsed nodes you are making the rendering changes.</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">import</span> htmlStreamWalker <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;parse-html-stream/walker&#x27;</span>

<span class="hljs-comment">// ...</span>

<span class="hljs-keyword">const</span> reader = res.<span class="hljs-property">body</span>.<span class="hljs-title function_">getReader</span>()
<span class="hljs-keyword">const</span> walker = <span class="hljs-keyword">await</span> <span class="hljs-title function_">htmlStreamWalker</span>(reader)

<span class="hljs-comment">// Root node</span>
<span class="hljs-keyword">const</span> rootNode = walker.<span class="hljs-property">rootNode</span>

<span class="hljs-comment">// Gives the firstChild taking into account the stream chunks</span>
<span class="hljs-keyword">const</span> child = <span class="hljs-keyword">await</span> walker.<span class="hljs-title function_">firstChild</span>(rootNode)

<span class="hljs-comment">// Gives the nextSibling taking into account the stream chunks</span>
<span class="hljs-keyword">const</span> brother = <span class="hljs-keyword">await</span> walker.<span class="hljs-title function_">nextSibling</span>(rootNode)

<span class="hljs-comment">// You can do it with every HTML node:</span>
<span class="hljs-keyword">const</span> childOfBrother = <span class="hljs-keyword">await</span> walker.<span class="hljs-title function_">firstChild</span>(brother)
</code></pre><p>In this case <code>rootNode</code>, <code>child</code>, <code>brother</code>, and <code>childOfBrother</code> are Nodes and you have access to all Node properties. Note, however, that there are two properties that may not be true because streaming chunks have yet to be received:</p>
<ul>
<li><code>node.firstChild</code> ❌ - Stop being correct, in some cases, it would work, in others it would not because maybe the next chunk is the child of the Node.</li>
<li><code>node.nextSibling</code> ❌ - Stop being correct, in some cases, it would work, in others it would not because maybe the next chunk is the sibling of the Node.</li>
</ul>
<p>For these cases, the <code>parse-html-stream</code> <strong>walker</strong> offers two methods to replace them so that they always work:</p>
<ul>
<li><code>walker.firstChild(node)</code> ✅ - Try first to get the <code>firstChild</code>, if not yet wait for the next chunk to verify if it has it or not. When the next chunk arrives it processes the next nodes.</li>
<li><code>walker.nextSibling(node)</code> ✅ - Try first to get the <code>nextSibling</code>, if not yet wait for the next chunk to verify if it has it or not. When the next chunk arrives it processes the next nodes.</li>
</ul>
<p>The initial nodes do not lose context and can be used after the execution of these functions.</p>
<h2 id="conclusions">Conclusions</h2>
<p>In the article, we talked about the benefits and practical applications of HTML Streaming beyond just the initial rendering.</p>
<p>We talked about more technical concepts such as <a href="#rpcs-ex-server-actions">RPC</a>, <a href="#navigation-view-transitions-api">View Transitions API</a>, <a href="#dom-diffing-algorithm">DOM Diffing algorithm</a>, but without going into detail on each of these topics, but rather how to use HTML Streaming in each of them. If you are interested in knowing more about topics that I have not gone into depth in this article, comment it in the comments and I will take it into account to make another article focusing on other topics that interest you.</p>
<p>We have also talked about <a href="https://github.com/aralroca/parse-html-stream">parse-html-stream</a>, a small library that I have recently put in open-source so that anyone can use it.</p>
<p>On a final note, the web was invented to transfer hypermedia (HTML) and after using JSON and a lot of client code over the last years, I hope to see more <a href="https://htmx.org/essays/hypermedia-driven-applications/">Hypermedia-Driven applications</a> available to make life easier for developers and make websites lighter with almost no JavaScript code, so I see great promise for the future of HTML streaming beyond the initial load.</p>
<h2 id="references">References</h2>
<ul>
<li><a href="https://bun.sh/docs/api/streams">https://bun.sh/docs/api/streams</a></li>
<li><a href="https://react.dev/reference/react/Suspense">https://react.dev/reference/react/Suspense</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model">https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model</a></li>
<li><a href="https://github.com/aralroca/parse-html-stream">https://github.com/aralroca/parse-html-stream</a></li>
<li><a href="https://web.dev/articles/client-side-rendering-of-html-and-interactivity">https://web.dev/articles/client-side-rendering-of-html-and-interactivity</a></li>
<li><a href="https://htmx.org/essays/hypermedia-driven-applications/">https://htmx.org/essays/hypermedia-driven-applications/</a></li>
<li><a href="https://en.wikipedia.org/wiki/Depth-first_search">https://en.wikipedia.org/wiki/Depth-first_search</a></li>
<li><a href="https://en.wikipedia.org/wiki/Breadth-first_search">https://en.wikipedia.org/wiki/Breadth-first_search</a></li>
<li><a href="https://github.com/patrick-steele-idem/morphdom">https://github.com/patrick-steele-idem/morphdom</a></li>
<li><a href="https://github.com/DylanPiercey/set-dom">https://github.com/DylanPiercey/set-dom</a></li>
<li><a href="https://diffhtml.org/">https://diffhtml.org/</a></li>
<li><a href="https://github.com/fiduswriter/diffDOM">https://github.com/fiduswriter/diffDOM</a></li>
<li><a href="https://github.com/choojs/nanomorph">https://github.com/choojs/nanomorph</a></li>
<li><a href="https://google.github.io/incremental-dom/">https://google.github.io/incremental-dom/</a></li>
<li><a href="https://demystifying-rsc.vercel.app/static-content/2/">https://demystifying-rsc.vercel.app/static-content/2/</a></li>
</ul>
]]></content:encoded>
            </item>
            <item>
              <title>HTML Streaming Over the Wire 🥳: A Deep Dive</title>
              <description>Explore HTML Streaming Over the Wire! We explain the Diff DOM algorithm with streaming to make only the necessary modifications, insertions, and deletions between a DOM node and an HTML stream reader.</description>
              <link>https://aralroca.com/blog/html-streaming-over-the-wire</link>
              <guid isPermaLink="false">https://aralroca.com/blog/html-streaming-over-the-wire/</guid>
              <pubDate>Mon Apr 08 2024 00:00:00 GMT+0000 (Coordinated Universal Time)</pubDate>
              <content:encoded><![CDATA[<p>In our previous article in the series, we introduced the Diff DOM Algorithm briefly without delving into its technical intricacies. In this installment, we present the <a href="https://github.com/aralroca/diff-dom-streaming"><code>diff-dom-streaming</code></a> library, an open-source solution designed to facilitate HTML Streaming Over the Wire using the Diff DOM Algorithm. This library is intended not only for use within other frameworks and libraries but also as a standalone solution.</p>
<ul>
<li><a href="#why-html-streaming-over-the-wire">Why HTML Streaming Over the Wire?</a><ul>
<li><a href="#interaction-with-json">Interaction with JSON</a></li>
<li><a href="#interaction-with-html-streaming">Interaction with HTML Streaming</a></li>
</ul>
</li>
<li><a href="#diff-dom-algorithm-with-streaming">Diff DOM Algorithm with Streaming</a></li>
<li><a href="#show-me-the-code">Show me the code</a><ul>
<li><a href="#community-support-and-adoption">Community Support and Adoption</a></li>
</ul>
</li>
<li><a href="#show-me-some-example">Show me some example</a></li>
<li><a href="#conclusion">Conclusion</a></li>
</ul>
<h2 id="why-html-streaming-over-the-wire">Why HTML Streaming Over the Wire?</h2>
<p>Given that modern browsers have supported HTML streaming for years, why limit ourselves to initial page loads? Why not extend HTML streaming to server interactions as well? Ultimately, reverting to HTML (HyperText Markup Language) restores the web&#39;s fundamental principles (HyperText Transfer Protocol).</p>
<figure align="center">
  <img src="https://aralroca.com/images/blog-images/http.png" width="200px" height="103px" alt="HyperText Transfer Protocol" class="center" />
  <figcaption><small>HyperText Transfer Protocol</small></figcaption>
</figure>

<p>For some time now, I&#39;ve been immersed in developing Brisa, an experimental framework slated for public release this summer <em>(If you are interested in knowing more, <a href="https://aralroca.com/blog">subscribe</a> to my blog newsletter for now)</em>. One of our primary objectives has been to minimize client-side JavaScript code for server interactions. Drawing inspiration from server actions and HTMX concepts, we&#39;ve achieved the capability to build single-page applications (SPAs) with just 800 bytes—equivalent to the RPC (<a href="https://en.wikipedia.org/wiki/Remote_procedure_call">Remote Procedure Call</a>) for server communication. In cases requiring client components, they seamlessly transform into web components with signals, expanding the code to a mere 3KB.</p>
<p>Additionally, we have aimed to leverage the web platform to its fullest extent. For server actions, we opted to transmit Hypertext. As for client components, they transform into web components and, coupled with signals, respond dynamically to document changes without data transmission.</p>
<p>To understand the difference between the server interactions that we are familiar with in recent years, let&#39;s review how we are doing these server interactions through JSON:</p>
<h3 id="interaction-with-json">Interaction with JSON</h3>
<p>Traditionally, server interactions entail:</p>
<ul>
<li>Capturing a client event and writing code to serialize data, sending JSON to an endpoint (expanding client-side code).</li>
<li>Writing an endpoint, deserializing/serializing both input and output data to return JSON.</li>
<li>Processing the response on the client side, deserializing JSON, and maintaining it in memory for UI library rerendering, thereby further increasing client-side bundle size.</li>
</ul>
<figure align="center">
  <img src="https://aralroca.com/images/blog-images/traditional.png" width="820px" height="495px" class="transparent" alt="Client code for a server interaction" class="center" />
  <figcaption><small>Client code for a server interaction</small></figcaption>
</figure>

<p>This is a very silly example about debouncing an input text and validating a code, but to show you that you can be familiar with doing all this logic on the client to do a server interaction:</p>
<p>Client code:</p>
<pre><code class="hljs language-tsx"><span class="hljs-keyword">function</span> <span class="hljs-title function_">debounce</span>(<span class="hljs-params">func, delay</span>) {
  <span class="hljs-keyword">let</span> timeoutId;
  <span class="hljs-keyword">return</span> <span class="hljs-function">(<span class="hljs-params">...args</span>) =&gt;</span> {
    <span class="hljs-keyword">const</span> fn = func.<span class="hljs-title function_">bind</span>(<span class="hljs-variable language_">this</span>, ...args)
    <span class="hljs-built_in">clearTimeout</span>(timeoutId);
    timeoutId = <span class="hljs-built_in">setTimeout</span>(fn, delay);
  };
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">ClientComponent</span>(<span class="hljs-params"></span>) {
  <span class="hljs-keyword">const</span> [showContent, setShowContent] = <span class="hljs-title function_">useState</span>(<span class="hljs-literal">false</span>)

  <span class="hljs-keyword">async</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">handleInputOnClient</span>(<span class="hljs-params">e</span>) {
    <span class="hljs-keyword">const</span> code = e.<span class="hljs-property">target</span>.<span class="hljs-property">value</span>;
    <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> <span class="hljs-title function_">fetch</span>(<span class="hljs-comment">/* some endpoint */</span>, {
      <span class="hljs-attr">method</span>: <span class="hljs-string">&#x27;POST&#x27;</span>,
      <span class="hljs-attr">body</span>: <span class="hljs-title class_">JSON</span>.<span class="hljs-title function_">stringify</span>({ code })
    })
    <span class="hljs-keyword">if</span>(res.<span class="hljs-property">ok</span>) <span class="hljs-title function_">setShowContent</span>(<span class="hljs-literal">true</span>)
  }

  <span class="hljs-keyword">if</span> (showContent) <span class="hljs-keyword">return</span> <span class="hljs-string">&#x27;some content&#x27;</span>

  <span class="hljs-keyword">return</span> <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">&quot;text&quot;</span>  <span class="hljs-attr">onChange</span>=<span class="hljs-string">{debounce(handleInputOnClient,</span> <span class="hljs-attr">300</span>)} /&gt;</span></span>
}
</code></pre><p>Server code:</p>
<pre><code class="hljs language-ts"><span class="hljs-keyword">export</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">POST</span>(<span class="hljs-params"><span class="hljs-attr">request</span>: <span class="hljs-title class_">Request</span></span>) {
  <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> req.<span class="hljs-title function_">json</span>()
  <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Response</span>(<span class="hljs-literal">null</span>, { <span class="hljs-attr">status</span>: data.<span class="hljs-property">code</span> === <span class="hljs-string">&#x27;foo&#x27;</span> ? <span class="hljs-number">200</span> : <span class="hljs-number">401</span> })
}
</code></pre><p>Isn&#39;t this cumbersome? Having to repeat this process for each server interaction only bloats client-side code.</p>
<h3 id="interaction-with-html-streaming">Interaction with HTML Streaming</h3>
<p>With HTML Streaming Over the Wire, the workflow in Brisa transforms:</p>
<ul>
<li>Upon capturing a client event within a server component, the process resembles that of an endpoint, where access to the browser event serialized from the server is available thanks to Brisa&#39;s RPC.</li>
<li>Upon completing event processing within the server component, the RPC runtime on the client side updates only the modified portions of the web, streaming HTML generated from the rerender executed on the server.</li>
</ul>
<figure align="center">
  <img src="https://aralroca.com/images/blog-images/server.png" width="820px" height="437px" class="transparent" alt="Server interaction with server code" class="center" />
  <figcaption><small>Server interaction with server code</small></figcaption>
</figure>

<p>Server component:</p>
<pre><code class="hljs language-tsx"><span class="hljs-keyword">export</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">ServerComponent</span>(<span class="hljs-params">{}, <span class="hljs-attr">request</span>: <span class="hljs-title class_">RequestContext</span></span>) {
  <span class="hljs-keyword">async</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">handleInputOnServer</span>(<span class="hljs-params">e</span>) {
    <span class="hljs-keyword">if</span> (e.<span class="hljs-property">target</span>.<span class="hljs-property">value</span> === <span class="hljs-string">&#x27;foo&#x27;</span>) {
      request.<span class="hljs-property">store</span>.<span class="hljs-title function_">set</span>(<span class="hljs-string">&#x27;display-content&#x27;</span>, <span class="hljs-literal">true</span>)
      <span class="hljs-title function_">rerenderInAction</span>()
    }
  }

  <span class="hljs-keyword">if</span> (request.<span class="hljs-property">store</span>.<span class="hljs-title function_">get</span>(<span class="hljs-string">&#x27;display-content&#x27;</span>)) <span class="hljs-keyword">return</span> <span class="hljs-string">&#x27;some content&#x27;</span>

  <span class="hljs-keyword">return</span> <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">&quot;text&quot;</span> <span class="hljs-attr">onInput</span>=<span class="hljs-string">{handleInputOnServer}</span> <span class="hljs-attr">debounceInput</span>=<span class="hljs-string">{300}</span> /&gt;</span></span>
}
</code></pre><p>In this workflow, there is <code>debounce[Event]</code> attribute inspired by HTMX to make the RPC client do the debounce, the rest is code that runs only on the server, and the store lives on request time.</p>
<p>This new workflow adds zero bytes of client-side code for each server interaction. Why add client-side JavaScript to perform the same DOM update task? This is the essence of the RPC code, which remains constant over time. The server actions are in charge of streaming HTML for the client RPC to update the UI.</p>
<figure align="center">
  <img src="https://aralroca.com/images/blog-images/server-action.gif" width="800px" height="408px" alt="Server interaction example in Brisa" class="center" />
  <figcaption><small>Server interaction example in Brisa</small></figcaption>
</figure>

<p>By eliminating JSON transmission, we can leverage streaming for significantly faster and more progressive UI updates, even enabling &quot;suspense&quot; without additional client-side code.</p>
<h2 id="diff-dom-algorithm-with-streaming">Diff DOM Algorithm with Streaming</h2>
<p>The Diff DOM (Document Object Model) algorithm has been utilized for years to simulate React&#39;s &quot;Virtual DOM&quot; without virtualization. Instead, it operates directly on the &quot;Browser DOM,&quot; comparing two HTML nodes efficiently and updating the first DOM tree based on the modifications present in the second.</p>
<p>In essence, HTML Over the Wire (without streaming) has been achievable for years thanks to the Diff DOM Algorithm, widely adopted by many modern libraries and frameworks.</p>
<figure align="center">
  <img src="https://aralroca.com/images/blog-images/boxes.gif" width="800px" height="333px" class="transparent" alt="Updating boxes" class="center" />
  <figcaption><small>Updating boxes</small></figcaption>
</figure>

<p>React, for instance, avoids using HTML for transmission between server components (RSCs) to facilitate streaming in single-page applications and communication with server actions. It relies on progressively loaded JSON with &quot;holes&quot;, which must be processed before employing the Virtual DOM. <a href="https://twitter.com/dan_abramov2">Dan Abramov</a> elucidated this in a <a href="https://twitter.com/dan_abramov2/status/1762099439303295409">tweet</a> a few months ago. I inquired further in another <a href="https://twitter.com/aralroca/status/1762113683784614065">tweet</a> regarding the feasibility of HTML Streaming, to which he cited two obstacles:</p>
<blockquote>
<ol>
<li><p>doesn’t help with passing new data to stateful stuff (like a list of todos to an already mounted todo list component)</p>
</li>
<li><p>you’d have to either parse HTML yourself (non-trivial to do correctly) or create throwaway DOM nodes</p>
</li>
</ol>
</blockquote>
<p>Upon analysis, I found these obstacles to be surmountable.</p>
<p>Regarding the first point, Brisa obviates the need to pass data for stateful components, as client components utilize real DOM elements, namely web components. When attributes are modified, they react to changes using signals, updating their content while preserving the state. Hence, use native constructs—web components, signals (currently as a <a href="https://github.com/proposal-signals/proposal-signals">proposal</a> in TC39 with stage 0), and HyperText Markup Language—made more sense.</p>
<p>Regarding the second point, browsers offer a native &quot;hack&quot; for parsing nodes from a stream. This technique is elucidated in a video on the &quot;Chrome for Developers&quot; YouTube channel <a href="https://www.youtube.com/watch?v=LLRig4s1_yA&t=1286s">here</a>.</p>
<p>Therefore, to support the Diff DOM Algorithm with HTML Streaming, three aspects must be considered:</p>
<ol>
<li><strong>DFS (Depth-First Search)</strong>: Instead of implementing breadth-first search (<a href="https://en.wikipedia.org/wiki/Breadth-first_search">BFS</a>) for analyzing the DOM tree, <a href="https://en.wikipedia.org/wiki/Depth-first_search">DFS</a> must be employed to synchronize with streaming, as HTML chunks during streaming always arrive in DFS order.</li>
<li><strong>Parsing HTML String Chunks to HTML Nodes</strong>: Efficient parsing of each incoming node during streaming is essential, along with traversal among the arriving nodes.</li>
<li><strong>Stop and Wait for Missing Chunks</strong>: Identifying instances where comparison involves data yet to be received, waiting only as necessary without blocking.</li>
</ol>
<figure align="center">
  <img src="https://aralroca.com/images/blog-images/boxes-streaming.gif" width="800px" height="333px" class="transparent" alt="Updating boxes with chunks received every 100ms" class="center" />
  <figcaption><small>Updating boxes with chunks received every 100ms</small></figcaption>
</figure>

<h2 id="show-me-the-code">Show me the code</h2>
<p>I&#39;ve open-sourced <a href="https://github.com/aralroca/diff-dom-streaming"><code>diff-dom-streaming</code></a>, a library weighing only 1KB. Most of the time, you&#39;ll load it lazily since user interaction triggers its need, eliminating the necessity of loading it upfront.</p>
<pre><code class="hljs language-ts"><span class="hljs-keyword">import</span> diff <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;https://unpkg.com/diff-dom-streaming@latest&#x27;</span>

<span class="hljs-comment">// ...</span>

<span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> <span class="hljs-title function_">fetch</span>(<span class="hljs-comment">/* some url */</span>)

<span class="hljs-comment">// Apply diff DOM between the current document</span>
<span class="hljs-comment">// and the stream reader:</span>
<span class="hljs-keyword">await</span> <span class="hljs-title function_">diff</span>(<span class="hljs-variable language_">document</span>, res.<span class="hljs-property">body</span>.<span class="hljs-title function_">getReader</span>())
</code></pre><p>This library heralds the reality of HTML Streaming Over the Wire, accessible not just to Brisa but to numerous other libraries and frameworks. You can even employ it in vanilla JavaScript without any additional libraries.</p>
<h3 id="community-support-and-adoption">Community Support and Adoption</h3>
<p>If you see potential in HTML Streaming Over the Wire and wish to contribute to its growth, consider giving the <a href="https://github.com/aralroca/diff-dom-streaming"><code>diff-dom-streaming</code></a> library a star on GitHub. Your support helps to promote its visibility and encourages further development in this innovative approach to web development.</p>
<h2 id="show-me-some-example">Show me some example</h2>
<p>Here&#39;s the <a href="https://stackblitz.com/edit/diff-dom-streaming?file=index.js">demo of the boxes</a> you can try out without any framework.</p>
<p>{% twitter 1776956020574585206 %}</p>
<h2 id="conclusion">Conclusion</h2>
<p>HTML Streaming Over the Wire, empowered by the Diff DOM algorithm, promises a return to the web&#39;s core principles, paving the way for a faster, more responsive, and scalable web experience for all. The future of this technology holds immense potential, and I&#39;m eager to see how it unfolds in more frameworks and libraries.</p>
]]></content:encoded>
            </item>
            <item>
              <title>🏝️ i18n translations in Next.js 13's app-dir for server/client components 🌊</title>
              <description>In this blog post, we explore how to use Next.js 13's app directory and consume i18n translations for both server and client components in an easy way. With this approach, you can reduce the size of your bundles and maintain the code clean.</description>
              <link>https://aralroca.com/blog/i18n-translations-nextjs-13-app-dir</link>
              <guid isPermaLink="false">https://aralroca.com/blog/i18n-translations-nextjs-13-app-dir/</guid>
              <pubDate>Sun Feb 19 2023 00:00:00 GMT+0000 (Coordinated Universal Time)</pubDate>
              <content:encoded><![CDATA[<p>In this post, I will explain how to easily load and use translations in the pages within the <strong><a href="https://beta.nextjs.org/docs/app-directory-roadmap"><code>app</code> directory</a></strong> of Next.js 13, a <strong>new paradigm</strong> for managing your pages that, if mismanaged, <strong>can lead to headaches</strong>.</p>
<h2 id="before-we-begin-a-little-context">Before we begin... A little context</h2>
<p>First, let me tell you about <strong><a href="https://github.com/aralroca/next-translate">Next-translate</a></strong>. It is one of the most widely used libraries for <strong>loading</strong> and <strong>using translations</strong> in <strong>Next.js pages</strong> (~70k weekly downloads) and has existed since Next.js 9.</p>
<p>By default, translations are stored in <em><strong>locales/</strong>{lang}/{namespace}.json</em>:</p>
<pre><code class="hljs language-bash">.
├── en
│   ├── common.json
│   └── home.json
└── es
    ├── common.json
    └── home.json
</code></pre><p><small>*However, they can also be loaded from a CDN or integrated with an online internationalization platform like localize.</small></p>
<p>🦄 One of the library&#39;s goals is to be <strong>easy to use</strong>. With a little <a href="https://github.com/aralroca/next-translate/tree/master#3-configuration">configuration</a> in the <code>i18n.js</code> file, you can already consume these translations in your pages and components:</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">import</span> useTranslation <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;next-translate/useTranslation&#x27;</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">Example</span>(<span class="hljs-params"></span>) {
  <span class="hljs-keyword">const</span> { t, lang } = <span class="hljs-title function_">useTranslation</span>(<span class="hljs-string">&#x27;common&#x27;</span>)
  <span class="hljs-keyword">return</span> <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>{t(&#x27;example&#x27;)}<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span></span>
}
</code></pre><p>🌍 Another goal of the library is to have all the <strong>i18n essentials</strong>: interpolation, plurals, formatters, translations with HTML tags, etc.</p>
<p>📦 And another goal is to be as <strong>lightweight as possible</strong>. In version 1.0, we made it occupy <strong>~1kb</strong>, but with version 2.0, there have been even more improvements that we will see in this post.</p>
<p>I won&#39;t go into further detail on how the library works. I invite you to read the <a href="https://github.com/aralroca/next-translate/tree/master">documentation</a> if you want more details.</p>
<img width="200" height="200" src="https://aralroca.com/images/blog-images/nt-logo.svg" alt="Next-translate logo" class="center">

<p>Now that I have introduced the library, let&#39;s see if it is still easy to use, lightweight, and has all the i18n essentials in the <strong>new paradigm</strong> that Next.js 13 introduced in late 2022: the <strong>app directory</strong>...</p>
<h2 id="what-is-the-app-dir">What is the app dir?</h2>
<p>The app directory in Next.js 13 (beta) introduces a new routing and data fetching system that supports layouts, nested routes, and utilizes <strong><a href="https://reactjs.org/blog/2020/12/21/data-fetching-with-react-server-components.html">React 18 Server Components</a></strong> by default.</p>
<p>Server components are a new type of React component that executes on the server and on the <strong>server only</strong>. This means that they are never shipped to the client, resulting in a significant reduction in the amount of code that needs to be shipped to the browser. This reduction in the amount of code can <strong>improve the performance</strong> of React applications, especially those dealing with large amounts of data.</p>
<p>They were originally thought of as a way to <strong>solve</strong> the <strong>&quot;waterfall problem&quot;</strong> in React applications. This <strong>occurs when</strong> a React component <strong>requires data</strong> from a server, but that data <strong>is not available yet</strong>. This can result in a &quot;waterfall&quot; of network requests, as <strong>each component waits</strong> for the data it needs before it can render, <strong>causing significant delays</strong> in rendering and impacting the user experience.</p>
<p>With Server components <strong>allows</strong> the server to <strong>fetch all</strong> the required data in one go, instead of going back and forth with the client. This results in a significant <strong>improvement</strong> in the <strong>performance</strong> of React applications.</p>
<p>They also <strong>reduce</strong> the <strong>amount of code</strong> that needs to be shipped to the client, which can improve the load time of React applications. They can also <strong>improve</strong> the <strong>maintainability</strong> of applications, as they can make it easier to manage data fetching in large applications.</p>
<p>However, if there are parts of your code that require interactivity with the user, you can create <strong>client component islands</strong>, where the page only uses the kilobytes of JavaScript used in these islands. As less JavaScript is always better, this approach is ideal.</p>
<figure align="center">
<img width="500" src="https://aralroca.com/images/blog-images/merch-islands-example.png" alt="Merch islands example" class="center">  <figcaption><small>Image of islands, from Deno <a href="https://deno.com/blog/the-future-and-past-is-server-side-rendering" target="_blank">post</a> blog</small></figcaption>
</figure>

<p>One of the challenges with this paradigm shift of working with server components and client islands is that they function differently and are like <strong>two separate worlds</strong>. Therefore, the <strong>goal</strong> is to <strong>find a way</strong> to make the <strong>usage of both</strong> as <strong>simple</strong> and <strong>consistent</strong> as possible.</p>
<h2 id="server-components-headaches">Server components headaches</h2>
<p>If a programmer works with both Server Components and Client Components at the same time, they may face some <strong>challenges</strong> in terms of <strong>integration</strong> and <strong>coordination</strong> between the two types of components.</p>
<p>One of the main challenges could be the need to <strong>maintain coherence</strong> between the <strong>data</strong> handled on the server and on the client. For example, if a server page loads a translation namespace and all page components need to use these translations, island client components must also have access to the same translations <strong>without fetching</strong> them from the client, which requires <strong>coordinating data requests</strong> to <strong>avoid</strong> unnecessary <strong>redundancies</strong> and <strong>ensure</strong> that the data is obtained <strong>efficiently</strong> to avoid delays in site loading and <strong>reduce</strong> unnecessary <strong>bandwidth</strong> usage.</p>
<img width="500" height="333" src="https://aralroca.com/images/blog-images/headache.jpg" alt="Headache" class="center">

<p>Finally, there may be <strong>challenges</strong> in <strong>integrating libraries</strong> used to work with Server Components and Client Components. Ensuring that these tools work well together can be a challenge. So let&#39;s see how we have dealt with these problems at Next-translate.</p>
<h2 id="load-and-consume-translations-in-nextjs-13-app-dir">Load and consume translations in Next.js 13 app dir</h2>
<p>In <strong><a href="https://github.com/aralroca/next-translate/releases/tag/2.0.0">Next-translate 2.0</a></strong>, we have struggled with this issue to make the solution as <strong>user-friendly as possible</strong>. In the end, we achieved this goal, and no additional configuration is necessary in your <code>i18n.js</code> setup. 🎉</p>
<p>Simply keep in mind 🧠 that the <strong>pages defined</strong> in the <strong>configuration</strong> are <strong>now used</strong> for the <strong>pages</strong> within the <strong>app directory</strong>:</p>
<pre><code class="hljs language-js"><span class="hljs-comment">// i18n.js</span>
<span class="hljs-variable language_">module</span>.<span class="hljs-property">exports</span> = {
  <span class="hljs-attr">locales</span>: [<span class="hljs-string">&#x27;en&#x27;</span>, <span class="hljs-string">&#x27;ca&#x27;</span>, <span class="hljs-string">&#x27;es&#x27;</span>],
  <span class="hljs-attr">defaultLocale</span>: <span class="hljs-string">&#x27;en&#x27;</span>,
  <span class="hljs-attr">pages</span>: {
    <span class="hljs-string">&#x27;*&#x27;</span>: [<span class="hljs-string">&#x27;common&#x27;</span>],
    <span class="hljs-string">&#x27;/&#x27;</span>: [<span class="hljs-string">&#x27;home&#x27;</span>], <span class="hljs-comment">// app/page.tsx</span>
    <span class="hljs-string">&#x27;/checkout&#x27;</span>: [<span class="hljs-string">&#x27;checkout&#x27;</span>], <span class="hljs-comment">// app/checkout/page.tsx</span>
  },
}
</code></pre><p>That&#39;s all you need to do! 😜</p>
<p>Our <strong><a href="https://github.com/aralroca/next-translate-plugin">plugin</a></strong>, which was already present Next-translate 1.0, now in Next-translate 2.0 takes care of loading translations in both server and client pages, as well as client component islands within a server page. Furthermore, we have reduced the size of our library from 1.8kb to <strong>498B</strong> (minified + gzipped).</p>
<figure align="center">
<img width="500" height="347" src="https://aralroca.com/images/blog-images/bundlephobia-next-translate-2.png" alt="Bundlephobia Next-translate 2.0" class="center">
 <figcaption><small><a href="https://bundlephobia.com/package/next-translate" target="_blank">Bundlephobia</a> capture: v1.6 -> 1.8kb - v2.0 -> 498B</small></figcaption>
</figure>

<p><strong>Prior</strong> to the app directory, the pages always had <strong>1.8kb</strong> of translations in the bundle, <strong>but now</strong>:</p>
<ul>
<li>If you have all translations in <strong>server components</strong>: <strong>0kb</strong> 🥳</li>
<li>If you need <strong>client</strong> component <strong>islands</strong> that change the text according to user interaction: <strong>498B</strong> 🎉</li>
</ul>
<p>To consume these translations, use the <strong><a href="https://github.com/aralroca/next-translate#usetranslation">useTranslation</a></strong> hook or the <strong><a href="https://github.com/aralroca/next-translate#trans-component">Trans</a></strong> component:</p>
<p><strong>🌊 Server page (+0kb): <code>app/page.js</code>:</strong></p>
<pre><code class="hljs language-js"><span class="hljs-keyword">import</span> useTranslation <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;next-translate/useTranslation&#x27;</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">HomePage</span>(<span class="hljs-params"></span>) {
  <span class="hljs-keyword">const</span> { t, lang } = <span class="hljs-title function_">useTranslation</span>(<span class="hljs-string">&#x27;home&#x27;</span>)

  <span class="hljs-keyword">return</span> <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>{t(&#x27;title&#x27;)}<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span></span>
}
</code></pre><p>In this case, we are dealing with a server page, as pages are by default server components. Under the hood, the <code>next-translate-plugin</code> <strong>dynamically</strong> loads translations <strong>directly</strong>, without the need for the <code>useEffect</code> hook or <code>context</code>. It also sets the <strong>language</strong> and <strong>namespace</strong> in the <strong>HTML</strong>, enabling other components, such as client-side islands, to hydrate and consume the same translations loaded for the page.</p>
<p><strong>🏝️ Client page (+498B): <code>app/checkout/page.js</code></strong></p>
<pre><code class="hljs language-js"><span class="hljs-string">&#x27;use client&#x27;</span>
<span class="hljs-keyword">import</span> useTranslation <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;next-translate/useTranslation&#x27;</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">CheckoutPage</span>(<span class="hljs-params"></span>) {
  <span class="hljs-keyword">const</span> { t, lang } = <span class="hljs-title function_">useTranslation</span>(<span class="hljs-string">&#x27;checkout&#x27;</span>)

  <span class="hljs-keyword">return</span> <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>{t(&#x27;title&#x27;)}<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span></span>
}
</code></pre><p>For client pages, which are not the default in Next.js, it is necessary to indicate that they are client-side using the <strong><code>&quot;use client&quot;</code></strong> line, which is a new feature in the <code>app</code> directory of Next.js 13. By default, if this line is not present, the page is server-side. Under the hood, the <code>next-translate-plugin</code> loads translations for these pages using the <code>useEffect</code> hook. In this case, there is no need to add <strong>anything to the HTML</strong> because all components will <strong>already</strong> have <strong>access</strong> to the <strong>translations</strong> without the need for hydration.</p>
<p><strong>🏝️ Client component (+498B): <code>components/ClientIsland</code></strong></p>
<pre><code class="hljs language-js"><span class="hljs-string">&#x27;use client&#x27;</span>
<span class="hljs-keyword">import</span> useTranslation <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;next-translate/useTranslation&#x27;</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">ClientIsland</span>(<span class="hljs-params"></span>) {
  <span class="hljs-keyword">const</span> { t, lang } = <span class="hljs-title function_">useTranslation</span>(<span class="hljs-string">&#x27;common&#x27;</span>)
  <span class="hljs-keyword">return</span> <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>{t(&#x27;island&#x27;)}<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span></span>
}
</code></pre><p>The same logic applies to components: if they do not have the <code>&quot;use client&quot;</code> line, they are server-side by default, but if they include it, they become client <strong>component islands</strong> that can be used on a server page.</p>
<p>In this case, the <code>next-translate-plugin</code> will check whether hydration is necessary, since it is possible that the translations are already accessible if the parent page or component was also a client.</p>
<p><strong>🌊 Server component (+0kb): <code>components/ServerSea</code></strong></p>
<pre><code class="hljs language-js"><span class="hljs-keyword">import</span> useTranslation <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;next-translate/useTranslation&#x27;</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">ServerSea</span>(<span class="hljs-params"></span>) {
  <span class="hljs-keyword">const</span> { t, lang } = <span class="hljs-title function_">useTranslation</span>(<span class="hljs-string">&#x27;common&#x27;</span>)
  <span class="hljs-keyword">return</span> <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>{t(&#x27;sea&#x27;)}<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span></span>
}
</code></pre><p>For server components, the <strong>plugin does not perform any transformation</strong>, as translations have already been loaded at the page level, and direct access to the translations is available within the <code>useTranslation</code> hook.</p>
<p><strong>🌊🏝️🏝️ Server page with client islands (+498B): <code>app/page.js</code></strong></p>
<pre><code class="hljs language-js"><span class="hljs-keyword">import</span> <span class="hljs-title class_">ServerSea</span> <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;@components/ServerSea&#x27;</span> <span class="hljs-comment">// this part 0kb</span>
<span class="hljs-keyword">import</span> <span class="hljs-title class_">ClientIsland</span> <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;@components/ClientIsland&#x27;</span>
<span class="hljs-keyword">import</span> <span class="hljs-title class_">AnotherClientIsland</span> <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;@components/AnotherClientIsland&#x27;</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">HomePage</span>(<span class="hljs-params"></span>) {
  <span class="hljs-keyword">return</span> (
    <span class="language-xml"><span class="hljs-tag">&lt;&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">ServerSea</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">ClientIsland</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">AnotherClientIsland</span> /&gt;</span>
    <span class="hljs-tag">&lt;/&gt;</span></span>
}
</code></pre><p>However, if there is a server page with client components, the client components must hydrate what has been provided from the server page and rerender.</p>
<h2 id="i18n-routing-with-app-dir">i18n routing with app dir</h2>
<p>Next.js 10 introduced <a href="https://nextjs.org/docs/advanced-features/i18n-routing">i18n routing</a> support, allowing pages to be rendered by navigating to <code>/es/page-name</code>, where the page <code>pages/page-name.js</code> was accessed using the <code>useRouter</code> hook to obtain the <code>locale</code>.</p>
<p>However, since the pages have been moved from the <code>pages</code> dir to the <strong>app dir</strong>, this i18n routing <strong>no longer works correctly</strong>.</p>
<p>At Next-translate, we have chosen not to re-implement this functionality, as we aim to be a library for translating pages, rather than routing them. We hope that in the future, this feature will be implemented in the <code>app</code> directory, as it is still in beta and many features still need to be supported.</p>
<p>However, all the support currently available is with the <code>lang</code> parameter. That is, <code>/es/page-name?lang=es</code> renders the page <code>app/page-name/page.js</code>, where we have internal access to the <code>lang</code> parameter, and you <strong>do not need</strong> to do <strong>anything extra</strong> other than using the <code>useTranslation</code> hook to consume your translations.</p>
<p>All the same, if you wish to use the language as a subpath <code>/es/page-name</code> without the param, you can utilize middleware to append the <code>lang</code> parameter and perform a rewrite:</p>
<pre><code class="hljs language-js"><span class="hljs-comment">// middleware.ts</span>
<span class="hljs-keyword">import</span> { <span class="hljs-title class_">NextResponse</span> } <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;next/server&#x27;</span>
<span class="hljs-keyword">import</span> type { <span class="hljs-title class_">NextRequest</span> } <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;next/server&#x27;</span>
<span class="hljs-keyword">import</span> i18n <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;./i18n&#x27;</span>

<span class="hljs-comment">// /es/page-name -&gt; rewrites to -&gt; /es/page-name?lang=es</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">middleware</span>(<span class="hljs-params">request: NextRequest</span>) {
  <span class="hljs-keyword">const</span> locale = request.<span class="hljs-property">nextUrl</span>.<span class="hljs-property">locale</span> || i18n.<span class="hljs-property">defaultLocale</span>
  request.<span class="hljs-property">nextUrl</span>.<span class="hljs-property">searchParams</span>.<span class="hljs-title function_">set</span>(<span class="hljs-string">&#x27;lang&#x27;</span>, locale)
  request.<span class="hljs-property">nextUrl</span>.<span class="hljs-property">href</span> = request.<span class="hljs-property">nextUrl</span>.<span class="hljs-property">href</span>.<span class="hljs-title function_">replace</span>(<span class="hljs-string">`/<span class="hljs-subst">${locale}</span>`</span>, <span class="hljs-string">&quot;&quot;</span>)
  <span class="hljs-keyword">return</span> <span class="hljs-title class_">NextResponse</span>.<span class="hljs-title function_">rewrite</span>(request.<span class="hljs-property">nextUrl</span>)
}
</code></pre><p>Here in the middleware, we are not adding the locale as a subpath, but rather eliminating the need to manually add the <code>lang</code> parameter. By default, the <strong>subpath still exists</strong>, but you <strong>cannot use <code>useRouter</code></strong> from <code>next/router</code> to access the <code>locale</code> within the components of the <code>app</code> directory. Therefore, we <strong>still need the parameter</strong>, even if we hide it from view.</p>
<p>And to navigate:</p>
<pre><code class="hljs language-js">&lt;<span class="hljs-title class_">Link</span> href={<span class="hljs-string">`/?lang=<span class="hljs-subst">${locale}</span>`</span>} <span class="hljs-keyword">as</span>={<span class="hljs-string">`/<span class="hljs-subst">${locale}</span>`</span>}&gt;
  {locale}
&lt;/<span class="hljs-title class_">Link</span>&gt;
</code></pre><p>If you need more i18n routing features like automatic locale detection you can follow these steps from the Next.js documentation:</p>
<ul>
<li><a href="https://beta.nextjs.org/docs/guides/internationalization">https://beta.nextjs.org/docs/guides/internationalization</a>.</li>
</ul>
<figure align="center">
<img width="500" height="445" src="https://aralroca.com/images/blog-images/routes.png" alt="Routes in different languages" class="center">
 <figcaption><small>Routes in different languages</small></figcaption>
</figure>

<h2 id="demo">Demo</h2>
<p>To conclude, if you are eager to try an example application using Next.js with the <code>app</code> directory and Next-translate, you can take a look at this example:</p>
<ul>
<li><a href="https://codesandbox.io/p/sandbox/next-translate-app-dir-fw68h2?file=%2Fsrc%2Fapp%2Fpage.tsx&selection=%5B%7B%22endColumn%22%3A1%2C%22endLineNumber%22%3A6%2C%22startColumn%22%3A1%2C%22startLineNumber%22%3A6%7D%5D">Codesandbox</a></li>
</ul>
<h2 id="conclusion">Conclusion</h2>
<p>This article discusses the Next.js 13 paradigm shift in managing pages, which introduces a new routing and data-fetching system that supports layouts, nested routes, and <strong>server components</strong>. Server components are a new type of React component that executes only on the server, reducing the amount of code that needs to be shipped to the browser and improving the performance of React applications.</p>
<p>The article also discusses the <strong>Next-translate</strong> library, which is one of the most widely used libraries for loading and using translations in Next.js pages. It aims to be easy to use, have all the i18n essentials, and be as lightweight as possible. The article evaluates whether Next-translate is still easy to use, lightweight, and has all the i18n essentials in the <strong>new paradigm</strong> of <strong>Next.js 13</strong>, and <strong>explains how to use it</strong>.</p>
<img width="500" height="347" src="https://aralroca.com/images/blog-images/learning-js.jpg" alt="Image from unsplash" class="center">

<h2 id="references">References</h2>
<ul>
<li>Next-translate repo: <a href="https://github.com/aralroca/next-translate">https://github.com/aralroca/next-translate</a></li>
<li>Next-translate 2.0 release: <a href="https://github.com/aralroca/next-translate/releases/tag/2.0.0">https://github.com/aralroca/next-translate/releases/tag/2.0.0</a></li>
<li>Next-translate-plugin repo: <a href="https://github.com/aralroca/next-translate-plugin">https://github.com/aralroca/next-translate-plugin</a></li>
<li>Next.js app dir docu: <a href="https://beta.nextjs.org/docs/guides/internationalization">https://beta.nextjs.org/docs/guides/internationalization</a></li>
<li>Next.js i18n routing docu (before app dir):<a href="https://nextjs.org/docs/advanced-features/i18n-routing">https://nextjs.org/docs/advanced-features/i18n-routing</a></li>
<li>Deno article about client islands: <a href="https://deno.com/blog/the-future-and-past-is-server-side-rendering">https://deno.com/blog/the-future-and-past-is-server-side-rendering</a></li>
<li>React server components: <a href="https://reactjs.org/blog/2020/12/21/data-fetching-with-react-server-components.html">https://reactjs.org/blog/2020/12/21/data-fetching-with-react-server-components.html</a></li>
<li>React 18 features: <a href="https://reactjs.org/blog/2022/03/29/react-v18.html">https://reactjs.org/blog/2022/03/29/react-v18.html</a></li>
</ul>
]]></content:encoded>
            </item>
            <item>
              <title>Learn Deno: Chat app</title>
              <description>Learn how Deno works and it's differences with Node.js by creating a Chat application.</description>
              <link>https://aralroca.com/blog/learn-deno-chat-app</link>
              <guid isPermaLink="false">https://aralroca.com/blog/learn-deno-chat-app/</guid>
              <pubDate>Sun May 10 2020 00:00:00 GMT+0000 (Coordinated Universal Time)</pubDate>
              <content:encoded><![CDATA[<p>Node.js was writtern initially by <a href="https://en.wikipedia.org/wiki/Ryan_Dahl">Ryan Dahl</a> on 2009 (in C++). Ryan left Node.js in 2012, as at this point he felt he had more or less fulfilled his goals.</p>
<p>His goals are now different. After realizing that there were some design errors impossible to fix in Node.js, he decided to create another JavaScript (also TypeScript) runtime built with V8: Deno (in Rust). Deno 1.0.0 will be finally released on 13th May 2020.</p>
<img src="https://aralroca.com/images/blog-images/38.svg" width="100" alt="Deno logo" class="center transparent" />

<p>We&#39;ll see how Deno works and its differences with Node, implementing a simple chat application.</p>
<p><strong>We will cover the following:</strong></p>
<ul>
<li><a href="#installing-deno">Installing Deno</a></li>
<li><a href="#simple-hello-world">Simple &quot;Hello World&quot;</a></li>
<li><a href="#serve-an-indexhtml">Serve an index.html</a></li>
<li><a href="#using-websockets">Using WebSockets</a></li>
<li><a href="#third-party-and-depsts-convention">Third-party and deps.ts convention</a></li>
<li><a href="#testing">Testing</a></li>
<li><a href="#debugging">Debugging</a></li>
<li><a href="#conclusion">Conclusion</a></li>
<li><a href="#code-of-this-article">Code of this article</a></li>
<li><a href="#references">References</a></li>
</ul>
<h2 id="installing-deno">Installing Deno</h2>
<p>There are different ways to install Deno: Using curl, iwr, Homebrew, Chocolatey... See how to install it <a href="https://github.com/denoland/deno/blob/34ec3b225425cecdccf754fbc87f4a8f3728890d/docs/getting_started/installation.md">here</a>. Deno is a single binary executable, it has no external dependencies.</p>
<p>In my case I&#39;m going to use Homebrew:</p>
<pre><code>➜  ~ brew install deno
➜  ~ deno --version
deno 1.0.0-rc1
v8 8.2.308
typescript 3.8.3
</code></pre><p>As we can see, there&#39;s no <code>npm</code> here. Npm started to be essential in the Node ecosystem... And it&#39;s a centralized (privately controlled even) repository for modules. This is now changing with Deno. We will see later how to install packages without a <code>package.json</code> and <code>node_modules</code> either.</p>
<p>To upgrade to the latest version we need to do <code>deno upgrade</code>.</p>
<p>I recommend to execute <code>deno help</code> to see all the possible usages:</p>
<pre><code>USAGE:
    deno [OPTIONS] [SUBCOMMAND]

OPTIONS:
    -h, --help                     Prints help information
    -L, --log-level &lt;log-level&gt;    Set log level [possible values: debug, info]
    -q, --quiet                    Suppress diagnostic output
    -V, --version                  Prints version information

SUBCOMMANDS:
    bundle         Bundle module and dependencies into single file
    cache          Cache the dependencies
    completions    Generate shell completions
    doc            Show documentation for a module
    eval           Eval script
    fmt            Format source files
    help           Prints this message or the help of the given subcommand(s)
    info           Show info about cache or info related to source file
    install        Install script as an executable
    repl           Read Eval Print Loop
    run            Run a program given a filename or url to the module
    test           Run tests
    types          Print runtime TypeScript declarations
    upgrade        Upgrade deno executable to newest version

ENVIRONMENT VARIABLES:
    DENO_DIR             Set deno&#x27;s base directory (defaults to $HOME/.deno)
    DENO_INSTALL_ROOT    Set deno install&#x27;s output directory
                         (defaults to $HOME/.deno/bin)
    NO_COLOR             Set to disable color
    HTTP_PROXY           Proxy address for HTTP requests
                         (module downloads, fetch)
    HTTPS_PROXY          Same but for HTTPS
</code></pre><p>In case that you are using Visual Studio Code, I recommend to install this plugin to ease working with Deno:</p>
<ul>
<li><a href="https://marketplace.visualstudio.com/items?itemName=axetroy.vscode-deno">https://marketplace.visualstudio.com/items?itemName=axetroy.vscode-deno</a></li>
</ul>
<h2 id="simple-hello-world">Simple &quot;Hello World&quot;</h2>
<p>For a simple &quot;Hello world&quot; in Deno, we just need to create a file <code>.js</code> or <code>.ts</code>, and execute it with <code>deno run [file]</code>.</p>
<p>In case of <code>.ts</code>, it will compile + execute, meanwhile for <code>.js</code>, the file will be executed directly:</p>
<pre><code class="hljs language-js"><span class="hljs-comment">// example.ts file</span>
<span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">&#x27;Hello from Deno 🖐&#x27;</span>)
</code></pre><p>And in the shell:</p>
<pre><code class="hljs language-bh">➜  deno run example.ts
Compile file:///Users/aralroca/example.ts
Hello from Deno 🖐
</code></pre><p>The <code>tsconfig.json</code> file is optional because in Deno there are some TypeScript defaults. To apply the <code>tsconfig.json</code> we should use <code>deno run -c tsconfig.json [file]</code>.</p>
<p>By the way, Deno uses web standards where possible. It&#39;s possible to use <code>window</code>, <code>fetch</code>, <code>Worker</code>... Our code should be compatible with both Deno and the browser.</p>
<h2 id="serve-an-indexhtml">Serve an index.html</h2>
<p>Deno has his own standard library <a href="https://deno.land/std/">https://deno.land/std/</a> so to use their modules we can import it directly from the <strong>URL</strong>. One of its goals is shipping only a single executable with minimal linkage. This way it&#39;s only necessary to import the URL to their projects, or execute directly with <code>deno run https://...</code> in case of CLIs.</p>
<p>In order to create a http server and serve an <code>index.html</code> we are going to use this module: <a href="https://deno.land/std/http/">https://deno.land/std/http/</a>.</p>
<p>We are going to create two files: <code>server.ts</code> and <code>index.html</code>.</p>
<p><small>index.html</small></p>
<pre><code class="hljs language-html"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">&quot;en&quot;</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">&quot;viewport&quot;</span> <span class="hljs-attr">content</span>=<span class="hljs-string">&quot;width=device-width, initial-scale=1&quot;</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">&quot;utf-8&quot;</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Example using Deno<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
    index.html served correctly
  <span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre><p><small>server.ts</small></p>
<pre><code class="hljs language-js"><span class="hljs-keyword">import</span> { listenAndServe } <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;https://deno.land/std/http/server.ts&#x27;</span>

<span class="hljs-title function_">listenAndServe</span>({ <span class="hljs-attr">port</span>: <span class="hljs-number">3000</span> }, <span class="hljs-title function_">async</span> (req) =&gt; {
  <span class="hljs-keyword">if</span> (req.<span class="hljs-property">method</span> === <span class="hljs-string">&#x27;GET&#x27;</span> &amp;&amp; req.<span class="hljs-property">url</span> === <span class="hljs-string">&#x27;/&#x27;</span>) {
    req.<span class="hljs-title function_">respond</span>({
      <span class="hljs-attr">status</span>: <span class="hljs-number">200</span>,
      <span class="hljs-attr">headers</span>: <span class="hljs-keyword">new</span> <span class="hljs-title class_">Headers</span>({
        <span class="hljs-string">&#x27;content-type&#x27;</span>: <span class="hljs-string">&#x27;text/html&#x27;</span>,
      }),
      <span class="hljs-attr">body</span>: <span class="hljs-keyword">await</span> <span class="hljs-title class_">Deno</span>.<span class="hljs-title function_">open</span>(<span class="hljs-string">&#x27;./index.html&#x27;</span>),
    })
  }
})

<span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">&#x27;Server running on localhost:3000&#x27;</span>)
</code></pre><p>We can use ESmodules by default instead of Common.js, indicating the file extension always at the end. Moreover, it supports the latest features as <code>async-await</code>.</p>
<p>Also, we don&#39;t need to worry about formatting anymore. Instead of using tools as Prettier, we can format the files with <code>deno fmt</code> command.</p>
<p>The first time <code>deno run server.ts</code> runs, we&#39;ll see two differences with respect to the &quot;Hello World&quot; example:</p>
<ol>
<li><p>It downloads all the dependencies from <code>http</code> module. Instead of using <code>yarn</code> or <code>npm install</code>, it should install all the needed dependencies before running the project. This happens only the first time, since it&#39;s cached. To clean the cache you can use the <code>--reload</code> command.</p>
</li>
<li><p>It throws an error <code>Uncaught PermissionDenied: network access to &quot;127.0.0.1:3000&quot;, run again with the --allow-net flag</code>. Deno is secure by default. This means that we can&#39;t access to the net or read a file (index.html). This is one of the big improvements over Node. In Node any CLI library could do many things without our consent. With Deno it&#39;s possible, for example, to allow reading access only in one folder: <code>deno --allow-read=/etc</code>. To see all permission flags, run <code>deno run -h</code>.</p>
</li>
</ol>
<p>Now we are ready to serve <code>index.html</code>:</p>
<pre><code class="hljs language-bh">➜ deno run --allow-net --allow-read server.ts
Compile file:///Users/aralroca/server.ts
Server running on localhost:3000
</code></pre><img class="center" src="https://aralroca.com/images/blog-images/32.png" alt="Deno server serving an index.html" />

<h2 id="using-websockets">Using WebSockets</h2>
<p>WebSockets, UUID, and other essentials in Node are not part of the core. This means that we need to use third-party libraries to use it. Yet, you can use WebSockets and UUID among many others by using Deno standard library. In other words, you don&#39;t need to worry about maintenance, because now it will be always maintained.</p>
<p>To continue implementing our simple chat app, let&#39;s create a new file <code>chat.ts</code> with:</p>
<pre><code class="hljs language-ts"><span class="hljs-keyword">import</span> {
  <span class="hljs-title class_">WebSocket</span>,
  isWebSocketCloseEvent,
} <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;https://deno.land/std/ws/mod.ts&#x27;</span>
<span class="hljs-keyword">import</span> { v4 } <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;https://deno.land/std/uuid/mod.ts&#x27;</span>

<span class="hljs-keyword">const</span> users = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Map</span>&lt;<span class="hljs-built_in">string</span>, <span class="hljs-title class_">WebSocket</span>&gt;()

<span class="hljs-keyword">function</span> <span class="hljs-title function_">broadcast</span>(<span class="hljs-params"><span class="hljs-attr">message</span>: <span class="hljs-built_in">string</span>, <span class="hljs-attr">senderId</span>?: <span class="hljs-built_in">string</span></span>): <span class="hljs-built_in">void</span> {
  <span class="hljs-keyword">if</span> (!message) <span class="hljs-keyword">return</span>
  <span class="hljs-keyword">for</span> (<span class="hljs-keyword">const</span> user <span class="hljs-keyword">of</span> users.<span class="hljs-title function_">values</span>()) {
    user.<span class="hljs-title function_">send</span>(senderId ? <span class="hljs-string">`[<span class="hljs-subst">${senderId}</span>]: <span class="hljs-subst">${message}</span>`</span> : message)
  }
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">chat</span>(<span class="hljs-params"><span class="hljs-attr">ws</span>: <span class="hljs-title class_">WebSocket</span></span>): <span class="hljs-title class_">Promise</span>&lt;<span class="hljs-built_in">void</span>&gt; {
  <span class="hljs-keyword">const</span> userId = v4.<span class="hljs-title function_">generate</span>()

  <span class="hljs-comment">// Register user connection</span>
  users.<span class="hljs-title function_">set</span>(userId, ws)
  <span class="hljs-title function_">broadcast</span>(<span class="hljs-string">`&gt; User with the id <span class="hljs-subst">${userId}</span> is connected`</span>)

  <span class="hljs-comment">// Wait for new messages</span>
  <span class="hljs-keyword">for</span> <span class="hljs-title function_">await</span> (<span class="hljs-keyword">const</span> event <span class="hljs-keyword">of</span> ws) {
    <span class="hljs-keyword">const</span> message = <span class="hljs-keyword">typeof</span> event === <span class="hljs-string">&#x27;string&#x27;</span> ? event : <span class="hljs-string">&#x27;&#x27;</span>

    <span class="hljs-title function_">broadcast</span>(message, userId)

    <span class="hljs-comment">// Unregister user conection</span>
    <span class="hljs-keyword">if</span> (!message &amp;&amp; <span class="hljs-title function_">isWebSocketCloseEvent</span>(event)) {
      users.<span class="hljs-title function_">delete</span>(userId)
      <span class="hljs-title function_">broadcast</span>(<span class="hljs-string">`&gt; User with the id <span class="hljs-subst">${userId}</span> is disconnected`</span>)
      <span class="hljs-keyword">break</span>
    }
  }
}
</code></pre><p>Now, register an endpoint <code>/ws</code> to expose the chat on <code>server.ts</code>:</p>
<pre><code class="hljs language-ts"><span class="hljs-keyword">import</span> { listenAndServe } <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;https://deno.land/std/http/server.ts&#x27;</span>
<span class="hljs-keyword">import</span> { acceptWebSocket, acceptable } <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;https://deno.land/std/ws/mod.ts&#x27;</span>
<span class="hljs-keyword">import</span> { chat } <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;./chat.ts&#x27;</span>

<span class="hljs-title function_">listenAndServe</span>({ <span class="hljs-attr">port</span>: <span class="hljs-number">3000</span> }, <span class="hljs-title function_">async</span> (req) =&gt; {
  <span class="hljs-keyword">if</span> (req.<span class="hljs-property">method</span> === <span class="hljs-string">&#x27;GET&#x27;</span> &amp;&amp; req.<span class="hljs-property">url</span> === <span class="hljs-string">&#x27;/&#x27;</span>) {
    req.<span class="hljs-title function_">respond</span>({
      <span class="hljs-attr">status</span>: <span class="hljs-number">200</span>,
      <span class="hljs-attr">headers</span>: <span class="hljs-keyword">new</span> <span class="hljs-title class_">Headers</span>({
        <span class="hljs-string">&#x27;content-type&#x27;</span>: <span class="hljs-string">&#x27;text/html&#x27;</span>,
      }),
      <span class="hljs-attr">body</span>: <span class="hljs-keyword">await</span> <span class="hljs-title class_">Deno</span>.<span class="hljs-title function_">open</span>(<span class="hljs-string">&#x27;./index.html&#x27;</span>),
    })
  }

  <span class="hljs-comment">// WebSockets Chat</span>
  <span class="hljs-keyword">if</span> (req.<span class="hljs-property">method</span> === <span class="hljs-string">&#x27;GET&#x27;</span> &amp;&amp; req.<span class="hljs-property">url</span> === <span class="hljs-string">&#x27;/ws&#x27;</span>) {
    <span class="hljs-keyword">if</span> (<span class="hljs-title function_">acceptable</span>(req)) {
      <span class="hljs-title function_">acceptWebSocket</span>({
        <span class="hljs-attr">conn</span>: req.<span class="hljs-property">conn</span>,
        <span class="hljs-attr">bufReader</span>: req.<span class="hljs-property">r</span>,
        <span class="hljs-attr">bufWriter</span>: req.<span class="hljs-property">w</span>,
        <span class="hljs-attr">headers</span>: req.<span class="hljs-property">headers</span>,
      }).<span class="hljs-title function_">then</span>(chat)
    }
  }
})

<span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">&#x27;Server running on localhost:3000&#x27;</span>)
</code></pre><p>To implement our client-side part, we are going to choose Preact to be able to use modules directly without the need of npm, babel and webpack, as we saw on the <a href="https://aralroca.com/blog/app-with-react-api-without-tools-as-webpack-or-babel">previous article</a>.</p>
<pre><code class="hljs language-html"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">&quot;en&quot;</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">&quot;UTF-8&quot;</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Chat using Deno<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">&quot;app&quot;</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">type</span>=<span class="hljs-string">&quot;module&quot;</span>&gt;</span><span class="language-javascript">
      <span class="hljs-keyword">import</span> {
        html,
        render,
        useEffect,
        useState,
      } <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;https://unpkg.com/htm/preact/standalone.module.js&#x27;</span>

      <span class="hljs-keyword">let</span> ws

      <span class="hljs-keyword">function</span> <span class="hljs-title function_">Chat</span>(<span class="hljs-params"></span>) {
        <span class="hljs-comment">// Messages</span>
        <span class="hljs-keyword">const</span> [messages, setMessages] = <span class="hljs-title function_">useState</span>([])
        <span class="hljs-keyword">const</span> <span class="hljs-title function_">onReceiveMessage</span> = (<span class="hljs-params">{ data }</span>) =&gt; <span class="hljs-title function_">setMessages</span>(<span class="hljs-function">(<span class="hljs-params">m</span>) =&gt;</span> [...m, data])
        <span class="hljs-keyword">const</span> <span class="hljs-title function_">onSendMessage</span> = (<span class="hljs-params">e</span>) =&gt; {
          <span class="hljs-keyword">const</span> msg = e.<span class="hljs-property">target</span>[<span class="hljs-number">0</span>].<span class="hljs-property">value</span>

          e.<span class="hljs-title function_">preventDefault</span>()
          ws.<span class="hljs-title function_">send</span>(msg)
          e.<span class="hljs-property">target</span>[<span class="hljs-number">0</span>].<span class="hljs-property">value</span> = <span class="hljs-string">&#x27;&#x27;</span>
        }

        <span class="hljs-comment">// Websocket connection + events</span>
        <span class="hljs-title function_">useEffect</span>(<span class="hljs-function">() =&gt;</span> {
          <span class="hljs-keyword">if</span> (ws) ws.<span class="hljs-title function_">close</span>()
          ws = <span class="hljs-keyword">new</span> <span class="hljs-title class_">WebSocket</span>(<span class="hljs-string">`ws://<span class="hljs-subst">${<span class="hljs-variable language_">window</span>.location.host}</span>/ws`</span>)
          ws.<span class="hljs-title function_">addEventListener</span>(<span class="hljs-string">&#x27;message&#x27;</span>, onReceiveMessage)

          <span class="hljs-keyword">return</span> <span class="hljs-function">() =&gt;</span> {
            ws.<span class="hljs-title function_">removeEventListener</span>(<span class="hljs-string">&#x27;message&#x27;</span>, onReceiveMessage)
          }
        }, [])

        <span class="hljs-keyword">return</span> html`<span class="language-xml">
          </span><span class="hljs-subst">${messages.map((message) =&gt; html`<span class="language-xml"> <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span></span><span class="hljs-subst">${message}</span><span class="language-xml"><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span> `</span>)}</span><span class="language-xml">

          <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">onSubmit</span>=</span></span><span class="hljs-subst">${onSendMessage}</span><span class="language-xml"><span class="hljs-tag">&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">&quot;text&quot;</span> /&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">button</span>&gt;</span>Send<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
        `</span>
      }

      <span class="hljs-title function_">render</span>(html`<span class="language-xml">&lt;</span><span class="hljs-subst">${Chat}</span><span class="language-xml"> /&gt;`</span>, <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">getElementById</span>(<span class="hljs-string">&#x27;app&#x27;</span>))
    </span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre><p>Result:</p>
<img class="center" src="https://aralroca.com/images/blog-images/33.gif" alt="Chat implemented with Deno + Preact" />

<p>It&#39;s a very ugly chat without styles, but functional, because our aim here is to understand how Deno works.</p>
<h2 id="third-party-and-depsts-convention">Third-party and deps.ts convention</h2>
<p>We can use third-party libraries in the same way we use the Deno Standard Library, by importing directly the URL of the module.</p>
<ul>
<li>STD, Deno core libraries: <a href="https://deno.land/std/">https://deno.land/std/</a></li>
<li>X, Deno Third-party libraries: <a href="https://deno.land/x/">https://deno.land/x/</a></li>
</ul>
<p>However, the ecosystem in <a href="https://deno.land/x/">https://deno.land/x/</a> is quite small yet. But hey, I have good news for you, we can use packages from <a href="https://www.pika.dev">https://www.pika.dev</a>. Thanks to tools like Parcel or Minibundle we can compile Node libraries into modules to re-use them in Deno projects.</p>
<p>We are going to use the <a href="https://www.pika.dev/npm/camel-case">camel-case</a> package to transform every chat message to camelCase!</p>
<img class="center" src="https://aralroca.com/images/blog-images/34.png" alt="Importing camel-case lib from pika web" />

<p>Let&#39;s add this import in our <code>chat.ts</code> file:</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">import</span> { camelCase } <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;https://cdn.pika.dev/camel-case@^4.1.1&#x27;</span>
<span class="hljs-comment">// ...before code</span>
<span class="hljs-keyword">const</span> message = <span class="hljs-title function_">camelCase</span>(<span class="hljs-keyword">typeof</span> event === <span class="hljs-string">&#x27;string&#x27;</span> ? event : <span class="hljs-string">&#x27;&#x27;</span>)
<span class="hljs-comment">// ... before code</span>
</code></pre><p>That&#39;s it. Running again the <code>server.ts</code> is going to download the <code>camel-case</code> package. Now you can see that it works:</p>
<img class="center" src="https://aralroca.com/images/blog-images/35.gif" alt="Using camel-case package on deno" />

<p>However, if I want to use this <code>camelCase</code> helper in more than one file, it&#39;s cumbersome to add the full import everywhere. The URL indicates which version of the package we have to use. This means that if we want to upgrade a dependency we will need to search and replace all the imports. This could cause us problems, but don&#39;t worry, there is a Deno convention for the dependencies that solves this. Creating a <code>deps.ts</code> file to export all project dependencies.</p>
<pre><code class="hljs language-js"><span class="hljs-comment">// deps.ts file</span>
<span class="hljs-keyword">export</span> { camelCase } <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;https://cdn.pika.dev/camel-case@^4.1.1&#x27;</span>
</code></pre><p>and</p>
<pre><code class="hljs language-js"><span class="hljs-comment">// chat.ts file</span>
<span class="hljs-keyword">import</span> { camelCase } <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;./deps.ts&#x27;</span>
<span class="hljs-comment">// ...</span>
<span class="hljs-keyword">const</span> message = <span class="hljs-title function_">camelCase</span>(<span class="hljs-keyword">typeof</span> event === <span class="hljs-string">&#x27;string&#x27;</span> ? event : <span class="hljs-string">&#x27;&#x27;</span>)
<span class="hljs-comment">// ...</span>
</code></pre><h2 id="testing">Testing</h2>
<p>We are going to build a useless <code>camelize.ts</code> utility to return the text in camelCase with a nice extra, it includes one 🐪 per uppercase letter. Why? To see how to test it with Deno.</p>
<pre><code class="hljs language-js"><span class="hljs-comment">/**
 * Return the text in camelCase + how many 🐪
 *
 * <span class="hljs-doctag">@example</span> &quot;this is an example&quot; -&gt; &quot;thisIsAnExample 🐪🐪🐪&quot;
 * <span class="hljs-doctag">@param</span> <span class="hljs-variable">text</span>
 * <span class="hljs-doctag">@returns</span> {<span class="hljs-type">string</span>}
 */</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">camelize</span>(<span class="hljs-params">text: string</span>) {
  <span class="hljs-comment">// @todo</span>
}
</code></pre><p>By the way, we can visualize the JSdocs of a file using <code>deno doc [file]</code>:</p>
<pre><code>➜  deno doc camelize.ts
function camelize(text: string)
  Return the text in camelCase + how many 🐪
</code></pre><p>Let&#39;s create a file <code>test.ts</code>. The test runner is built into the core of Deno using the <code>Deno.test()</code> and we can use assertions using the STD <a href="https://deno.land/std/testing/asserts.ts">https://deno.land/std/testing/asserts.ts</a>.</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">import</span> { assertStrictEq } <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;https://deno.land/std/testing/asserts.ts&#x27;</span>
<span class="hljs-keyword">import</span> { camelize } <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;./camelize.ts&#x27;</span>

<span class="hljs-title class_">Deno</span>.<span class="hljs-title function_">test</span>(<span class="hljs-string">&#x27;camelize works&#x27;</span>, <span class="hljs-title function_">async</span> () =&gt; {
  <span class="hljs-title function_">assertStrictEq</span>(<span class="hljs-title function_">camelize</span>(<span class="hljs-string">&#x27;this is an example&#x27;</span>), <span class="hljs-string">&#x27;thisIsAnExample 🐪🐪🐪&#x27;</span>)
})
</code></pre><p>To run all tests, we just need to execute <code>deno test</code>.</p>
<pre><code>➜  deno test
Compile file:///Users/aralroca/test.ts
running 1 tests
test camelize works ... FAILED (0ms)

failures:

camelize works
AssertionError: actual: undefined expected: thisIsAnExample 🐪🐪🐪
    at assertStrictEq (asserts.ts:224:11)
    at test.ts:5:3
    at asyncOpSanitizer ($deno$/testing.ts:36:11)
    at Object.resourceSanitizer [as fn] ($deno$/testing.ts:70:11)
    at TestApi.[Symbol.asyncIterator] ($deno$/testing.ts:264:22)
    at TestApi.next (&lt;anonymous&gt;)
    at Object.runTests ($deno$/testing.ts:346:20)

failures:

        camelize works

test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out (0ms)
</code></pre><p>Of course it fails because we didn&#39;t implemented our utility yet, but still, we can see how the errors are displayed in the shell.</p>
<p>After implementing the <code>camelize</code> utility:</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">import</span> { camelCase } <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;./deps.ts&#x27;</span>

<span class="hljs-comment">/**
 * Return the text in camelCase + how many 🐪
 *
 * <span class="hljs-doctag">@example</span> &quot;this is an example&quot; -&gt; &quot;thisIsAnExample 🐪🐪🐪&quot;
 * <span class="hljs-doctag">@param</span> <span class="hljs-variable">text</span>
 * <span class="hljs-doctag">@returns</span> {<span class="hljs-type">string</span>}
 */</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">camelize</span>(<span class="hljs-params">text: string</span>) {
  <span class="hljs-keyword">const</span> camelCaseText = <span class="hljs-title function_">camelCase</span>(text)
  <span class="hljs-keyword">const</span> matches = camelCaseText.<span class="hljs-title function_">match</span>(<span class="hljs-regexp">/[A-Z]/g</span>) || []
  <span class="hljs-keyword">const</span> camels = <span class="hljs-title class_">Array</span>.<span class="hljs-title function_">from</span>({ <span class="hljs-attr">length</span>: matches.<span class="hljs-property">length</span> })
    .<span class="hljs-title function_">map</span>(<span class="hljs-function">() =&gt;</span> <span class="hljs-string">&#x27;🐪&#x27;</span>)
    .<span class="hljs-title function_">join</span>(<span class="hljs-string">&#x27;&#x27;</span>)

  <span class="hljs-keyword">return</span> <span class="hljs-string">`<span class="hljs-subst">${camelCaseText}</span> <span class="hljs-subst">${camels}</span>`</span>
}
</code></pre><p>Now all tests pass:</p>
<pre><code>➜  deno test
Compile file:///Users/aralroca/camelize.ts
running 1 tests
test camelize works ... ok (3ms)

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out (3ms)
</code></pre><p>If you want to use a watcher to not execute everytime all tests, you can use <a href="https://deno.land/x/denon/">https://deno.land/x/denon/</a>, based on nodemon, and then run <code>denon test</code>.</p>
<p>Now we are ready to use our helper on <code>chat.ts</code>.</p>
<img class="center" src="https://aralroca.com/images/blog-images/36.png" alt="camels on the message" />
<br />

<h2 id="debugging">Debugging</h2>
<p>In order to debug with Deno:</p>
<ol>
<li>Add somewhere in your code a <code>debugger;</code> line of code.</li>
<li>Run with <code>--inspect-brk</code> flag. <code>deno run --inspect-brk ...</code> or <code>deno test --inspect-brk ...</code> to debug tests.</li>
<li>Open <code>chrome://inspect</code> page on Chrome.</li>
<li>On the Remote Target section press to &quot;inspect&quot;.</li>
<li>Press the Resume script execution button, the code will pause just in your breakpoint.</li>
</ol>
<img src="https://aralroca.com/images/blog-images/37.png" alt="Debugging with Deno" class="center" />
<br />

<h2 id="conclusion">Conclusion</h2>
<p>We learned about how Deno works by creating a simple chat app in TypeScript. We did it without npm, package.json, node_modules, webpack, babel, jest, prettier... because we don&#39;t need them, Deno simplifies this.</p>
<p>We explored important things to begin with a Deno project: Permissions, deno commands, how to use deno internals, how to use third-party dependencies, serving a file, websockets, formating files, testing, debugging, etc.</p>
<p>I hope this article will be useful to start using Deno 1.0.0 in your projects when it comes out on May 13, 2020.</p>
<h2 id="code-of-this-article">Code of this article</h2>
<p>I uploaded the code on my GitHub:</p>
<ul>
<li><a href="https://github.com/aralroca/chat-with-deno-and-preact">https://github.com/aralroca/chat-with-deno-and-preact</a></li>
</ul>
<h2 id="references">References</h2>
<ul>
<li><a href="https://deno.land/">https://deno.land/</a></li>
<li><a href="https://github.com/denoland/deno/tree/master/docs">https://github.com/denoland/deno/tree/master/docs</a></li>
<li><a href="https://blog.logrocket.com/deno-1-0-what-you-need-to-know/">https://blog.logrocket.com/deno-1-0-what-you-need-to-know/</a></li>
<li><a href="https://twitter.com/flaviocopes/status/1259068673966383105">https://twitter.com/flaviocopes/status/1259068673966383105</a></li>
<li><a href="https://www.youtube.com/watch?v=M3BM9TB-8yA">https://www.youtube.com/watch?v=M3BM9TB-8yA</a></li>
<li><a href="https://github.com/denoland/deno">https://github.com/denoland/deno</a></li>
<li><a href="https://en.wikipedia.org/wiki/Ryan_Dahl">https://en.wikipedia.org/wiki/Ryan_Dahl</a></li>
</ul>
]]></content:encoded>
            </item>
            <item>
              <title>Next-translate - Version 1.0 Released</title>
              <description>Next-translate is an i18n library to keep the translations as simple as possible in a Next.js environment. Today we announce the release of version 1.0.</description>
              <link>https://aralroca.com/blog/next-translate-1.0</link>
              <guid isPermaLink="false">https://aralroca.com/blog/next-translate-1.0/</guid>
              <pubDate>Wed Dec 09 2020 00:00:00 GMT+0000 (Coordinated Universal Time)</pubDate>
              <content:encoded><![CDATA[<p>Today is the day. The Vinissimus Team is very proud and happy to announce the much-anticipated <a href="https://github.com/vinissimus/next-translate/releases/tag/1.0.0">version 1.0</a> of <a href="https://github.com/vinissimus/next-translate">Next-translate</a> library. It&#39;s been a year since the first <a href="https://aralroca.com/blog/next-translate-released">version 0.1</a> and a lot happened <em>(+160 closed issues)</em>.</p>

<div align="center"><small>Showing version 1.0 when it was experimental</small></div>

<h2 id="what-is-next-translate">What is Next-translate?</h2>
<p><a href="https://github.com/vinissimus/next-translate">Next-translate</a> is a library to keep the translations as simple as possible in a Next.js environment. It arose from the need in <a href="https://www.vinissimus.com">Vinissimus</a> to reduce the bundle size when we realized that the <a href="https://github.com/isaachinman/next-i18next">next-i18next</a> library we used occupied 20 times more than Preact. We decided to create our own library with clear goals. In addition, we took advantage of this to support SSG, since next-i18next required the translations to be loaded into a getInitialProps, sacrificing automatic page optimization.</p>
<h3 id="goals">Goals</h3>
<ul>
<li>Being a small i18n library (~1kb).</li>
<li>Cover the i18n basics: interpolation, plurals, Trans component, t function, nested translations, fallbacks...</li>
<li>Only load the necessary translations for each page and language. If you navigate to <code>/en/about</code>, just load the <code>about</code> namespace in English.</li>
<li>Support automatic page optimization (SSG).</li>
<li>Make it easy to integrate translations on pages.</li>
<li>Make it easy to migrate to future changes in the Next.js core.</li>
</ul>
<h2 id="what-does-version-10-provide">What does version 1.0 provide?</h2>
<h3 id="nextjs-plugin">Next.js plugin</h3>
<p>Last year, to achieve the goals of the previous point, we had to create a workaround by doing a &quot;build step&quot; to generate the static pages with all the languages. We had to work in a different directory than &quot;pages&quot;. It worked, but it was a bit uncomfortable. Today, in version 1.0, we have been able to remove this workaround while maintaining all the goals.</p>
<p>Now, the Next.js plugin is the new toy. It is responsible for loading the necessary translations on each page through a webpack loader. This way, you don&#39;t have to write on each page a getStaticProps, getServerSideProps or any other method you want to use to load the translations. The plugin will take care of it by overwriting the method you have or using a default one (getStaticProps).</p>
<a href="https://aralroca.com/images/blog-images/example-next-translate-plugin.png">
  <figure align="center">
    <img class="center" src="https://aralroca.com/images/blog-images/example-next-translate-plugin.png" alt="Labelai logo" />
    <figcaption><small>Working with Next-translate 1.0</small></figcaption>
  </figure>
</a>

<p>The plugin is needed to cover the last two goals mentioned in the previous point:</p>
<ul>
<li>Make it easy to integrate translations on pages.</li>
<li>Make it easy to migrate to future changes in the Next.js core.</li>
</ul>
<blockquote>
<p><em>If you don&#39;t want the plugin to inject the webpack loader so you can have control over how to load the namespaces on each page, you can use the <code>loader=false</code> in the configuration, and then manually load the namespaces with <a href="https://github.com/vinissimus/next-translate/tree/1.0.0#loadnamespaces">loadNamespaces</a>.</em></p>
</blockquote>
<h3 id="improve-plurals-support">Improve plurals support</h3>
<p>In version <code>0.x</code> the support of plurals was quite simple. Now with <code>1.0</code> we&#39;ve <a href="https://github.com/vinissimus/next-translate/tree/1.0.0#5-plurals">improved the support</a> by adding 6 plural forms (taken from <a href="http://cldr.unicode.org/index/cldr-spec/plural-rules">CLDR Plurals page</a>):</p>
<ul>
<li><code>zero</code></li>
<li><code>one</code> (singular)</li>
<li><code>two</code> (dual)</li>
<li><code>few</code> (paucal)</li>
<li><code>many</code> (also used for fractions if they have a separate class)</li>
<li><code>other</code> (required—general plural form—also used if the language only has a single form)</li>
</ul>
<h3 id="consume-translations-outside-pages--components">Consume translations outside pages / components</h3>
<p>We add the <a href="https://github.com/vinissimus/next-translate/tree/1.0.0#gett">getT</a> asynchronous function to load the <code>t</code> function outside components / pages. It works on both server-side and client-side.</p>
<p>Unlike the useTranslation hook, we can use here any namespace. It doesn&#39;t have to be a namespace defined in the &quot;pages&quot; configuration. It will <strong>download the namespace</strong> indicated as a parameter <strong>on runtime</strong>.</p>
<p>Example inside <code>getStaticProps</code>:</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">import</span> getT <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;next-translate/getT&#x27;</span>
<span class="hljs-comment">// ...</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">getStaticProps</span>(<span class="hljs-params">{ locale }</span>) {
  <span class="hljs-keyword">const</span> t = <span class="hljs-keyword">await</span> <span class="hljs-title function_">getT</span>(locale, <span class="hljs-string">&#x27;common&#x27;</span>)
  <span class="hljs-keyword">const</span> title = <span class="hljs-title function_">t</span>(<span class="hljs-string">&#x27;title&#x27;</span>)
  <span class="hljs-keyword">return</span> { <span class="hljs-attr">props</span>: { title } }
}
</code></pre><p>Example inside API Route, ex: <code>/fr/api/example</code>:</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">import</span> getT <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;next-translate/getT&#x27;</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">async</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">handler</span>(<span class="hljs-params">req, res</span>) {
  <span class="hljs-keyword">const</span> t = <span class="hljs-keyword">await</span> <span class="hljs-title function_">getT</span>(req.<span class="hljs-property">query</span>.<span class="hljs-property">__nextLocale</span>, <span class="hljs-string">&#x27;common&#x27;</span>)
  <span class="hljs-keyword">const</span> title = <span class="hljs-title function_">t</span>(<span class="hljs-string">&#x27;title&#x27;</span>)

  res.<span class="hljs-property">statusCode</span> = <span class="hljs-number">200</span>
  res.<span class="hljs-title function_">setHeader</span>(<span class="hljs-string">&#x27;Content-Type&#x27;</span>, <span class="hljs-string">&#x27;application/json&#x27;</span>)
  res.<span class="hljs-title function_">end</span>(<span class="hljs-title class_">JSON</span>.<span class="hljs-title function_">stringify</span>({ title }))
}
</code></pre><h2 id="useful-links">Useful links</h2>
<ul>
<li><a href="https://github.com/vinissimus/next-translate/tree/1.0.0#2-getting-started">How to start with Next-translate 1.0</a></li>
<li><a href="https://github.com/vinissimus/next-translate/blob/1.0.0/docs/migration-guide-1.0.0.md">Migration guide 0.x to 1.0</a></li>
<li><a href="https://github.com/vinissimus/next-translate/releases/tag/1.0.0">Release 1.0 notes</a></li>
<li><a href="https://github.com/vinissimus/next-translate/tree/1.0.0/examples">Examples with Next-translate 1.0</a></li>
</ul>
<h2 id="contributors">Contributors</h2>
<p>During 2020, +20 people contributed to the Next-translate codebase, implementing new features, fixing bugs and issues, writing documentation, and so on. The Vinissimus team would like to thank all of you who helped us build Next-translate to become what it is today.</p>
<p><a href="https://github.com/vincentducorps">@vincentducorps</a>, <a href="https://github.com/giovannigiordano">@giovannigiordano</a>, <a href="https://github.com/dnepro">@dnepro</a>,
<a href="https://github.com/BjoernRave">@BjoernRave</a>, <a href="https://github.com/croutonn">@croutonn</a>, <a href="https://github.com/justincy">@justincy</a>, <a href="https://github.com/YannSuissa">@YannSuissa</a>, <a href="https://github.com/thanhlmm">@thanhlmm</a>, <a href="https://github.com/stpch">@stpch</a>, <a href="https://github.com/shunkakinoki">@shunkakinoki</a>, <a href="https://github.com/rekomat">@rekomat</a>, <a href="https://github.com/psanlorenzo">@psanlorenzo</a>, <a href="https://github.com/pgrimaud">@pgrimaud</a>, <a href="https://github.com/lone-cloud">@lone-cloud</a>, <a href="https://github.com/kidnapkin">@kidnapkin</a>, <a href="https://github.com/hibearpanda">@hibearpanda</a>, <a href="https://github.com/ftonato">@ftonato</a>, <a href="https://github.com/dhobbs">@dhobbs</a>, <a href="https://github.com/bickmaev5">@bickmaev5</a>, <a href="https://github.com/Faulik">@Faulik</a>, <a href="https://github.com/josephfarina">@josephfarina</a>, <a href="https://github.com/gurkerl83">@gurkerl83</a>, <a href="https://github.com/aralroca">@aralroca</a></p>
]]></content:encoded>
            </item>
            <item>
              <title>Next-translate 3.0.0 - Turbopack, Next.js 16, and a New Chapter</title>
              <description>Today, I am incredibly happy to announce that Next-translate version 3.0.0 is officially out! This release is focused on Modernity and Stability, including Turbopack support and compatibility with Next.js 15+ async params.</description>
              <link>https://aralroca.com/blog/next-translate-3-0</link>
              <guid isPermaLink="false">https://aralroca.com/blog/next-translate-3-0/</guid>
              <pubDate>Sun Mar 01 2026 00:00:00 GMT+0000 (Coordinated Universal Time)</pubDate>
              <content:encoded><![CDATA[<p>It has been quite a journey since the last major update of <strong>Next-translate</strong>. Today, I am incredibly happy to announce that version <strong>3.0.0</strong> is officially out!</p>
<h2 id="a-personal-note">A Personal Note</h2>
<p>Before diving into the technical details, I want to address the elephant in the room: the lack of updates over the past year. I’ve been deeply involved in other projects and, unfortunately, Next-translate didn&#39;t get the attention it deserved.</p>
<p>I want to apologize for the wait. The community support has been overwhelming, and seeing so many of you still rely on this tool made me realize I need to give it my priority again. Starting today, I’m back to a constant maintenance rhythm. Next-translate is a core part of my focus once more.</p>
<h2 id="whats-new-in-300">What&#39;s New in 3.0.0?</h2>
<p>This release is focused on two things: <strong>Modernity</strong> and <strong>Stability</strong>.</p>
<h3 id="1-turbopack-support-finally">1. Turbopack Support (Finally!)</h3>
<p>One of the most requested features was compatibility with <strong>Turbopack</strong>. With Next.js 16 pushing for better build performance, we&#39;ve updated <code>next-translate-plugin</code> to play nice with the Rust-based bundler. You get the speed of Turbopack without losing your localized content.</p>
<h3 id="2-nextjs-15-async-params">2. Next.js 15+ Async Params</h3>
<p>Next.js 15 introduced a significant change: <code>params</code> and <code>searchParams</code> are now asynchronous. Version 3.0.0 handles this natively, ensuring your translations work perfectly in both Server and Client components without the dreaded &quot;params is not a promise&quot; errors.</p>
<h3 id="3-stabilizing-the-app-router">3. Stabilizing the App Router</h3>
<p>We&#39;ve introduced a new <code>createTranslation</code> helper specifically designed for the <code>app</code> directory architecture. We&#39;ve also stabilized the internal <code>t</code> function to prevent unnecessary re-renders and improved the overall DX for server-side translation.</p>
<h3 id="4-better-components-and-typings">4. Better Components and Typings</h3>
<ul>
<li><strong>Trans Component</strong>: Now supports nested arrays and improved pluralization.</li>
<li><strong>formatElements</strong>: Now explicitly exported for your own custom formatting logic.</li>
<li><strong>TypeScript 5.3+</strong>: Full support for the latest TS features and significantly improved auto-completion for your namespaces.</li>
</ul>
<h2 id="breaking-changes-to-watch-out-for">Breaking Changes to Watch Out For</h2>
<p>To keep the project lean and compatible with the future of Next.js, we&#39;ve made a few adjustments:</p>
<ul>
<li>Support for the automatic migration logic from <code>pages</code> to <code>app</code> router has been removed from the plugin.</li>
<li>We now use the <code>exports</code> field in <code>package.json</code>, so make sure you use formal entry points like <code>next-translate/useTranslation</code> instead of internal paths.</li>
</ul>
<h2 id="moving-forward">Moving Forward</h2>
<p>This is just the beginning of the 3.0.x cycle. My goal is to keep Next-translate as the lightweight, powerful alternative for i18n in Next.js.</p>
<p>Thank you to everyone who contributed to this release. Let&#39;s build a more global web together!</p>
<hr>
<p>Check out the code on <a href="https://github.com/aralroca/next-translate">GitHub</a>.</p>
]]></content:encoded>
            </item>
            <item>
              <title>Next-translate released 🎉</title>
              <description>i18n for Next.js static pages</description>
              <link>https://aralroca.com/blog/next-translate-released</link>
              <guid isPermaLink="false">https://aralroca.com/blog/next-translate-released/</guid>
              <pubDate>Wed Dec 11 2019 00:00:00 GMT+0000 (Coordinated Universal Time)</pubDate>
              <content:encoded><![CDATA[<p>After having some trouble with <code>next-i18next</code> library to internationalize pages, we decided to create an alternative library! With some differences:</p>
<p>📦 Tiny (~1kb)
⚡️ It also works for static-sites (with no server)</p>
<p>We just released it today 🎉</p>
<p>The result after replacing <code>next-i18next</code> to <code>next-translate</code> in our project (with a custom server):</p>
<p>Before:</p>
<p><img src="https://aralroca.com/images/blog-images/1.jpeg" alt="Before"></p>
<p>After:</p>
<p><img src="https://aralroca.com/images/blog-images/2.jpeg" alt="After"></p>
<p>714 B !! 🤯</p>
<p>Please, help us with a star on GitHub repo (or contribute, PRs are welcome 😊</p>
<p>👉 <a href="https://github.com/vinissimus/next-translate">https://github.com/vinissimus/next-translate</a></p>
]]></content:encoded>
            </item>
            <item>
              <title>I Built a 100% Private, On-Device AI Audio Stem Splitter (No Servers!)</title>
              <description>How I implemented a browser-based AI tool to split songs into vocals, drums, and bass without uploading a single byte to any server.</description>
              <link>https://aralroca.com/blog/on-device-ai-stem-splitter</link>
              <guid isPermaLink="false">https://aralroca.com/blog/on-device-ai-stem-splitter/</guid>
              <pubDate>Tue Mar 17 2026 00:00:00 GMT+0000 (Coordinated Universal Time)</pubDate>
              <content:encoded><![CDATA[<p>Most &quot;AI&quot; tools these days are just wrappers around an API. You upload your
file, wait for a server to process it, and hope your data isn&#39;t being used to
train the next big model.</p>
<p>When I decided to add an <strong>Audio Stem Splitter</strong> to
<a href="https://kitmul.com/en/music/audio-stem-splitter">Kitmul</a>, I had one
non-negotiable rule: <strong>Zero server uploads</strong>.</p>
<p>The result is a tool that can take any song and split it into <strong>Vocals, Drums,
Bass, and Instruments</strong> entirely within your browser.</p>
<img class="center" alt="Stems" src="https://aralroca.com/images/blog-images/stems.png">

<h2 id="the-problem-with-traditional-audio-splitting">The Problem with Traditional Audio Splitting</h2>
<p>If you&#39;ve ever used tools like PhonicMind or LALAL.AI, you know the drill:</p>
<ol>
<li>Upload your MP3.</li>
<li>Wait in a queue.</li>
<li>Pay for &quot;credits&quot; or high-quality downloads.</li>
<li>Your file sits on someone else&#39;s server.</li>
</ol>
<p>For musicians, producers, or just karaoke fans, this is slow and
privacy-invasive. I wanted to see if we could bring the power of models like
<a href="https://huggingface.co/smank/htdemucs-onnx/resolve/main/htdemucs.onnx"><strong>Demucs</strong></a>
directly to the user&#39;s hardware using <strong>WebAssembly</strong> and <strong>Web Workers</strong>.</p>
<h2 id="how-it-works-ai-in-the-browser">How it Works: AI in the Browser</h2>
<p>The magic happens thanks to a few modern web technologies:</p>
<ol>
<li><strong>WebAssembly (WASM):</strong> We run the heavy lifting—the actual neural network
inference—using a specialized AI model optimized for the browser.</li>
<li><strong>Web Workers:</strong> Splitting audio is CPU-intensive. By offloading the process
to a background thread, the UI remains snappy. You can still navigate the
site while the &quot;AI chef&quot; is in the kitchen.</li>
<li><strong>Local Processing:</strong> When you drag a file into the splitter, the browser
reads the raw bytes, processes them locally, and generates the stems. <strong>Your
audio never leaves your computer.</strong></li>
</ol>
<img class="center" alt="Stem Splitter process" src="https://aralroca.com/images/blog-images/kitmul-stem-splitter.png">

<h2 id="why-use-an-on-device-splitter">Why Use an On-Device Splitter?</h2>
<ul>
<li><strong>Privacy First:</strong> Your unfinished demos or private recordings stay private.</li>
<li><strong>No Subscriptions:</strong> Since it uses <em>your</em> CPU/GPU, there&#39;s no server cost for
me to pass on to you. It&#39;s free.</li>
<li><strong>High Fidelity:</strong> We export the results in high-quality <strong>WAV</strong> format, not
compressed MP3s.</li>
<li><strong>No Limits:</strong> Split as many songs as you want without worrying about &quot;minutes
remaining.&quot;</li>
</ul>
<h2 id="beyond-karaoke-practical-use-cases">Beyond Karaoke: Practical Use Cases</h2>
<p>While removing vocals for karaoke is the most obvious use, I&#39;ve seen some great
creative ways to use it:</p>
<ul>
<li><strong>Sampling for Producers:</strong> Isolate a clean drum break or a bassline for your
own tracks.</li>
<li><strong>Instrument Practice:</strong> Remove the guitar track so you can be the lead
guitarist for your favorite band.</li>
<li><strong>Mixing Reference:</strong> Listen only to the vocal harmonies to study how a
professional track was layered.</li>
</ul>
<h2 id="try-it-out">Try it Out</h2>
<p>The <strong>Audio Stem Splitter</strong> is now live on Kitmul. It&#39;s best used on desktop
(Chrome or Edge handle the AI models particularly well).</p>
<img class="center" alt="Kitmul processing" src="https://aralroca.com/images/blog-images/kitmul-processing.png">

<p><strong><a href="https://kitmul.com/en/music/audio-stem-splitter">👉 Try the Audio Stem Splitter on Kitmul</a></strong></p>
<p>I&#39;m constantly adding more tools to Kitmul (we&#39;re at over 150 now!), but this
one feels special because it pushes the boundaries of what the browser can do
without relying on the cloud.</p>
<p>If you are a developer interested in on-device AI or a musician looking for a
private way to split tracks, let me know what you think in the comments!</p>
]]></content:encoded>
            </item>
            <item>
              <title>One PR to a parser unlocked prerendering in Brisa</title>
              <description>How contributing import attributes support to Meriyah enabled Brisa's entire prerender pipeline; and why understanding ASTs matters for framework authors.</description>
              <link>https://aralroca.com/blog/one-pr-unlocked-prerendering-brisa</link>
              <guid isPermaLink="false">https://aralroca.com/blog/one-pr-unlocked-prerendering-brisa/</guid>
              <pubDate>Wed Apr 22 2026 00:00:00 GMT+0000 (Coordinated Universal Time)</pubDate>
              <content:encoded><![CDATA[<p>I built a JavaScript framework called <a href="https://brisa.build">Brisa</a>. The kind of framework that needs to parse every single source file your app contains; analyze imports, detect server vs. client components, inject macros, transform JSX. All of that happens at the AST level.</p>
<p>Before Brisa, I was already maintaining <a href="https://github.com/aralroca/next-translate">next-translate</a>, an i18n library for Next.js. For the plugin that auto-injects locale loaders into pages, I used the TypeScript compiler API. It worked. It was also painfully slow; <code>ts.createProgram()</code> for every page file at build time, full type-checker instantiation, lib resolution. We had to add <code>noResolve: true</code> and <code>noLib: true</code> just to make it bearable. The parser was doing ten times more work than we needed because all we wanted was the AST, not the types.</p>
<p>When I started building Brisa, I knew I needed something faster. Something that gave me an ESTree-compliant AST without the overhead of a full compiler. That&#39;s how I found <a href="https://github.com/meriyah/meriyah">Meriyah</a>.</p>
<h2 id="why-i-chose-meriyah-over-everything-else">Why I chose Meriyah over everything else</h2>
<p>Meriyah is written entirely in JavaScript. No native bindings. No WASM loading step. No compilation step. Just <code>parseScript(code, { jsx: true, module: true, next: true })</code> and you get back an <a href="https://github.com/estree/estree">ESTree</a> AST in microseconds.</p>
<p>For Brisa&#39;s build pipeline, that speed difference compounds. Every source file in a Brisa project passes through Meriyah. The parser runs inside <code>AST().parseCodeToAST()</code>, which first transpiles via Bun&#39;s transpiler and then feeds the result to Meriyah. The output is a standard ESTree <code>Program</code> node that I can traverse, modify, and regenerate with <a href="https://github.com/davidbonnet/astring">astring</a>.</p>
<p>But here&#39;s where it got interesting. Brisa has a feature called <a href="https://brisa.build/api-reference/extended-props/renderOn"><code>renderOn</code></a> that lets you prerender components at build time. You write this in your page:</p>
<pre><code class="hljs language-tsx">&lt;<span class="hljs-title class_">SomeComponent</span> renderOn=<span class="hljs-string">&quot;build&quot;</span> foo=<span class="hljs-string">&quot;bar&quot;</span> /&gt;
</code></pre><p>And at build time, the AST transform detects <code>renderOn=&quot;build&quot;</code>, replaces the JSX with a <code>__prerender__macro()</code> call, and injects this import at the top of the file:</p>
<pre><code class="hljs language-javascript"><span class="hljs-keyword">import</span> { __prerender__macro } <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;brisa/macros&#x27;</span> <span class="hljs-keyword">with</span> { <span class="hljs-attr">type</span>: <span class="hljs-string">&#x27;macro&#x27;</span> };
</code></pre><p>That <code>with { type: &#39;macro&#39; }</code> is an <a href="https://github.com/tc39/proposal-import-attributes">import attribute</a> that tells <a href="https://bun.sh/docs/bundler/macros">Bun&#39;s bundler</a> to resolve the import at compile time. The component gets rendered during the build, and the result is injected as static HTML. The user writes <code>renderOn=&quot;build&quot;</code>, but under the hood the framework constructs <code>ImportDeclaration</code> and <code>ImportAttribute</code> AST nodes by hand and regenerates the code.</p>
<p>The problem: Meriyah didn&#39;t support <a href="https://github.com/meriyah/meriyah/pull/280">import attributes</a> when I started using it. So I contributed a PR to add the feature. That PR landed, and Brisa&#39;s entire prerender pipeline could work end to end.</p>
<p>Going from &quot;the parser can&#39;t handle my syntax&quot; to &quot;I&#39;ll fix the parser itself&quot; is the kind of thing that only happens when you understand ASTs well enough to read the parser&#39;s source code and see what&#39;s missing.</p>
<h2 id="the-inspiration">The inspiration</h2>
<p><a href="https://astexplorer.net/">AST Explorer</a> exists, and it&#39;s great. I use it regularly. It&#39;s the reference tool for exploring ASTs. I wanted to build something similar as part of <a href="https://kitmul.com">Kitmul</a>; my own version of an AST visualizer with parser selection, interactive tree view, and support for the parsers I use daily.</p>
<p>The <a href="https://kitmul.com/en/visualizers-logic/ast-visualizer">AST Visualizer</a> does exactly this. Paste JavaScript, pick your parser (Acorn, Meriyah, or SWC), and get an interactive tree or raw JSON. Everything runs locally in your browser.</p>
<p><img src="https://kitmul.com/images/blog/ast-visualizer-tool-screenshot.webp" alt="The AST Visualizer showing a JavaScript function parsed with Acorn into an interactive tree view; Monaco editor on the left, collapsible AST nodes on the right, with parser and view mode selectors in the toolbar"></p>
<p>The parser choice matters because each one produces a slightly different AST:</p>
<ul>
<li><strong><a href="https://github.com/acornjs/acorn">Acorn</a></strong> follows the ESTree spec strictly. It&#39;s the parser that <a href="https://eslint.org/">ESLint</a> uses internally. If you&#39;re writing ESLint rules, this is the tree your rule will traverse.</li>
<li><strong><a href="https://github.com/meriyah/meriyah">Meriyah</a></strong> also follows ESTree, but adds JSX support and bleeding-edge features via the <code>next: true</code> flag. It&#39;s the parser I chose for Brisa because it&#39;s fast, lightweight, and written in pure JS.</li>
<li><strong><a href="https://swc.rs/">SWC</a></strong> is a Rust-based compiler that runs via WASM in the browser. Its AST uses a different structure; <code>Module</code> instead of <code>Program</code>, <code>span</code> objects instead of <code>start</code>/<code>end</code> positions. If you&#39;re working with Next.js or Turbopack internals, this is the AST you&#39;re dealing with.</li>
</ul>
<p>Switching between parsers and seeing how the same code produces different trees is the fastest way to understand their trade-offs.</p>
<h2 id="three-things-the-tree-teaches-you-that-docs-dont">Three things the tree teaches you that docs don&#39;t</h2>
<p><img src="https://kitmul.com/images/blog/ast-visualizer-nexttranslate.webp" alt="next-translate bundle size comparison; the i18n library for Next.js where I first dealt with AST parsing via the TypeScript compiler API"></p>
<p><strong>1. Expressions vs. statements are visible.</strong></p>
<p>Every JavaScript developer hears &quot;expression vs. statement&quot; at some point. Few can articulate the difference until they see it in a tree. Consider:</p>
<pre><code class="hljs language-javascript">x = <span class="hljs-number">5</span>;
</code></pre><p>The AST shows an <code>ExpressionStatement</code> wrapping an <code>AssignmentExpression</code>. The expression is the <code>x = 5</code> part. The statement is the semicolon-terminated wrapper that makes it a standalone line. This distinction is why <code>if (x = 5)</code> is legal JavaScript; the assignment is an expression that returns a value.</p>
<p><strong>2. Operator precedence is tree depth.</strong></p>
<pre><code class="hljs language-javascript"><span class="hljs-number">2</span> + <span class="hljs-number">3</span> * <span class="hljs-number">4</span>
</code></pre><pre><code>BinaryExpression (operator: &quot;+&quot;)
├─ left: Literal (2)
└─ right: BinaryExpression (operator: &quot;*&quot;)
           ├─ left: Literal (3)
           └─ right: Literal (4)
</code></pre><p>The multiplication is <em>nested inside</em> the addition&#39;s right operand. That&#39;s not a formatting choice; that&#39;s the parser encoding precedence into structure. The deeper node evaluates first. Parentheses change the tree structure, not some invisible priority flag.</p>
<p><strong>3. Import attributes reveal how <code>renderOn=&quot;build&quot;</code> works.</strong></p>
<p>Parse this with Meriyah:</p>
<pre><code class="hljs language-javascript"><span class="hljs-keyword">import</span> { __prerender__macro } <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;brisa/macros&#x27;</span> <span class="hljs-keyword">with</span> { <span class="hljs-attr">type</span>: <span class="hljs-string">&#x27;macro&#x27;</span> };
</code></pre><p>The <code>ImportDeclaration</code> node gets an <code>attributes</code> array containing <code>ImportAttribute</code> nodes. Each attribute has a <code>key</code> and a <code>value</code>, both <code>Literal</code> nodes. This is the import that Brisa&#39;s build pipeline injects when it finds <code>renderOn=&quot;build&quot;</code> on a component. The <code>with { type: &#39;macro&#39; }</code> tells Bun to resolve the function at compile time. Without seeing the tree, you&#39;d never guess that <code>with { type: &#39;macro&#39; }</code> becomes a nested array of attribute objects.</p>
<h2 id="comparing-parsers-side-by-side">Comparing parsers side by side</h2>
<table>
<thead>
<tr>
<th>Feature</th>
<th>Acorn</th>
<th>Meriyah</th>
<th>SWC</th>
</tr>
</thead>
<tbody><tr>
<td>Language</td>
<td>JavaScript</td>
<td>JavaScript</td>
<td>Rust (WASM in browser)</td>
</tr>
<tr>
<td>Spec</td>
<td>ESTree</td>
<td>ESTree</td>
<td>SWC AST</td>
</tr>
<tr>
<td>JSX support</td>
<td>No</td>
<td>Yes</td>
<td>Yes</td>
</tr>
<tr>
<td>Import attributes</td>
<td>No</td>
<td>Yes</td>
<td>Yes</td>
</tr>
<tr>
<td>Speed</td>
<td>Fast</td>
<td>Very fast</td>
<td>Fast (after WASM load)</td>
</tr>
<tr>
<td>Bundle size</td>
<td>~120KB</td>
<td>~320KB</td>
<td>~14MB (WASM)</td>
</tr>
<tr>
<td>Used by</td>
<td>ESLint</td>
<td>Brisa</td>
<td>Next.js, Turbopack</td>
</tr>
</tbody></table>
<p>The point isn&#39;t that one parser is better. Each one has trade-offs. Acorn is the standard. Meriyah is the fast, feature-rich option. SWC is the heavyweight that handles everything but requires loading 14MB of WASM. The <a href="https://kitmul.com/en/visualizers-logic/ast-visualizer">AST Visualizer</a> lets you switch between all three and see the differences.</p>
<h2 id="real-use-cases-from-building-frameworks">Real use cases from building frameworks</h2>
<p>I keep hearing &quot;ASTs are for compiler people.&quot; No. ASTs are for anyone who writes tools that operate on code. Here&#39;s where I&#39;ve actually used AST knowledge:</p>
<p><strong>Framework build pipelines.</strong> In <a href="https://brisa.build">Brisa</a>, every source file is parsed to an AST, analyzed for imports, transformed (macro injection, server/client separation, i18n processing), and regenerated as code. The central function is <code>AST(&#39;tsx&#39;).parseCodeToAST(code)</code>, which returns an ESTree <code>Program</code> node. Without understanding the tree, I couldn&#39;t write a single one of those transforms.</p>
<p><strong>Prerender macro injection via <code>renderOn=&quot;build&quot;</code>.</strong> When Brisa encounters <code>&lt;Foo renderOn=&quot;build&quot; /&gt;</code>, the AST transform constructs <code>ImportAttribute</code> nodes by hand to inject <code>import {__prerender__macro} from &#39;brisa/macros&#39; with { type: &quot;macro&quot; }</code>. There&#39;s a quirk: Meriyah uses <code>value</code> on Literal nodes where astring expects <code>name</code>. That&#39;s an actual comment in the <a href="https://github.com/brisa-build/brisa">Brisa source code</a>: <code>// This astring is looking for &quot;name&quot;, but meriyah &quot;value&quot;</code>. You only discover that kind of thing by staring at trees.</p>
<p><strong>i18n loader injection.</strong> In <a href="https://github.com/aralroca/next-translate-plugin">next-translate-plugin</a>, the Webpack loader uses <code>ts.createProgram()</code> to parse each page and detect its exports. It needs to know whether the page has <code>getStaticProps</code>, <code>getServerSideProps</code>, or a default export, so it can inject the right locale loader. The TypeScript AST uses <code>SyntaxKind</code> enums instead of string-based types, which is a different mental model from ESTree. Seeing both trees side by side clarifies the difference instantly.</p>
<p><strong>Import path resolution.</strong> Brisa resolves relative imports to absolute paths during the build. The AST transform walks every <code>ImportDeclaration</code>, reads its <code>source.value</code>, resolves it against the file&#39;s directory, and replaces it. This is pure AST surgery; no regex, no string splitting.</p>
<h2 id="privacy">Privacy</h2>
<p>All three parsers run entirely in your browser. Acorn and Meriyah are JavaScript libraries that execute client-side. SWC loads a WASM binary from a local file. No code is transmitted to any server. No analytics track what you paste. If you&#39;re parsing proprietary source code, nothing leaves your device.</p>
<h2 id="the-actual-takeaway">The actual takeaway</h2>
<p>ASTs aren&#39;t magic. They&#39;re trees. Every piece of code you&#39;ve ever written has a tree representation that a parser produces in milliseconds. The gap between &quot;I&#39;ve heard of ASTs&quot; and &quot;I can build a framework&#39;s compiler pipeline&quot; is mostly about seeing enough trees that the patterns become obvious.</p>
<p>I went from struggling with the TypeScript compiler API in next-translate to contributing parser features to Meriyah for Brisa. The turning point wasn&#39;t reading more documentation. It was seeing enough ASTs that the node types became second nature.</p>
<p>The <a href="https://kitmul.com/en/visualizers-logic/ast-visualizer">AST Visualizer</a> won&#39;t teach you compiler theory. It&#39;ll teach you what the parser sees when it reads your code. For writing framework internals, build tools, codemods, and ESLint rules, that&#39;s the only thing that matters.</p>
<hr>
<p><em>The AST Visualizer is free, private, and runs entirely in your browser. No signup, no install, no data leaves your device. Part of the <a href="https://kitmul.com/en/visualizers-logic">Visualizers &amp; Logic Tools</a> collection on Kitmul.</em></p>
]]></content:encoded>
            </item>
            <item>
              <title>OpenCV directly in the browser (webassembly + webworker)</title>
              <description>Learn how to use OpenCV in the web without a lot of headaches.</description>
              <link>https://aralroca.com/blog/opencv-in-the-web</link>
              <guid isPermaLink="false">https://aralroca.com/blog/opencv-in-the-web/</guid>
              <pubDate>Tue May 05 2020 00:00:00 GMT+0000 (Coordinated Universal Time)</pubDate>
              <content:encoded><![CDATA[<p>We&#39;ll see how to use the OpenCV library directly on the browser! To do this, we will compile OpenCV to webassembly and then run it inside a webworker.</p>
<h2 id="what-is-opencv">What is OpenCV</h2>
<p>OpenCV is the most popular library of Computer Vision, and has existed since 1999! What it does is providing a user-friendly and highly efficient development environment. It is a library written in C and C++ by Intel.</p>
<p>OpenCV can also use Intel&#39;s embedded performance primitives, a set of low-level routines specific of Intel.</p>
<p>With OpenCV you can develop things like:</p>
<ul>
<li>2D and 3D feature toolkits</li>
<li>Egomotion estimation</li>
<li>Facial recognition system</li>
<li>Gesture recognition</li>
<li>Human–computer interaction (HCI)</li>
<li>Mobile robotics</li>
<li>Motion understanding</li>
<li>Object identification</li>
<li>Segmentation and recognition</li>
<li>Stereopsis stereo vision: depth perception from 2 cameras</li>
<li>Structure from motion (SFM)</li>
<li>Motion tracking</li>
<li>Augmented reality</li>
</ul>
<br />
<img class="center" src="https://aralroca.com/images/blog-images/30.png" alt="OpenCV logo" />
<br />

<h2 id="why-in-the-browser">Why in the browser</h2>
<p>Being able to run computer vision algorithms directly from the browser allows us to move costs to the client device, and thus save many costs on the server.</p>
<p>Imagine you want to get the characteristics of a label of wine from a picture. There are many ways to do this. If we look for the most ergonomic way for our server, we&#39;d move part of the wine label detection logic in the browser. Then, when we fetch the request to the server, we&#39;ll only need to send the final vector. This way, we avoid processing the image on the server.</p>
<p>Or even if it&#39;s an embedded app for the private use of a company, we could put all the logic in the browser.</p>
<h2 id="starting-a-new-nextjs-project">Starting a new Next.js project</h2>
<p>We&#39;re going to use the Next.js framework with React, to ease the setup and use of the project. However, the same can be applied to a project with Angular, Vue.js, Svelte... or vanilla.js.</p>
<p>To start with, let&#39;s just create a new Next.js project with the following command:</p>
<pre><code>yarn create next-app
</code></pre><p>Once you fill in the name of your project, raise the local environment with <code>yarn dev</code>. Now we are ready to start using OpenCV in our Next.js project.</p>
<h2 id="compile-opencv-into-webassembly">Compile OpenCV into Webassembly</h2>
<p>To compile OpenCV to webassembly we can follow the official documentation at:</p>
<ul>
<li><a href="https://docs.opencv.org/3.4.10/d4/da1/tutorial_js_setup.html">https://docs.opencv.org/3.4.10/d4/da1/tutorial_js_setup.html</a></li>
</ul>
<p>However, I&#39;ll tell you the steps I&#39;ve taken:</p>
<p>First clone the OpenCV repo:</p>
<pre><code class="hljs language-bh">git clone https://github.com/opencv/opencv.git
</code></pre><p>Now, once inside the repo directory we&#39;ve cloned, let&#39;s compile with Docker!</p>
<p>For Linux / Mac:</p>
<pre><code class="hljs language-bh">docker run --rm --workdir /code -v &quot;$PWD&quot;:/code &quot;trzeci/emscripten:latest&quot; python ./platforms/js/build_js.py build
</code></pre><p>For Windows:</p>
<pre><code class="hljs language-bh">docker run --rm --workdir /code -v &quot;$(get-location):/code&quot; &quot;trzeci/emscripten:latest&quot; python ./platforms/js/build_js.py build
</code></pre><p>Now it&#39;s time to wait... it may take about 15 minutes.</p>
<img class="center" alt="compiling OpenCV into webassembly" src="https://aralroca.com/images/blog-images/31.png">

<p>Once finished, copy the file you&#39;ve generated into the project and then move it into <code>/public</code>.</p>
<pre><code class="hljs language-diff">public
├── favicon.ico
├── js
<span class="hljs-addition">+│   ├── opencv.js</span>
└── vercel.svg
</code></pre><h2 id="loading-opencv-on-a-worker">Loading OpenCV on a Worker</h2>
<p>Once we have the OpenCV file in webassembly inside the <code>/public</code> directory, it&#39;s ready to use it inside a worker.</p>
<p>It is important to use a worker because all OpenCV functions are very expensive and would block the UI. It is not mandatory to use a worker, but highly recommended.</p>
<h3 id="creating-the-worker">Creating the worker</h3>
<p>Let&#39;s create the worker within the same <code>/public</code> directory.</p>
<pre><code class="hljs language-diff">public
├── favicon.ico
├── js
<span class="hljs-addition">+│   ├── cv.worker.js</span>
│   ├── opencv.js
└── vercel.svg
</code></pre><p>The initial content will be like this:</p>
<pre><code class="hljs language-js"><span class="hljs-comment">/**
 *  Here we will check from time to time if we can access the OpenCV
 *  functions. We will return in a callback if it&#x27;s been resolved
 *  well (true) or if there has been a timeout (false).
 */</span>
<span class="hljs-keyword">function</span> <span class="hljs-title function_">waitForOpencv</span>(<span class="hljs-params">callbackFn, waitTimeMs = <span class="hljs-number">30000</span>, stepTimeMs = <span class="hljs-number">100</span></span>) {
  <span class="hljs-keyword">if</span> (cv.<span class="hljs-property">Mat</span>) <span class="hljs-title function_">callbackFn</span>(<span class="hljs-literal">true</span>)

  <span class="hljs-keyword">let</span> timeSpentMs = <span class="hljs-number">0</span>
  <span class="hljs-keyword">const</span> interval = <span class="hljs-built_in">setInterval</span>(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> limitReached = timeSpentMs &gt; waitTimeMs
    <span class="hljs-keyword">if</span> (cv.<span class="hljs-property">Mat</span> || limitReached) {
      <span class="hljs-built_in">clearInterval</span>(interval)
      <span class="hljs-keyword">return</span> <span class="hljs-title function_">callbackFn</span>(!limitReached)
    } <span class="hljs-keyword">else</span> {
      timeSpentMs += stepTimeMs
    }
  }, stepTimeMs)
}

<span class="hljs-comment">/**
 * This exists to capture all the events that are thrown out of the worker
 * into the worker. Without this, there would be no communication possible
 * with the project.
 */</span>
onmessage = <span class="hljs-keyword">function</span> (<span class="hljs-params">e</span>) {
  <span class="hljs-keyword">switch</span> (e.<span class="hljs-property">data</span>.<span class="hljs-property">msg</span>) {
    <span class="hljs-keyword">case</span> <span class="hljs-string">&#x27;load&#x27;</span>: {
      <span class="hljs-comment">// Import Webassembly script</span>
      self.<span class="hljs-title function_">importScripts</span>(<span class="hljs-string">&#x27;./opencv.js&#x27;</span>)
      <span class="hljs-title function_">waitForOpencv</span>(<span class="hljs-keyword">function</span> (<span class="hljs-params">success</span>) {
        <span class="hljs-keyword">if</span> (success) <span class="hljs-title function_">postMessage</span>({ <span class="hljs-attr">msg</span>: e.<span class="hljs-property">data</span>.<span class="hljs-property">msg</span> })
        <span class="hljs-keyword">else</span> <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Error</span>(<span class="hljs-string">&#x27;Error on loading OpenCV&#x27;</span>)
      })
      <span class="hljs-keyword">break</span>
    }
    <span class="hljs-attr">default</span>:
      <span class="hljs-keyword">break</span>
  }
}
</code></pre><h3 id="loading-the-worker-in-our-project">Loading the worker in our project</h3>
<p>Okay, now we can create in our project a service that communicates with the worker. For this, we are going to create a <code>services</code> directory where we will put our file.</p>
<pre><code class="hljs language-diff">services
<span class="hljs-addition">+└── cv.js</span>
</code></pre><p>Once the file has been created, we will enter this initial code, which will allow us to load OpenCV into our project:</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">class</span> <span class="hljs-title class_">CV</span> {
  <span class="hljs-comment">/**
   * We will use this method privately to communicate with the worker and
   * return a promise with the result of the event. This way we can call
   * the worker asynchronously.
   */</span>
  <span class="hljs-title function_">_dispatch</span>(<span class="hljs-params">event</span>) {
    <span class="hljs-keyword">const</span> { msg } = event
    <span class="hljs-variable language_">this</span>.<span class="hljs-property">_status</span>[msg] = [<span class="hljs-string">&#x27;loading&#x27;</span>]
    <span class="hljs-variable language_">this</span>.<span class="hljs-property">worker</span>.<span class="hljs-title function_">postMessage</span>(event)
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Promise</span>(<span class="hljs-function">(<span class="hljs-params">res, rej</span>) =&gt;</span> {
      <span class="hljs-keyword">let</span> interval = <span class="hljs-built_in">setInterval</span>(<span class="hljs-function">() =&gt;</span> {
        <span class="hljs-keyword">const</span> status = <span class="hljs-variable language_">this</span>.<span class="hljs-property">_status</span>[msg]
        <span class="hljs-keyword">if</span> (status[<span class="hljs-number">0</span>] === <span class="hljs-string">&#x27;done&#x27;</span>) <span class="hljs-title function_">res</span>(status[<span class="hljs-number">1</span>])
        <span class="hljs-keyword">if</span> (status[<span class="hljs-number">0</span>] === <span class="hljs-string">&#x27;error&#x27;</span>) <span class="hljs-title function_">rej</span>(status[<span class="hljs-number">1</span>])
        <span class="hljs-keyword">if</span> (status[<span class="hljs-number">0</span>] !== <span class="hljs-string">&#x27;loading&#x27;</span>) {
          <span class="hljs-keyword">delete</span> <span class="hljs-variable language_">this</span>.<span class="hljs-property">_status</span>[msg]
          <span class="hljs-built_in">clearInterval</span>(interval)
        }
      }, <span class="hljs-number">50</span>)
    })
  }

  <span class="hljs-comment">/**
   * First, we will load the worker and capture the onmessage
   * and onerror events to always know the status of the event
   * we have triggered.
   *
   * Then, we are going to call the &#x27;load&#x27; event, as we&#x27;ve just
   * implemented it so that the worker can capture it.
   */</span>
  <span class="hljs-title function_">load</span>(<span class="hljs-params"></span>) {
    <span class="hljs-variable language_">this</span>.<span class="hljs-property">_status</span> = {}
    <span class="hljs-variable language_">this</span>.<span class="hljs-property">worker</span> = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Worker</span>(<span class="hljs-string">&#x27;/js/cv.worker.js&#x27;</span>) <span class="hljs-comment">// load worker</span>

    <span class="hljs-comment">// Capture events and save [status, event] inside the _status object</span>
    <span class="hljs-variable language_">this</span>.<span class="hljs-property">worker</span>.<span class="hljs-property">onmessage</span> = <span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> (<span class="hljs-variable language_">this</span>.<span class="hljs-property">_status</span>[e.<span class="hljs-property">data</span>.<span class="hljs-property">msg</span>] = [<span class="hljs-string">&#x27;done&#x27;</span>, e])
    <span class="hljs-variable language_">this</span>.<span class="hljs-property">worker</span>.<span class="hljs-property">onerror</span> = <span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> (<span class="hljs-variable language_">this</span>.<span class="hljs-property">_status</span>[e.<span class="hljs-property">data</span>.<span class="hljs-property">msg</span>] = [<span class="hljs-string">&#x27;error&#x27;</span>, e])
    <span class="hljs-keyword">return</span> <span class="hljs-variable language_">this</span>.<span class="hljs-title function_">_dispatch</span>({ <span class="hljs-attr">msg</span>: <span class="hljs-string">&#x27;load&#x27;</span> })
  }
}

<span class="hljs-comment">// Export the same instant everywhere</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">new</span> <span class="hljs-title function_">CV</span>()
</code></pre><h3 id="using-the-service">Using the service</h3>
<p>Since we are exporting the instance directly, we can import it into our page or component.</p>
<p>For example, we could load it on an <code>onClick</code> event:</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">async</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">onClick</span>(<span class="hljs-params"></span>) {
  <span class="hljs-keyword">await</span> cv.<span class="hljs-title function_">load</span>()
  <span class="hljs-comment">// Ready to use OpenCV on our component</span>
}
</code></pre><h2 id="using-opencv-in-the-browser">Using OpenCV in the browser</h2>
<p>Now that we have managed to load the OpenCV library in our browser we will see how to run some utilities from the library.</p>
<p>Of course you can do many things with OpenCV. Here I&#39;ll show a simple example. Then it will be your job to read the official documentation and learn how to use OpenCV.</p>
<p>The example we&#39;re going to use is a simple image processing, to take pictures with the camera and processing them to a grayscale. Although it may seem simple, this is our first &quot;hello world&quot; with OpenCV.</p>
<pre><code class="hljs language-jsx"><span class="hljs-keyword">import</span> { useEffect, useRef, useState } <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;react&#x27;</span>
<span class="hljs-keyword">import</span> cv <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;../services/cv&#x27;</span>

<span class="hljs-comment">// We&#x27;ll limit the processing size to 200px.</span>
<span class="hljs-keyword">const</span> maxVideoSize = <span class="hljs-number">200</span>

<span class="hljs-comment">/**
 * What we&#x27;re going to render is:
 *
 * 1. A video component so the user can see what&#x27;s on the camera.
 *
 * 2. A button to generate an image of the video, load OpenCV and
 * process the image.
 *
 * 3. A canvas to allow us to capture the image of the video and
 * show it to the user.
 */</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">Page</span>(<span class="hljs-params"></span>) {
  <span class="hljs-keyword">const</span> [processing, updateProcessing] = <span class="hljs-title function_">useState</span>(<span class="hljs-literal">false</span>)
  <span class="hljs-keyword">const</span> videoElement = <span class="hljs-title function_">useRef</span>(<span class="hljs-literal">null</span>)
  <span class="hljs-keyword">const</span> canvasEl = <span class="hljs-title function_">useRef</span>(<span class="hljs-literal">null</span>)

  <span class="hljs-comment">/**
   * In the onClick event we&#x27;ll capture a frame within
   * the video to pass it to our service.
   */</span>
  <span class="hljs-keyword">async</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">onClick</span>(<span class="hljs-params"></span>) {
    <span class="hljs-title function_">updateProcessing</span>(<span class="hljs-literal">true</span>)

    <span class="hljs-keyword">const</span> ctx = canvasEl.<span class="hljs-property">current</span>.<span class="hljs-title function_">getContext</span>(<span class="hljs-string">&#x27;2d&#x27;</span>)
    ctx.<span class="hljs-title function_">drawImage</span>(videoElement.<span class="hljs-property">current</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, maxVideoSize, maxVideoSize)
    <span class="hljs-keyword">const</span> image = ctx.<span class="hljs-title function_">getImageData</span>(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, maxVideoSize, maxVideoSize)
    <span class="hljs-comment">// Load the model</span>
    <span class="hljs-keyword">await</span> cv.<span class="hljs-title function_">load</span>()
    <span class="hljs-comment">// Processing image</span>
    <span class="hljs-keyword">const</span> processedImage = <span class="hljs-keyword">await</span> cv.<span class="hljs-title function_">imageProcessing</span>(image)
    <span class="hljs-comment">// Render the processed image to the canvas</span>
    ctx.<span class="hljs-title function_">putImageData</span>(processedImage.<span class="hljs-property">data</span>.<span class="hljs-property">payload</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>)
    <span class="hljs-title function_">updateProcessing</span>(<span class="hljs-literal">false</span>)
  }

  <span class="hljs-comment">/**
   * In the useEffect hook we&#x27;ll load the video
   * element to show what&#x27;s on camera.
   */</span>
  <span class="hljs-title function_">useEffect</span>(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">async</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">initCamara</span>(<span class="hljs-params"></span>) {
      videoElement.<span class="hljs-property">current</span>.<span class="hljs-property">width</span> = maxVideoSize
      videoElement.<span class="hljs-property">current</span>.<span class="hljs-property">height</span> = maxVideoSize

      <span class="hljs-keyword">if</span> (navigator.<span class="hljs-property">mediaDevices</span> &amp;&amp; navigator.<span class="hljs-property">mediaDevices</span>.<span class="hljs-property">getUserMedia</span>) {
        <span class="hljs-keyword">const</span> stream = <span class="hljs-keyword">await</span> navigator.<span class="hljs-property">mediaDevices</span>.<span class="hljs-title function_">getUserMedia</span>({
          <span class="hljs-attr">audio</span>: <span class="hljs-literal">false</span>,
          <span class="hljs-attr">video</span>: {
            <span class="hljs-attr">facingMode</span>: <span class="hljs-string">&#x27;user&#x27;</span>,
            <span class="hljs-attr">width</span>: maxVideoSize,
            <span class="hljs-attr">height</span>: maxVideoSize,
          },
        })
        videoElement.<span class="hljs-property">current</span>.<span class="hljs-property">srcObject</span> = stream

        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Promise</span>(<span class="hljs-function">(<span class="hljs-params">resolve</span>) =&gt;</span> {
          videoElement.<span class="hljs-property">current</span>.<span class="hljs-property">onloadedmetadata</span> = <span class="hljs-function">() =&gt;</span> {
            <span class="hljs-title function_">resolve</span>(videoElement.<span class="hljs-property">current</span>)
          }
        })
      }
      <span class="hljs-keyword">const</span> errorMessage =
        <span class="hljs-string">&#x27;This browser does not support video capture, or this device does not have a camera&#x27;</span>
      <span class="hljs-title function_">alert</span>(errorMessage)
      <span class="hljs-keyword">return</span> <span class="hljs-title class_">Promise</span>.<span class="hljs-title function_">reject</span>(errorMessage)
    }

    <span class="hljs-keyword">async</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">load</span>(<span class="hljs-params"></span>) {
      <span class="hljs-keyword">const</span> videoLoaded = <span class="hljs-keyword">await</span> <span class="hljs-title function_">initCamara</span>()
      videoLoaded.<span class="hljs-title function_">play</span>()
      <span class="hljs-keyword">return</span> videoLoaded
    }

    <span class="hljs-title function_">load</span>()
  }, [])

  <span class="hljs-keyword">return</span> (
    <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>
      <span class="hljs-attr">style</span>=<span class="hljs-string">{{</span>
        <span class="hljs-attr">display:</span> &#x27;<span class="hljs-attr">flex</span>&#x27;,
        <span class="hljs-attr">justifyContent:</span> &#x27;<span class="hljs-attr">center</span>&#x27;,
        <span class="hljs-attr">alignItems:</span> &#x27;<span class="hljs-attr">center</span>&#x27;,
        <span class="hljs-attr">flexDirection:</span> &#x27;<span class="hljs-attr">column</span>&#x27;,
      }}
    &gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">video</span> <span class="hljs-attr">className</span>=<span class="hljs-string">&quot;video&quot;</span> <span class="hljs-attr">playsInline</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">{videoElement}</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
        <span class="hljs-attr">disabled</span>=<span class="hljs-string">{processing}</span>
        <span class="hljs-attr">style</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">width:</span> <span class="hljs-attr">maxVideoSize</span>, <span class="hljs-attr">padding:</span> <span class="hljs-attr">10</span> }}
        <span class="hljs-attr">onClick</span>=<span class="hljs-string">{onClick}</span>
      &gt;</span>
        {processing ? &#x27;Processing...&#x27; : &#x27;Take a photo&#x27;}
      <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">canvas</span>
        <span class="hljs-attr">ref</span>=<span class="hljs-string">{canvasEl}</span>
        <span class="hljs-attr">width</span>=<span class="hljs-string">{maxVideoSize}</span>
        <span class="hljs-attr">height</span>=<span class="hljs-string">{maxVideoSize}</span>
      &gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">canvas</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  )
}
</code></pre><p>In our service:</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">class</span> <span class="hljs-title class_">CV</span> {
  <span class="hljs-comment">// ...previous service code here...</span>

  <span class="hljs-comment">/**
   * We are going to use the _dispatch event we created before to
   * call the postMessage with the msg and the image as payload.
   *
   * Thanks to what we&#x27;ve implemented in the _dispatch, this will
   * return a promise with the processed image.
   */</span>
  <span class="hljs-title function_">imageProcessing</span>(<span class="hljs-params">payload</span>) {
    <span class="hljs-keyword">return</span> <span class="hljs-variable language_">this</span>.<span class="hljs-title function_">_dispatch</span>({ <span class="hljs-attr">msg</span>: <span class="hljs-string">&#x27;imageProcessing&#x27;</span>, payload })
  }
}
</code></pre><p>In our worker:</p>
<pre><code class="hljs language-js"><span class="hljs-comment">// ...previous worker code here...</span>

<span class="hljs-comment">/**
 * With OpenCV we have to work with the images as cv.Mat (matrices),
 * so you&#x27;ll have to transform the ImageData to it.
 */</span>
<span class="hljs-keyword">function</span> <span class="hljs-title function_">imageProcessing</span>(<span class="hljs-params">{ msg, payload }</span>) {
  <span class="hljs-keyword">const</span> img = cv.<span class="hljs-title function_">matFromImageData</span>(payload)
  <span class="hljs-keyword">let</span> result = <span class="hljs-keyword">new</span> cv.<span class="hljs-title class_">Mat</span>()

  <span class="hljs-comment">// This converts the image to a greyscale.</span>
  cv.<span class="hljs-title function_">cvtColor</span>(img, result, cv.<span class="hljs-property">COLOR_BGR2GRAY</span>)
  <span class="hljs-title function_">postMessage</span>({ msg, <span class="hljs-attr">payload</span>: <span class="hljs-title function_">imageDataFromMat</span>(result) })
}

<span class="hljs-comment">/**
 * This function converts again from cv.Mat to ImageData
 */</span>
<span class="hljs-keyword">function</span> <span class="hljs-title function_">imageDataFromMat</span>(<span class="hljs-params">mat</span>) {
  <span class="hljs-comment">// converts the mat type to cv.CV_8U</span>
  <span class="hljs-keyword">const</span> img = <span class="hljs-keyword">new</span> cv.<span class="hljs-title class_">Mat</span>()
  <span class="hljs-keyword">const</span> depth = mat.<span class="hljs-title function_">type</span>() % <span class="hljs-number">8</span>
  <span class="hljs-keyword">const</span> scale =
    depth &lt;= cv.<span class="hljs-property">CV_8S</span> ? <span class="hljs-number">1.0</span> : depth &lt;= cv.<span class="hljs-property">CV_32S</span> ? <span class="hljs-number">1.0</span> / <span class="hljs-number">256.0</span> : <span class="hljs-number">255.0</span>
  <span class="hljs-keyword">const</span> shift = depth === cv.<span class="hljs-property">CV_8S</span> || depth === cv.<span class="hljs-property">CV_16S</span> ? <span class="hljs-number">128.0</span> : <span class="hljs-number">0.0</span>
  mat.<span class="hljs-title function_">convertTo</span>(img, cv.<span class="hljs-property">CV_8U</span>, scale, shift)

  <span class="hljs-comment">// converts the img type to cv.CV_8UC4</span>
  <span class="hljs-keyword">switch</span> (img.<span class="hljs-title function_">type</span>()) {
    <span class="hljs-keyword">case</span> cv.<span class="hljs-property">CV_8UC1</span>:
      cv.<span class="hljs-title function_">cvtColor</span>(img, img, cv.<span class="hljs-property">COLOR_GRAY2RGBA</span>)
      <span class="hljs-keyword">break</span>
    <span class="hljs-keyword">case</span> cv.<span class="hljs-property">CV_8UC3</span>:
      cv.<span class="hljs-title function_">cvtColor</span>(img, img, cv.<span class="hljs-property">COLOR_RGB2RGBA</span>)
      <span class="hljs-keyword">break</span>
    <span class="hljs-keyword">case</span> cv.<span class="hljs-property">CV_8UC4</span>:
      <span class="hljs-keyword">break</span>
    <span class="hljs-attr">default</span>:
      <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Error</span>(
        <span class="hljs-string">&#x27;Bad number of channels (Source image must have 1, 3 or 4 channels)&#x27;</span>
      )
  }
  <span class="hljs-keyword">const</span> clampedArray = <span class="hljs-keyword">new</span> <span class="hljs-title class_">ImageData</span>(
    <span class="hljs-keyword">new</span> <span class="hljs-title class_">Uint8ClampedArray</span>(img.<span class="hljs-property">data</span>),
    img.<span class="hljs-property">cols</span>,
    img.<span class="hljs-property">rows</span>
  )
  img.<span class="hljs-title function_">delete</span>()
  <span class="hljs-keyword">return</span> clampedArray
}

onmessage = <span class="hljs-keyword">function</span> (<span class="hljs-params">e</span>) {
  <span class="hljs-keyword">switch</span> (e.<span class="hljs-property">data</span>.<span class="hljs-property">msg</span>) {
    <span class="hljs-comment">// ...previous onmessage code here...</span>
    <span class="hljs-keyword">case</span> <span class="hljs-string">&#x27;imageProcessing&#x27;</span>:
      <span class="hljs-keyword">return</span> <span class="hljs-title function_">imageProcessing</span>(e.<span class="hljs-property">data</span>)
    <span class="hljs-attr">default</span>:
      <span class="hljs-keyword">break</span>
  }
}
</code></pre><p>The result:</p>
<img class="center" alt="First result of image processing using OpenCV in JavaScript" src="https://aralroca.com/images/blog-images/28.gif" />

<p>Although we have processed the image in a very simple way and we could have done it without using OpenCV, this is our &quot;hello world&quot; with OpenCV. It opens the doors to more complex things.</p>
<h2 id="conclusion">Conclusion</h2>
<p>We have seen how to use the most used library for computer vision in the browser. We&#39;ve seen how to compile OpenCV into webassembly and use it in a worker to not block the UI for a good performance. I hope that even if you have never heard of this library, now you&#39;ll give it a try.</p>
<br />
<img class="center" src="https://aralroca.com/images/blog-images/29.jpg" alt="Example of computer vision" />
<br />

<h2 id="code">Code</h2>
<p>I&#39;ve uploaded the code of this article on GitHub in case you want to take a look.</p>
<ul>
<li>CODE -&gt; <a href="https://github.com/vinissimus/opencv-js-webworker">https://github.com/vinissimus/opencv-js-webworker</a></li>
<li>DEMO -&gt; <a href="https://vinissimus.github.io/opencv-js-webworker/">https://vinissimus.github.io/opencv-js-webworker/</a></li>
</ul>
<p>To see a more sophisticated example implemented in Vue.js, take a look at this other repo:</p>
<ul>
<li><a href="https://github.com/latsic/imgalign">https://github.com/latsic/imgalign</a></li>
</ul>
<h2 id="references">References</h2>
<ul>
<li><a href="https://docs.opencv.org/3.4.10/d4/da1/tutorial_js_setup.html">https://docs.opencv.org/3.4.10/d4/da1/tutorial_js_setup.html</a></li>
<li><a href="https://docs.opencv.org/master/de/d06/tutorial_js_basic_ops.html">https://docs.opencv.org/master/de/d06/tutorial_js_basic_ops.html</a></li>
<li><a href="https://en.wikipedia.org/wiki/OpenCV">https://en.wikipedia.org/wiki/OpenCV</a></li>
<li><a href="https://github.com/latsic/imgalign">https://github.com/latsic/imgalign</a></li>
<li><a href="https://opencv.org/">https://opencv.org/</a></li>
</ul>
]]></content:encoded>
            </item>
            <item>
              <title>How to create pagination badges</title>
              <description>Learn how to re-use pagination badges logic in all your projects.</description>
              <link>https://aralroca.com/blog/pagination-badges</link>
              <guid isPermaLink="false">https://aralroca.com/blog/pagination-badges/</guid>
              <pubDate>Wed May 13 2020 00:00:00 GMT+0000 (Coordinated Universal Time)</pubDate>
              <content:encoded><![CDATA[<p>The purpose of this short article is to share a helper function to create typical paging badges. I have used this helper on several places and I think it can be useful for anyone who needs it.</p>
<img src="https://aralroca.com/images/blog-images/39.gif" alt="Example of paging badges" class="center" />

<p>The helper accepts a list of 3 options:</p>
<ul>
<li><code>currentPage</code> - The current page, which will determine how badges are displayed with separators.</li>
<li><code>pages</code> - Total number of pages to display.</li>
<li><code>numBadges</code> (optional). Number of badges to be generated, by default is 5.</li>
</ul>
<p>It returns an array with the badges as <code>number</code>, filling <code>null</code> for the separators. This is implemented this way (in pure JavaScript instead of returning for example JSX) to reuse it everywhere: (P)React, Vue, Svelte, Angular... Even in Node or Deno.</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">pagesBadges</span>(<span class="hljs-params">{ currentPage, pages, numBadges = <span class="hljs-number">5</span> }</span>) {
  <span class="hljs-keyword">const</span> maxBadgesSide = numBadges - <span class="hljs-number">2</span>

  <span class="hljs-comment">// Without separators case</span>
  <span class="hljs-comment">// ex: [1, 2, 3, 4, 5]</span>
  <span class="hljs-keyword">if</span> (pages &lt;= numBadges) {
    <span class="hljs-keyword">return</span> <span class="hljs-title class_">Array</span>.<span class="hljs-title function_">from</span>({ <span class="hljs-attr">length</span>: pages }).<span class="hljs-title function_">map</span>(<span class="hljs-function">(<span class="hljs-params">v, i</span>) =&gt;</span> i + <span class="hljs-number">1</span>)
  }

  <span class="hljs-keyword">const</span> sideBadges = <span class="hljs-title class_">Array</span>.<span class="hljs-title function_">from</span>({ <span class="hljs-attr">length</span>: numBadges - <span class="hljs-number">1</span> })

  <span class="hljs-comment">// With a separator at the end case</span>
  <span class="hljs-comment">// ex: [1, 2, 3, 4, null, 49]</span>
  <span class="hljs-keyword">if</span> (currentPage &lt;= maxBadgesSide) {
    <span class="hljs-keyword">return</span> [...sideBadges.<span class="hljs-title function_">map</span>(<span class="hljs-function">(<span class="hljs-params">v, i</span>) =&gt;</span> i + <span class="hljs-number">1</span>), <span class="hljs-literal">null</span>, pages]
  }

  <span class="hljs-comment">// With a separator at the beginning case</span>
  <span class="hljs-comment">// ex: [1, null, 46, 47, 48, 49]</span>
  <span class="hljs-keyword">if</span> (currentPage &gt; pages - maxBadgesSide) {
    <span class="hljs-keyword">return</span> [<span class="hljs-number">1</span>, <span class="hljs-literal">null</span>, ...sideBadges.<span class="hljs-title function_">map</span>(<span class="hljs-function">(<span class="hljs-params">v, i</span>) =&gt;</span> pages - i).<span class="hljs-title function_">reverse</span>()]
  }

  <span class="hljs-comment">// In the middle (separator left + right) case</span>
  <span class="hljs-comment">// ex: [1, null, 26, 27, 28, null, 49]</span>
  sideBadges.<span class="hljs-title function_">pop</span>()
  <span class="hljs-keyword">const</span> curr = <span class="hljs-title class_">Math</span>.<span class="hljs-title function_">floor</span>(sideBadges.<span class="hljs-property">length</span> / <span class="hljs-number">2</span>)
  <span class="hljs-keyword">const</span> center = sideBadges.<span class="hljs-title function_">map</span>(<span class="hljs-function">(<span class="hljs-params">v, i</span>) =&gt;</span> currentPage - curr + i)

  <span class="hljs-keyword">return</span> [<span class="hljs-number">1</span>, <span class="hljs-literal">null</span>, ...center, <span class="hljs-literal">null</span>, pages]
}
</code></pre><p>I&#39;ve published the code in GitHub (~200 bytes) in case that you want to use it in your projects:</p>
<ul>
<li><a href="https://github.com/aralroca/js-paging">https://github.com/aralroca/js-paging</a></li>
</ul>
<p>This code is not providing any UI component, but it gives you the logic. With it you&#39;ll be able to create your paging component with the library/framework you want, to your liking. This partly offers a lot of flexibility in terms of design.</p>
<h2 id="example-of-usage-in-react">Example of usage in React</h2>
<p>Sandbox using the paging module in React:</p>
<iframe
  src="https://codesandbox.io/embed/js-paging-j4hvd?fontsize=14&hidenavigation=1&theme=dark"
  style="width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;"
  title="js-paging"
  allow="accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr"
  sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"
></iframe>
]]></content:encoded>
            </item>
            <item>
              <title>Power of Partial Prerendering with Bun</title>
              <description>Unlock the power of partial pre-rendering with Bun, optimizing web application performance and package size effortlessly.</description>
              <link>https://aralroca.com/blog/partial-prerendering</link>
              <guid isPermaLink="false">https://aralroca.com/blog/partial-prerendering/</guid>
              <pubDate>Sun Mar 24 2024 00:00:00 GMT+0000 (Coordinated Universal Time)</pubDate>
              <content:encoded><![CDATA[<p>In modern web development, optimizing the performance of web applications is paramount. One approach gaining traction is <strong>partial prerendering</strong>, a technique that combines static and dynamic content rendering to enhance both the bundle size and runtime speed. In this article, we&#39;ll delve into the concept of partial prerendering and explore its benefits, especially when implemented through Ahead of Time (<a href="https://en.wikipedia.org/wiki/Ahead-of-time_compilation">AOT</a>) rendering.</p>
<p>Also, we will see how to do it with a Bun plugin and be able to apply it in every JSX framework in an easy way.</p>
<h2 id="how-partial-prerendering-works">How Partial Prerendering Works</h2>
<p>Working with JSX components we have usually generated static and/or dynamic pages. Some frameworks allow you to prerender static pages during compilation, which significantly improves initial load times. However, most routes are not fully static or dynamic. You may have a route that has both static and dynamic content.</p>
<p>Partial prerendering (PPR) involves rendering static components during build-time and deferring the rendering of dynamic components to runtime. This means that instead of rendering all components dynamically, some components are pre-rendered as HTML during the build process.</p>
<p>Thinking about server-side rendering (SSR), in many cases, we have completely static components, such as the header, footer, etc., and we can save the milliseconds of rendering of these components if we only prerender once in build-time and then do not have to repeat the rendering for each request.</p>
<p>The blue part is the run-time rendering time:</p>
<figure align="center">
    <img src="https://aralroca.com/images/blog-images/partial-prerendering.gif" alt="with partial prerender">
    <figcaption><small>render vs partial prerender</small></figcaption>
</figure>

<p>There are also cases in even the data that we consume in the components can be static, imagine that we want to show in our e-commerce homepage static data sections where you have to hardcode a JSON file with this data to make it faster, however:</p>
<ul>
<li>It&#39;s needed to parse the JSON and render the component.</li>
<li>We have duplicate data between the database and the hardcoded data.</li>
</ul>
<p>In this case, we could directly request them from the source without having them hardcoded and prerender the component ahead of time to save milliseconds of parsing and rendering.</p>
<h3 id="benefits">Benefits</h3>
<ul>
<li>Smaller Bundle Size</li>
<li>Faster Runtime Rendering</li>
<li>Less resources spent on each request</li>
<li>Better UX</li>
<li>Better SEO</li>
</ul>
<h2 id="macros">Macros</h2>
<p>Bun introduces the idea of <a href="https://bun.sh/docs/bundler/macros">Macros</a> into JavaScript. Macros are a new paradigm that allows optimizations ahead of time just by adding an <a href="https://github.com/tc39/proposal-import-attributes">import attribute</a>.</p>
<pre><code class="hljs language-tsx"><span class="hljs-keyword">import</span> { random } <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;./random.ts&#x27;</span> <span class="hljs-keyword">with</span> { <span class="hljs-attr">type</span>: <span class="hljs-string">&#x27;macro&#x27;</span> };

<span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">`Your random number is <span class="hljs-subst">${random()}</span>`</span>);
</code></pre><p>By adding this import attribute <code>macro</code>, the macro runs this <code>random</code> function at bundle-time. The value returned from these functions is directly inlined into your bundle.</p>
<p>Well, this allows things like static data to be requested directly from the source instead of hardcoding it and passing the work ahead of time. The bundler performs dead code elimination after running the macro, then we will have a smaller bundle size. However, can we directly prerender components with the macro?</p>
<p>Well, for the communication between the Bun transpiler and the macro the data has to be serialized, so not all data is supported, only this is supported:</p>
<ul>
<li><a href="https://en.wikipedia.org/wiki/JSON">JSON-compatible</a> data structures</li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray">TypedArray</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/API/Response">Response</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/API/Blob">Blob</a></li>
</ul>
<p>As far as we can see, functions as a data structure are not supported, and <strong>components are functions</strong>! so, can we prerender a component from a macro?</p>
<p>The answer is yes, but not in an elegant way using Bun&#39;s macro. You have to pass the absolute path string where the component is located and from the macro import it, render it, and pass the resulting JSX with the html string injection.</p>
<figure align="center">
    <img width="128px" height="128px" src="https://aralroca.com/images/blog-images/bunbum.webp" alt="Bun bum!">
</figure>

<p>The purpose of this article is not to explain in detail how to do it through a Bun macro because it would be too cumbersome for developers to have to do this every time they want to prerender a component. However, read on, because now thanks to a Bun plugin you can integrate it in an easy way.</p>
<h2 id="prerender-macro">Prerender Macro</h2>
<p>Last summer I started Brisa, an experimental framework, and I hope to make it public this summer. One of the features I had in mind for a long time is to be able to make hybrid pages between static and dynamic and it would be very easy to use. When I discovered the idea of Bun macros, the first thing that came to my mind was that something similar had to exist to prerender the components, now I have made public this Bun plugin so that all JSX frameworks can use this feature.</p>
<p>Today I release <a href="https://github.com/aralroca/prerender-macro">Prerender Macro</a> Bun plugin. As of today, partial hydration can be managed with Bun as follows:</p>
<pre><code class="hljs language-tsx"><span class="hljs-keyword">import</span> <span class="hljs-title class_">StaticComponent</span> <span class="hljs-keyword">from</span> <span class="hljs-string">&quot;@/static-component&quot;</span> <span class="hljs-keyword">with</span> { <span class="hljs-attr">type</span>: <span class="hljs-string">&quot;prerender&quot;</span> };
<span class="hljs-keyword">import</span> <span class="hljs-title class_">DynamicComponent</span> <span class="hljs-keyword">from</span> <span class="hljs-string">&quot;@/dynamic-component&quot;</span>;

<span class="hljs-keyword">return</span> (
  <span class="language-xml"><span class="hljs-tag">&lt;&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">StaticComponent</span> <span class="hljs-attr">foo</span>=<span class="hljs-string">&quot;bar&quot;</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">DynamicComponent</span> /&gt;</span>
  <span class="hljs-tag">&lt;/&gt;</span></span>
);
</code></pre><p>I invite you to try the <a href="#demos">demos</a>.</p>
<a href="https://github.com/aralroca/prerender-macro/tree/main/examples/react">
<figure align="center">
    <img src="https://aralroca.com/images/blog-images/partial-prerender.gif">
    <figcaption><small>Example with React and <code>prerender-macro</code></small></figcaption>
</figure>
</a>

<h2 id="next-steps">Next steps</h2>
<p>The concept of PPR is not something new, recently Vercel together with people from React and Next.js has made it <a href="https://vercel.com/blog/partial-prerendering-with-next-js-creating-a-new-default-rendering-model#try-ppr-on-vercel-today">possible to do it</a> based on <code>Suspense</code> components and have the static part served on the nearest edge. It&#39;s a brilliant idea.</p>
<p>With <a href="https://github.com/aralroca/prerender-macro">Prerender Macro</a> I add my grain of sand to open-source so that it can be combined or can be used in other contexts differently, because for version 0.1 it injects the HTML into the JSX tree itself, instead of fetching it from the edge. However, I would like for future versions to generate the infrastructure as code (IaC) to connect to the necessary cloud (Vercel, or others that implement PPR). Now that the library is open-source any contribution in this regard will be very welcome.</p>
<h2 id="demos">Demos</h2>
<p>There are demos for <a href="https://github.com/aralroca/prerender-macro/tree/main/examples/brisa">Brisa</a>, <a href="https://github.com/aralroca/prerender-macro/tree/main/examples/react">React</a> and <a href="https://github.com/aralroca/prerender-macro/tree/main/examples/preact">Preact</a>. But feel free to <a href="https://github.com/aralroca/prerender-macro/fork">add</a> more JSX frameworks to help people understand the plugin configuration they need to adapt to the framework.</p>
<figure align="center">
    <img src="https://aralroca.com/images/blog-images/we-got-it.jpg">
      <figcaption><small>Photo by Clay Banks on Unsplash</small></figcaption>
</figure>

<p>For further updates on the Brisa framework and more, <a href="https://aralroca.com/blog/partial-prerendering#demos">subscribe</a> to my blog newsletter today!</p>
<h2 id="references">References</h2>
<ul>
<li><a href="https://github.com/aralroca/prerender-macro">Prerender Macro GitHub repository</a></li>
<li><a href="https://github.com/aralroca/prerender-macro/tree/main/examples">Examples of Prerender Macro</a></li>
<li><a href="https://nextjs.org/docs/app/api-reference/next-config-js/partial-prerendering">Partial Prerendering in Next.js</a></li>
<li><a href="https://vercel.com/blog/partial-prerendering-with-next-js-creating-a-new-default-rendering-model#try-ppr-on-vercel-today">Partial Prerendering Vercel</a></li>
</ul>
]]></content:encoded>
            </item>
            <item>
              <title>React state with a fragmented store</title>
              <description>Fragmented store concept and how to apply it with React Context</description>
              <link>https://aralroca.com/blog/react-fragmented-store</link>
              <guid isPermaLink="false">https://aralroca.com/blog/react-fragmented-store/</guid>
              <pubDate>Fri Sep 24 2021 00:00:00 GMT+0000 (Coordinated Universal Time)</pubDate>
              <content:encoded><![CDATA[<p>There are many ways to manage the React state between many components: using libraries like Redux, MobX, Immer, Recoil, etc, or using a React Context.</p>
<p>After using several of them, I personally choose React Context because of its simplicity. To use a React Context to manage the state you have to put the state in the <code>Provider</code> along with the method to update it. Then you can consume it from the <code>Consumer</code>.</p>
<p>However, the problem with React Context is that if you change the value of a single field of the state, instead of updating the components that use only this field, all components that use any field from the state will be re-rendered.</p>
<figure align="center">
  <img class="center" src="https://aralroca.com/images/blog-images/unfragmented-store-schema.gif" alt="Unfragmented store schema" />
  <figcaption><small>Unfragmented state in React Context</small></figcaption>
</figure>

<p>In this article I&#39;m going to explain the concept of &quot;<strong>fragmented store</strong>&quot; to solve this, and how to use it in a simple and easy way.</p>
<h2 id="what-is-a-fragmented-store">What is a fragmented store</h2>
<p>The fragmented store makes it possible to <strong>consume each field of the store separately</strong>. Since most of the components will consume few fields of the whole store, it&#39;s not interesting that they are re-rendered when other fields are updated.</p>
<figure align="center">
  <img class="center" src="https://aralroca.com/images/blog-images/fragmented-store-schema.gif" alt="Fragmented store schema" />
  <figcaption><small>Fragmented store in React Context</small></figcaption>
</figure>

<p>To solve this with React Context you have to create a context for each field of the store, which is not very feasible due to its difficulty.</p>
<pre><code class="hljs language-jsx"><span class="hljs-comment">// ❌  Not recommended</span>
&lt;<span class="hljs-title class_">UsernameProvider</span>&gt;
  <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">AgeProvider</span>&gt;</span>{children}<span class="hljs-tag">&lt;/<span class="hljs-name">AgeProvider</span>&gt;</span></span>
&lt;/<span class="hljs-title class_">UsernameProvider</span>&gt;
</code></pre><p>Naturally, if we have very few properties in the &quot;store&quot; it could work. But when we start to have too many, there will be too much logic implemented to solve the problem of re-rendering, since it would be necessary to implement each context for each property.</p>
<p>However, I have good news, it can be automatically created.</p>
<h2 id="how-to-use-a-fragmented-store">How to use a fragmented store</h2>
<p>I created a tiny library (500b) called <strong><a href="https://github.com/aralroca/fragmented-store">fragmented-store</a></strong> to make it super simple and easy to use. It uses React Context underneath (I&#39;ll explain later what it does exactly).</p>
<figure align="center">
  <img class="center" style="max-width: 178px" src="https://raw.githubusercontent.com/aralroca/fragmented-store/master/logo.svg" alt="Fragmented store logo" />
  <figcaption><small>Fragmented store logo</small></figcaption>
</figure>

<h3 id="create-context--add-the-provider">Create context + add the Provider</h3>
<p>Just as we would go with the React Context, we need to create the context and add the provider to the application. We&#39;ll take this opportunity to initialize the store to the data we want at the beginning.</p>
<pre><code class="hljs language-jsx"><span class="hljs-keyword">import</span> createStore <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;fragmented-store&#x27;</span>

<span class="hljs-comment">// It is advisable to set all the fields. If you don&#x27;t know the</span>
<span class="hljs-comment">// initial value you can set it to undefined or null to be able</span>
<span class="hljs-comment">// to consume the values in the same way</span>
<span class="hljs-keyword">const</span> { <span class="hljs-title class_">Provider</span> } = <span class="hljs-title function_">createStore</span>({
  <span class="hljs-attr">username</span>: <span class="hljs-string">&#x27;Aral&#x27;</span>,
  <span class="hljs-attr">age</span>: <span class="hljs-number">31</span>,
})

<span class="hljs-keyword">function</span> <span class="hljs-title function_">App</span>(<span class="hljs-params"></span>) {
  <span class="hljs-keyword">return</span> <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">Provider</span>&gt;</span>{/* rest */}<span class="hljs-tag">&lt;/<span class="hljs-name">Provider</span>&gt;</span></span>
}
</code></pre><h3 id="consume-one-field">Consume one field</h3>
<p>For the example, we will make 2 components that consume a field of the store. As you&#39;ll see, it&#39;s similar to having a <code>useState</code> in each component with the property that you want, with the difference that several components can share the same property with the same value.</p>
<pre><code class="hljs language-jsx"><span class="hljs-keyword">import</span> createStore <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;fragmented-store&#x27;</span>

<span class="hljs-comment">// We can import hooks with the property name in camelCase.</span>
<span class="hljs-comment">// username -&gt; useUsername</span>
<span class="hljs-comment">// age -&gt; useAge</span>
<span class="hljs-keyword">const</span> { <span class="hljs-title class_">Provider</span>, useUsername, useAge } = <span class="hljs-title function_">createStore</span>({
  <span class="hljs-attr">username</span>: <span class="hljs-string">&#x27;Aral&#x27;</span>,
  <span class="hljs-attr">age</span>: <span class="hljs-number">31</span>,
})

<span class="hljs-keyword">function</span> <span class="hljs-title function_">App</span>(<span class="hljs-params"></span>) {
  <span class="hljs-keyword">return</span> (
    <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">Provider</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">UsernameComponent</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">AgeComponent</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">Provider</span>&gt;</span></span>
  )
}

<span class="hljs-comment">// Consume the &quot;username&quot; field</span>
<span class="hljs-keyword">function</span> <span class="hljs-title function_">UsernameComponent</span>(<span class="hljs-params"></span>) {
  <span class="hljs-keyword">const</span> [username, setUsername] = <span class="hljs-title function_">useUsername</span>()
  <span class="hljs-keyword">return</span> (
    <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> setUsername(&#x27;AnotherUserName&#x27;)}&gt;
      Update {username}
    <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span></span>
  )
}

<span class="hljs-comment">// Consume the &quot;age&quot; field</span>
<span class="hljs-keyword">function</span> <span class="hljs-title function_">AgeComponent</span>(<span class="hljs-params"></span>) {
  <span class="hljs-keyword">const</span> [age, setAge] = <span class="hljs-title function_">useAge</span>()
  <span class="hljs-keyword">return</span> (
    <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>{age}<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> setAge((s) =&gt; s + 1)}&gt;Inc age<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  )
}
</code></pre><p>When the <code>AgeComponent</code> updates the <code>age</code> field only the <code>AgeComponent</code> is re-rendered. The <code>UsernameComponent</code> is not re-rendered since it does not use the same fragmented part of the store.</p>
<h3 id="consume-all-the-store">Consume all the store</h3>
<p>In case you want to update several fields of the store, you can consume the whole store directly. The component that consumes all the store will be re-render for any updated field.</p>
<pre><code class="hljs language-jsx"><span class="hljs-keyword">import</span> createStore <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;fragmented-store&#x27;</span>

<span class="hljs-comment">// Special hook useStore</span>
<span class="hljs-keyword">const</span> { <span class="hljs-title class_">Provider</span>, useStore } = <span class="hljs-title function_">createStore</span>({
  <span class="hljs-attr">username</span>: <span class="hljs-string">&#x27;Aral&#x27;</span>,
  <span class="hljs-attr">age</span>: <span class="hljs-number">31</span>,
})

<span class="hljs-keyword">function</span> <span class="hljs-title function_">App</span>(<span class="hljs-params"></span>) {
  <span class="hljs-keyword">return</span> (
    <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">Provider</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">AllStoreComponent</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">Provider</span>&gt;</span></span>
  )
}

<span class="hljs-comment">// Consume all fields of the store</span>
<span class="hljs-keyword">function</span> <span class="hljs-title function_">AllStoreComponent</span>(<span class="hljs-params"></span>) {
  <span class="hljs-keyword">const</span> [store, update] = <span class="hljs-title function_">useStore</span>()

  <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>({ store }) <span class="hljs-comment">// all store</span>

  <span class="hljs-keyword">function</span> <span class="hljs-title function_">onClick</span>(<span class="hljs-params"></span>) {
    <span class="hljs-title function_">update</span>({ <span class="hljs-attr">age</span>: <span class="hljs-number">32</span>, <span class="hljs-attr">username</span>: <span class="hljs-string">&#x27;Aral Roca&#x27;</span> })
  }

  <span class="hljs-keyword">return</span> <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{onClick}</span>&gt;</span>Modify store<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span></span>
}
</code></pre><p>And again, if we only update some fields, the components that consume these fields will be re-rendered while other components that consume other fields won&#39;t!</p>
<pre><code class="hljs language-js"><span class="hljs-comment">// It only updates the &quot;username&quot; field, other fields won&#x27;t be updated</span>
<span class="hljs-comment">// The UsernameComponent is going to be re-rendered while AgeComponent won&#x27;t :)</span>
<span class="hljs-title function_">update</span>({ <span class="hljs-attr">username</span>: <span class="hljs-string">&#x27;Aral Roca&#x27;</span> })
</code></pre><p>You don&#39;t need to do this <em>(even if it&#39;s supported)</em>:</p>
<pre><code class="hljs language-js"><span class="hljs-title function_">update</span>(<span class="hljs-function">(<span class="hljs-params">s</span>) =&gt;</span> ({ ...s, <span class="hljs-attr">username</span>: <span class="hljs-string">&#x27;Aral&#x27;</span> }))
</code></pre><p>With this only the components that consume the <code>username</code> field with the <code>useUsername</code> hook would be re-rendered.</p>
<h2 id="how-is-implemented-underneath">How is implemented underneath</h2>
<p>The <a href="https://github.com/aralroca/fragmented-store">fragmented-store</a> library is a single <a href="https://github.com/aralroca/fragmented-store/blob/master/index.js">very short file</a>. It&#39;s similar of what we&#39;d manually do to create several React Contexts for each property. It automatically creates everything you need to consume and update them (hooks).</p>
<pre><code class="hljs language-jsx"><span class="hljs-keyword">import</span> <span class="hljs-title class_">React</span>, { useState, useContext, createContext } <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;react&#x27;</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">createStore</span>(<span class="hljs-params">store = {}</span>) {
  <span class="hljs-keyword">const</span> keys = <span class="hljs-title class_">Object</span>.<span class="hljs-title function_">keys</span>(store)
  <span class="hljs-keyword">const</span> <span class="hljs-title function_">capitalize</span> = (<span class="hljs-params">k</span>) =&gt; <span class="hljs-string">`<span class="hljs-subst">${k[<span class="hljs-number">0</span>].toUpperCase()}</span><span class="hljs-subst">${k.slice(<span class="hljs-number">1</span>, k.length)}</span>`</span>

  <span class="hljs-comment">// storeUtils is the object we&#x27;ll return with everything</span>
  <span class="hljs-comment">// (Provider, hooks)</span>
  <span class="hljs-comment">//</span>
  <span class="hljs-comment">// We initialize it by creating a context for each property and</span>
  <span class="hljs-comment">// returning a hook to consume the context of each property</span>
  <span class="hljs-keyword">const</span> storeUtils = keys.<span class="hljs-title function_">reduce</span>(<span class="hljs-function">(<span class="hljs-params">o, key</span>) =&gt;</span> {
    <span class="hljs-keyword">const</span> context = <span class="hljs-title function_">createContext</span>(store[key]) <span class="hljs-comment">// Property context</span>
    <span class="hljs-keyword">const</span> keyCapitalized = <span class="hljs-title function_">capitalize</span>(key)

    <span class="hljs-keyword">if</span> (keyCapitalized === <span class="hljs-string">&#x27;Store&#x27;</span>) {
      <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">error</span>(
        <span class="hljs-string">&#x27;Avoid to use the &quot;store&quot; name at the first level, it\&#x27;s reserved for the &quot;useStore&quot; hook.&#x27;</span>
      )
    }

    <span class="hljs-keyword">return</span> {
      ...o,
      <span class="hljs-comment">// All contexts</span>
      <span class="hljs-attr">contexts</span>: [...(o.<span class="hljs-property">contexts</span> || []), { context, key }],
      <span class="hljs-comment">// Hook to consume the property context</span>
      [<span class="hljs-string">`use<span class="hljs-subst">${keyCapitalized}</span>`</span>]: <span class="hljs-function">() =&gt;</span> <span class="hljs-title function_">useContext</span>(context),
    }
  }, {})

  <span class="hljs-comment">// We create the main provider by wrapping all the providers</span>
  storeUtils.<span class="hljs-property">Provider</span> = <span class="hljs-function">(<span class="hljs-params">{ children }</span>) =&gt;</span> {
    <span class="hljs-keyword">const</span> <span class="hljs-title function_">Empty</span> = (<span class="hljs-params">{ children }</span>) =&gt; children
    <span class="hljs-keyword">const</span> <span class="hljs-title class_">Component</span> = storeUtils.<span class="hljs-property">contexts</span>
      .<span class="hljs-title function_">map</span>(<span class="hljs-function">(<span class="hljs-params">{ context, key }</span>) =&gt;</span> <span class="hljs-function">(<span class="hljs-params">{ children }</span>) =&gt;</span> {
        <span class="hljs-keyword">const</span> ctx = <span class="hljs-title function_">useState</span>(store[key])
        <span class="hljs-keyword">return</span> <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">context.Provider</span> <span class="hljs-attr">value</span>=<span class="hljs-string">{ctx}</span>&gt;</span>{children}<span class="hljs-tag">&lt;/<span class="hljs-name">context.Provider</span>&gt;</span></span>
      })
      .<span class="hljs-title function_">reduce</span>(
        <span class="hljs-function">(<span class="hljs-params">RestProviders, Provider</span>) =&gt;</span>
          <span class="hljs-function">(<span class="hljs-params">{ children }</span>) =&gt;</span>
            (
              <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">Provider</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">RestProviders</span>&gt;</span>{children}<span class="hljs-tag">&lt;/<span class="hljs-name">RestProviders</span>&gt;</span>
              <span class="hljs-tag">&lt;/<span class="hljs-name">Provider</span>&gt;</span></span>
            ),
        <span class="hljs-title class_">Empty</span>
      )

    <span class="hljs-keyword">return</span> <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">Component</span>&gt;</span>{children}<span class="hljs-tag">&lt;/<span class="hljs-name">Component</span>&gt;</span></span>
  }

  <span class="hljs-comment">// As a bonus, we create the useStore hook to return all the</span>
  <span class="hljs-comment">// state. Also to return an updater that uses all the created hooks at</span>
  <span class="hljs-comment">// the same time</span>
  storeUtils.<span class="hljs-property">useStore</span> = <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> state = {}
    <span class="hljs-keyword">const</span> updates = {}
    keys.<span class="hljs-title function_">forEach</span>(<span class="hljs-function">(<span class="hljs-params">k</span>) =&gt;</span> {
      <span class="hljs-keyword">const</span> [s, u] = storeUtils[<span class="hljs-string">`use<span class="hljs-subst">${capitalize(k)}</span>`</span>]()
      state[k] = s
      updates[k] = u
    })

    <span class="hljs-keyword">function</span> <span class="hljs-title function_">updater</span>(<span class="hljs-params">newState</span>) {
      <span class="hljs-keyword">const</span> s =
        <span class="hljs-keyword">typeof</span> newState === <span class="hljs-string">&#x27;function&#x27;</span> ? <span class="hljs-title function_">newState</span>(state) : newState || {}
      <span class="hljs-title class_">Object</span>.<span class="hljs-title function_">keys</span>(s).<span class="hljs-title function_">forEach</span>(<span class="hljs-function">(<span class="hljs-params">k</span>) =&gt;</span> updates[k] &amp;&amp; updates[k](s[k]))
    }

    <span class="hljs-keyword">return</span> [state, updater]
  }

  <span class="hljs-comment">// Return everything we&#x27;ve generated</span>
  <span class="hljs-keyword">return</span> storeUtils
}
</code></pre><h2 id="demo">Demo</h2>
<p>I created a Codesandbox in case you want to try how it works. I added a <code>console.log</code> in each component so you can check when each one is re-rendered. The example is super simple, but you can try creating your own components and your state.</p>
<ul>
<li><a href="https://codesandbox.io/s/fragmented-store-example-4p5dv?file=/src/App.js">https://codesandbox.io/s/fragmented-store-example-4p5dv?file=/src/App.js</a></li>
</ul>
<iframe
  src="https://codesandbox.io/embed/fragmented-store-example-4p5dv?fontsize=14&hidenavigation=0&theme=dark"
  style="width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;"
  title="webgl-triangle"
  allow="accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking"
  sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"
></iframe>

<h2 id="conclusions">Conclusions</h2>
<p>In this article I&#39;ve explained the benefits of the &quot;fragmented store&quot; concept and how to apply it with React Context without the need to manually create many contexts.</p>
<p>In the example of the article and the fragmented-store library the fragmentation level is only at the first level for now. The library I&#39;ve implemented is in a very early stage and there are certainly a number of improvements that could be made. Any proposal for changes can be made on GitHub as the project is open source and will be very well received:</p>
<ul>
<li><a href="https://github.com/aralroca/fragmented-store">https://github.com/aralroca/fragmented-store</a></li>
</ul>
]]></content:encoded>
            </item>
            <item>
              <title>Build Reactive Web Components with SSR</title>
              <description>A quick guide on how to build reactive Web Components that work with SSR and with any JavaScript or Vanilla JS framework.</description>
              <link>https://aralroca.com/blog/reactive-web-components-with-ssr</link>
              <guid isPermaLink="false">https://aralroca.com/blog/reactive-web-components-with-ssr/</guid>
              <pubDate>Sat Aug 24 2024 00:00:00 GMT+0000 (Coordinated Universal Time)</pubDate>
              <content:encoded><![CDATA[<p>Traditional way of writing Web Components is not very SSR (Server Side Rendering) friendly. In this post, I show you how you can build reactive Web Components that work with SSR and with any JavaScript framework (Vue, React, Svelte, Solid, Brisa) or Vanilla JS.</p>
<ul>
<li><a href="#introduction">Introduction</a></li>
<li><a href="#writting-a-web-component-with-brisa">Writting a Web Component with Brisa</a></li>
<li><a href="#building-the-web-component">Building the Web Component</a></li>
<li><a href="#loading-the-web-component-in-a-vanilla-js-project">Loading the Web Component in a Vanilla JS project</a></li>
<li><a href="#ssr-of-the-web-component">SSR of the Web Component</a></li>
<li><a href="#tell-me-more-about-brisa-please">Tell me more about Brisa... Please...</a></li>
<li><a href="#note-for-web-component-library-creators">Note for Web Component library creators</a></li>
<li><a href="#example">Example</a></li>
<li><a href="#conclusion">Conclusion</a></li>
</ul>
<h2 id="introduction">Introduction</h2>
<p>We are going to use <a href="https://brisa.build">Brisa</a> Web Component Compiler. Brisa is a web framework that, besides being similar to other frameworks like Next.js or Nuxt.js, also allows you to build reactive Web Components that work with signals for reactivity, with JSX and with SSR.</p>
<a href="https://brisa.build" target="_blank" rel="noopener noreferrer">
<figure align="center">
<img width="100" height="100" src="https://aralroca.com/images/blog-images/brisa.svg" alt="Brisa logo" class="center" />
  <figcaption><small>Brisa logo</small></figcaption>
</figure>
</a>

<p>In order to do this, you only need to know the syntax of Brisa when writing Web Components. Brisa is not yet public as it is currently at <strong>95.48% of the v0.1 routemap</strong>, but we estimate that in 1 month it will be ready for launch and everyone will be able to access it. However, even if it is not public at all, you can already use it to create your own Web Components libraries.</p>
<h2 id="writting-a-web-component-with-brisa">Writting a Web Component with Brisa</h2>
<p>As an example, we are going to write a Web Component of a counter, as always, the classic example.</p>
<p><strong>counter-wc.tsx</strong></p>
<pre><code class="hljs language-tsx"><span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> { <span class="hljs-title class_">WebContext</span> } <span class="hljs-keyword">from</span> <span class="hljs-string">&quot;brisa&quot;</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">CounterWC</span>(<span class="hljs-params">
  { start = <span class="hljs-number">0</span>, color = <span class="hljs-string">&quot;#2cebcf&quot;</span> }: { start?: <span class="hljs-built_in">number</span>; color?: <span class="hljs-built_in">string</span> },
  { state, css }: <span class="hljs-title class_">WebContext</span>,
</span>) {
  <span class="hljs-keyword">const</span> count = <span class="hljs-title function_">state</span>(start);

  css`<span class="language-css">
    <span class="hljs-selector-tag">button</span> {
      <span class="hljs-attribute">background-color</span>: </span><span class="hljs-subst">${color}</span><span class="language-css">;
      <span class="hljs-attribute">color</span>: white;
      <span class="hljs-attribute">border</span>: none;
      <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">5px</span>;
      <span class="hljs-attribute">padding</span>: <span class="hljs-number">10px</span>;
      <span class="hljs-attribute">margin</span>: <span class="hljs-number">5px</span>;
      <span class="hljs-attribute">cursor</span>: pointer;
    }
    <span class="hljs-selector-tag">div</span> {
      <span class="hljs-attribute">display</span>: flex;
      <span class="hljs-attribute">justify-content</span>: center;
      <span class="hljs-attribute">align-items</span>: center;
    }
  `</span>;

  <span class="hljs-keyword">return</span> (
    <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> count.value++}&gt;+<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
      {count.value}
      <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> count.value--}&gt;-<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
}
</code></pre><p>Brisa uses the name of the files to know the selector, here the selector would be <code>counter-wc</code>.</p>
<blockquote>
<p><strong>TIP</strong>: Although Brisa is not public yet, you can use TypeScript types to guide you on how to write Web Components.</p>
</blockquote>
<p>In the example above, <code>state</code> is used to create a signal and then using the <code>.value</code> you make it reactive inside the JSX. The props are also special signals, since as they are read-only, the <code>.value</code> is not used to make it easier to use and to define default values more easily, this is done through build-time optimizations, similar to React to act as if they were using signals but the other way around.</p>
<p>The <code>css</code> template literal allows it to react to reactive changes in this case of the <code>color</code> property. This <code>css</code> template literal outside of this example is very useful for making reactive animations easily. It is important to remember that Web Components work with Shadow DOM, so the CSS does not affect the rest of the page.</p>
<h2 id="building-the-web-component">Building the Web Component</h2>
<p>To build the Web Component, you need to run the following command:</p>
<pre><code class="hljs language-bash">brisa build -w counter-wc.tsx
</code></pre><p>This command will generate 2 files:</p>
<pre><code class="hljs language-bash">[ <span class="hljs-built_in">wait</span> ]  🚀 building your standalone components...
[ info ]
[ info ]   Standalone components:
[ info ]   - build/counter-wc.client.js (670.00 B)
[ info ]   - build/counter-wc.server.js (842.00 B)
[ info ]
[ info ]   ✨  Done <span class="hljs-keyword">in</span> 42.20ms.
</code></pre><p>These files are not the Web Component, it is only the rendering function of the Web Component optimized at build-time to be as light as possible <em>(the bytes that come out are without gzip)</em>.</p>
<p>So, how do we load the Web Component?</p>
<h2 id="loading-the-web-component-in-a-vanilla-js-project">Loading the Web Component in a Vanilla JS project</h2>
<p>To do this, you need to add the importmap in the HTML with <code>brisa/client</code> and then load the <code>counter-wc.client.js</code> module:</p>
<pre><code class="hljs language-html"><span class="hljs-meta">&lt;!doctype <span class="hljs-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">&quot;en&quot;</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">&quot;UTF-8&quot;</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">&quot;viewport&quot;</span> <span class="hljs-attr">content</span>=<span class="hljs-string">&quot;width=device-width, initial-scale=1.0&quot;</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Brisa Web Component Example<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">type</span>=<span class="hljs-string">&quot;importmap&quot;</span>&gt;</span><span class="language-javascript">
      {
        <span class="hljs-string">&quot;imports&quot;</span>: {
          <span class="hljs-string">&quot;brisa/client&quot;</span>: <span class="hljs-string">&quot;https://unpkg.com/brisa@latest/client-simplified/index.js&quot;</span>
        }
      }
    </span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">type</span>=<span class="hljs-string">&quot;module&quot;</span> <span class="hljs-attr">src</span>=<span class="hljs-string">&quot;https://unpkg.com/counter-wc@latest&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">counter-wc</span> <span class="hljs-attr">start</span>=<span class="hljs-string">&quot;15&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">counter-wc</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre><p>Here only the rendering part would be ported in each web component file, while they would all use the same Brisa wrapper defined in the importmap, which is responsible for creating the Web Component with the signals and the shadow DOM.</p>
<h2 id="ssr-of-the-web-component">SSR of the Web Component</h2>
<p>SSR of a Web Component can now be done thanks to <a href="https://web.dev/articles/declarative-shadow-dom">Declarative Shadow DOM</a>. The <code>counter-wc.server.js</code> file has already been compiled with this behavior, so you only need to import it on your server and render it in the HTML and adapt it to your server framework.</p>
<p>Here is an example with Bun.js or Node.js without using JSX:</p>
<p><strong>ssr.js</strong></p>
<pre><code class="hljs language-js"><span class="hljs-keyword">import</span> { renderToString } <span class="hljs-keyword">from</span> <span class="hljs-string">&quot;brisa/server&quot;</span>;
<span class="hljs-keyword">import</span> { jsx } <span class="hljs-keyword">from</span> <span class="hljs-string">&quot;brisa/jsx-runtime&quot;</span>;
<span class="hljs-keyword">import</span> <span class="hljs-title class_">CustomCounter</span> <span class="hljs-keyword">from</span> <span class="hljs-string">&quot;counter-wc/server&quot;</span>;

<span class="hljs-keyword">const</span> html = <span class="hljs-string">`
&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;en&quot;&gt;
	&lt;head&gt;
	&lt;meta charset=&quot;UTF-8&quot;&gt;
	&lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&gt;
	&lt;title&gt;Brisa Web Component Example&lt;/title&gt;
	&lt;script type=&quot;importmap&quot;&gt;
	{
		&quot;imports&quot;: {
			&quot;brisa/client&quot;: &quot;https://unpkg.com/brisa@latest/client-simplified/index.js&quot;
		}
	}
	&lt;/script&gt;
	&lt;script type=&quot;module&quot; src=&quot;https://unpkg.com/counter-wc@latest&quot;&gt;&lt;/script&gt;
	&lt;/head&gt;
	&lt;body&gt;
		<span class="hljs-subst">${<span class="hljs-keyword">await</span> renderToString(jsx(CustomCounter, { start: <span class="hljs-number">10</span> }))}</span>
	&lt;/body&gt;
&lt;/html&gt;
`</span>;

<span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(html);
</code></pre><p>Then run <code>bun run ssr.js</code> and you will see the HTML with the rendered web component using the Declarative Shadow DOM.</p>
<h2 id="tell-me-more-about-brisa-please">Tell me more about Brisa... Please...</h2>
<p>The integration of these Web Component libraries with Brisa is done through a configuration file:</p>
<pre><code class="hljs language-tsx"><span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> { <span class="hljs-title class_">WebComponentIntegrations</span> } <span class="hljs-keyword">from</span> <span class="hljs-string">&quot;brisa&quot;</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
  <span class="hljs-string">&quot;custom-counter&quot;</span>: {
    <span class="hljs-attr">client</span>: <span class="hljs-string">&quot;./path/to/web-component.client.js&quot;</span>,
    <span class="hljs-attr">server</span>: <span class="hljs-string">&quot;./path/to/web-component.server.js&quot;</span>,
    <span class="hljs-attr">types</span>: <span class="hljs-string">&quot;./path/to/web-component.types.d.ts&quot;</span>,
  },
} <span class="hljs-keyword">satisfies</span> <span class="hljs-title class_">WebComponentIntegrations</span>;
</code></pre><p>In this way, SSR and TypeScript types are automatically integrated into your project. And you can use the Web Component in any Server Component or within another Web Component.</p>
<img src="https://aralroca.com/images/blog-images/types.gif" alt="Typescript types" />

<p>If you are interested in knowing more, I invite you to subscribe to the <a href="https://brisa.build">Brisa newsletter</a> to receive the latest news and updates on Brisa. We estimate that by the end of September it will be ready for launch.</p>
<h2 id="note-for-web-component-library-creators">Note for Web Component library creators</h2>
<p>We encourage you to try Brisa to create your own Web Component libraries. If you put the &quot;made with Brisa&quot; badge, we will put a link to your library on the Brisa page.</p>
<a href="https://brisa.build" target="_blank" rel="noopener noreferrer">
  <img width="150" height="42" src="https://brisa.build/images/brisa_badge.svg" alt="Made with Brisa" />
</a>

<pre><code class="hljs language-html"><span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">&quot;https://brisa.build&quot;</span> <span class="hljs-attr">target</span>=<span class="hljs-string">&quot;_blank&quot;</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">&quot;noopener noreferrer&quot;</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">img</span>
    <span class="hljs-attr">width</span>=<span class="hljs-string">&quot;150&quot;</span>
    <span class="hljs-attr">height</span>=<span class="hljs-string">&quot;42&quot;</span>
    <span class="hljs-attr">src</span>=<span class="hljs-string">&quot;https://brisa.build/images/brisa_badge.svg&quot;</span>
    <span class="hljs-attr">alt</span>=<span class="hljs-string">&quot;Made with Brisa&quot;</span>
  /&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
</code></pre><h2 id="example">Example</h2>
<p>If you want to see the GitHub repository of the example of the counter that we have explained in this article, you can take a look and use it as a reference for your own creations:</p>
<ul>
<li><a href="https://github.com/aralroca/counter-wc">https://github.com/aralroca/counter-wc</a></li>
</ul>
<h2 id="conclusion">Conclusion</h2>
<p>In this post, we have seen how to build reactive Web Components that work with SSR and with any JavaScript framework or Vanilla JS. We have used Brisa to build the Web Component and we have seen how to load it in a Vanilla JS project and how to do SSR with it.</p>
<p>I hope you have enjoyed this post and that you have learned something new. If you have any questions, do not hesitate to ask me in the comments below. I will be happy to help you.</p>
<p>Happy coding and enjoy the rest of the summer! 🌞🌴</p>
<figure align="center">
<img width="400" height="400" src="https://aralroca.com/images/blog-images/brisabeach.png" alt="Summer" class="center"/>
<figcaption><small>Enjoy the rest of the summer!</small></figcaption>
]]></content:encoded>
            </item>
            <item>
              <title>Server Actions have been fixed</title>
              <description>Server Actions emerged as an idea to reduce client code and simplifying the interactions that require communication with the server. It is an excellent solution that allows developers to write less code. However, there are several challenges associated with its implementation in other frameworks, which should not be overlooked.</description>
              <link>https://aralroca.com/blog/server-actions-have-been-fixed</link>
              <guid isPermaLink="false">https://aralroca.com/blog/server-actions-have-been-fixed/</guid>
              <pubDate>Sun Oct 20 2024 00:00:00 GMT+0000 (Coordinated Universal Time)</pubDate>
              <content:encoded><![CDATA[<p><a href="https://brisa.build/building-your-application/data-management/server-actions">Server Actions</a> emerged as an idea to <strong>reduce client code</strong> and <strong>simplifying the interactions</strong> that require communication with the server. It is an excellent solution that allows developers to write less code. However, there are several challenges associated with its implementation in other frameworks, which should not be overlooked.</p>
<p>In this article we will talk about these problems and how in <a href="https://brisa.build"><strong>Brisa</strong></a> we have found a solution.</p>
<h2 id="why-the-need-for-server-actions">Why the need for Server Actions?</h2>
<p>To understand what Server Actions provide, it is useful to review how communication with the server used to be. You are probably used to performing the following actions for each interaction with the server:</p>
<ol>
<li>Capture a browser event <em>(Client)</em></li>
<li>Normalize and serialize data <em>(Client)</em></li>
<li>Make a request to the server <em>(Client)</em></li>
<li>Process the request in an endpoint API <em>(Server)</em></li>
<li>Respond with the necessary data <em>(Server)</em></li>
<li>Wait for the response from the server and process it <em>(Client)</em></li>
<li>Update the data on the client and render the changes <em>(Client)</em></li>
</ol>
<p>These seven actions are <strong>repeated for each interaction</strong>. For example, if you have a page with 10 different interactions, you will repeat a very similar code 10 times, changing only details such as the type of request, the URL, the data sent and the status of the customer.</p>
<p>A familiar example would be
a:</p>
<pre><code class="hljs language-tsx">&lt;input
  onInput={<span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> {
    <span class="hljs-comment">// debounce</span>
    <span class="hljs-keyword">if</span> (timeout) {
      <span class="hljs-built_in">clearTimeout</span>(timeout);
    }
    timeout = <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =&gt;</span> {
      <span class="hljs-title function_">fetch</span>(<span class="hljs-string">&quot;/api/search&quot;</span>, {
        <span class="hljs-attr">method</span>: <span class="hljs-string">&quot;POST&quot;</span>,
        <span class="hljs-attr">body</span>: <span class="hljs-title class_">JSON</span>.<span class="hljs-title function_">stringify</span>({ <span class="hljs-attr">query</span>: e.<span class="hljs-property">target</span>.<span class="hljs-property">value</span> }),
      })
        .<span class="hljs-title function_">then</span>(<span class="hljs-function">(<span class="hljs-params">res</span>) =&gt;</span> res.<span class="hljs-title function_">json</span>())
        .<span class="hljs-title function_">then</span>(<span class="hljs-function">(<span class="hljs-params">data</span>) =&gt;</span> {
          <span class="hljs-title function_">setState</span>({ data });
        });
    }, <span class="hljs-number">300</span>);
  }}
/&gt;
</code></pre><p>And in the server:</p>
<pre><code class="hljs language-js">app.<span class="hljs-title function_">post</span>(<span class="hljs-string">&quot;/api/search&quot;</span>, <span class="hljs-title function_">async</span> (req, res) =&gt; {
  <span class="hljs-keyword">const</span> { query } = req.<span class="hljs-property">body</span>;
  <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> <span class="hljs-title function_">search</span>(query);
  res.<span class="hljs-title function_">json</span>(data);
});
</code></pre><p>Increasing the client bundle size... and the frustration of developers.</p>
<figure align="center">
  <img class="center" src="https://aralroca.com/images/blog-images/frustrated.jpeg" alt="Developer frustrated" />
  <figcaption><small>Frustrated Developer</small></figcaption>
</figure>

<h2 id="how-server-actions-work">How Server Actions work</h2>
<p>Server Actions <strong>encapsulate</strong> these actions in a <strong>Remote Procedure Call (RPC)</strong>, which manages the client-server communication, reducing the code on the client and centralizing the logic on the server:</p>
<ol>
<li>Capture a browser event <em>(RPC Client)</em></li>
<li>Normalize and serialize data <em>(RPC Client)</em></li>
<li>Make a request to the RPC server <em>(RPC Client)</em></li>
<li>Execute the action on the server with the data <em>(RPC Server)</em></li>
<li>Option 1:</li>
</ol>
<ul>
<li>Render from the server and send streaming to the client <em>(RPC Server)</em></li>
<li>Process the chunks of the stream so that the changes are visible <em>(RPC Client)</em></li>
</ul>
<ol start="6">
<li>Option 2:</li>
</ol>
<ul>
<li>Reply with the necessary data and transfer properties from the server store to the client store <em>(RPC Server)</em></li>
<li>Make the signals that were listening to the changes react to the changes in the store <em>(RPC Client)</em></li>
</ul>
<p>Here everything is done for you by the Brisa RPC.</p>
<figure align="center">
  <img class="center" src="https://aralroca.com/images/blog-images/rpc.jpeg" alt="RPC" />
  <figcaption><small>Remote Procedure Call</small></figcaption>
</figure>

<p>This would be the code from a <strong>server component</strong>:</p>
<pre><code class="hljs language-tsx">&lt;input
  debounceInput={<span class="hljs-number">300</span>}
  onInput={<span class="hljs-title function_">async</span> (e) =&gt; {
    <span class="hljs-comment">// All this code only runs on the server</span>
    <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> <span class="hljs-title function_">search</span>(e.<span class="hljs-property">target</span>.<span class="hljs-property">value</span>);
    store.<span class="hljs-title function_">set</span>(<span class="hljs-string">&quot;query&quot;</span>, data);
    store.<span class="hljs-title function_">transferToClient</span>([<span class="hljs-string">&quot;query&quot;</span>]);
  }}
/&gt;
</code></pre><p>Here, developers do not write client code, since it is a server component. The <code>onInput</code> event is received after the debounce, handled by the Client RPC, while the Server RPC uses &quot;Action Signals&quot; to trigger the Web Components that have signals registered with that store property.</p>
<p>As you can see, this significantly reduces the server code and, best of all, the code size on the client does not increase with each interaction. The RPC Client code occupies a fixed 2 KB, whether you have 10 or 1000 such interactions. This means that <strong>increase 0 bytes</strong> in the client bundle size, with other words, doesn&#39;t increase.</p>
<figure align="center">
  <img class="center" src="https://aralroca.com/images/blog-images/0-bytes.jpeg" alt="0 bytes" />
  <figcaption><small>+0 bytes on client bundle size</small></figcaption>
</figure>

<p>Moreover, in the case of needing a rerender, this is done on the server and is returned in HTML streaming, making the user see the changes much earlier than in the traditional way where you had to do this work on the client after the server response.</p>
<p>In this way:</p>
<ul>
<li><strong>Improve</strong> the user experience (<strong>UX</strong>)</li>
<li><strong>Improve</strong> the development experience (<strong>DX</strong>)</li>
</ul>
<figure align="center">
  <img class="center" src="https://aralroca.com/images/blog-images/happy.jpeg" alt="Developer happy" />
  <figcaption><small>Happy Developer</small></figcaption>
</figure>

<h2 id="differences-between-brisa-server-actions-and-other-frameworks">Differences between Brisa Server Actions and other frameworks</h2>
<h3 id="1-numbers-of-events-to-capture">1. Numbers of events to capture</h3>
<p>In other frameworks such as React, they have focused on actions <strong>only</strong> being part of the <strong>form <code>onSubmit</code></strong>, instead of any event.</p>
<p>This is a problem, since there are many non-form events that should also be handled from a server component without adding client code. For example, an <strong><code>onInput</code></strong> of an input to do <strong>automatic suggestions</strong>, an <strong><code>onScroll</code></strong> to load an <strong>infinite scroll</strong>, an <strong><code>onMouseOver</code></strong> to do a <strong>hover</strong>, etc.</p>
<figure align="center">
  <img class="center" src="https://aralroca.com/images/blog-images/interactivity.jpeg" alt="Interactivity" />
  <figcaption><small>Applications are more interactive than expected</small></figcaption>
</figure>

<h3 id="2-having-more-html-controls-over-server-actions">2. Having more HTML controls over Server Actions</h3>
<p>Many frameworks have also seen the HTMX library as a very different alternative to server actions, when in fact it has brought very good ideas that can be combined with Server Actions to have more potential by simply adding extra attributes in the HTML that the RPC Client can take into account, such as the <code>debounceInput</code> that we have seen before. Also other HTMX ideas like the <code>indicator</code> to show a spinner while making the request, or being able to handle an error in the RPC Client.</p>
<figure align="center">
  <img class="center" src="https://aralroca.com/images/blog-images/htmx.jpg" alt="HTMX Ideas" />
  <figcaption><small>HTMX ideas</small></figcaption>
</figure>

<h3 id="3-separation-of-concerns">3. Separation of concerns</h3>
<p>When Server Actions were introduced in <strong>React</strong>, there was a <strong>new paradigm shift</strong> that many developers had to change the mental chip when working with them.</p>
<p>We wanted to make it as <strong>familiar as possible to the Web Platform</strong>, this way, you can capture the serialized event from the server and use its properties. The only event a little different is the <code>onSubmit</code> that has already transferred the <code>FormData</code> and has the <code>e.formData</code> property, nevertheless, the rest of <strong>event properties</strong> are interactable. This is an example <strong>resetting a form</strong>:</p>
<pre><code class="hljs language-tsx"><span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> { <span class="hljs-title class_">RequestContext</span> } <span class="hljs-keyword">from</span> <span class="hljs-string">&quot;brisa&quot;</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">FormOnServer</span>(<span class="hljs-params">{}, { indicate }: <span class="hljs-title class_">RequestContext</span></span>) {
  <span class="hljs-keyword">const</span> pending = <span class="hljs-title function_">indicate</span>(<span class="hljs-string">&quot;action-name&quot;</span>);

  <span class="hljs-keyword">return</span> (
    <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">form</span>
      <span class="hljs-attr">indicateSubmit</span>=<span class="hljs-string">{pending}</span>
      <span class="hljs-attr">onSubmit</span>=<span class="hljs-string">{(e)</span> =&gt;</span> {
        // This code runs on the server
        console.log(&quot;Username:&quot;, e.formData.get(&quot;username&quot;));
        e.target.reset(); // Tell to the RPC client to reset the form
      }}
    &gt;
      <span class="hljs-tag">&lt;<span class="hljs-name">label</span>&gt;</span>
        Username:
        <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">&quot;text&quot;</span> <span class="hljs-attr">name</span>=<span class="hljs-string">&quot;username&quot;</span> /&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">br</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">indicator</span>=<span class="hljs-string">{pending}</span> <span class="hljs-attr">type</span>=<span class="hljs-string">&quot;submit&quot;</span>&gt;</span>
        Submit
      <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span></span>
  );
}
</code></pre><p>In this example, there is no client code at all and during the server action you can <strong>disable the submit button</strong> with the <code>indicator</code>, using CSS, so that the form cannot be submitted twice, and at the same time after doing the action on the server and <strong>access the form data</strong> with <code>e.formData</code> and then <strong>resetting the form</strong> using the same API of the event.</p>
<p>Mentally, it is very <strong>similar</strong> to working with the <strong>Web Platform</strong>. The only difference is that all the events of all the server components are server actions.</p>
<p>This way, there is a real separation of concerns, where it is <strong>NOT necessary</strong> to put <strong><code>&quot;user server&quot;</code></strong> or <strong><code>&quot;use client&quot;</code></strong> in your components <strong>anymore</strong>.</p>
<p>Just keep in mind that <strong>everything runs only on the server</strong>. The only <strong>exception</strong> is for the <strong><code>src/web-components</code></strong> folder which runs on the <strong>client</strong> and there the <strong>events are normal</strong>.</p>
<figure align="center">
  <img class="center" src="https://aralroca.com/images/blog-images/separation-concers.jpeg" alt="Two different worlds, but in agreement" />
  <figcaption><small>Two different worlds, but in agreement</small></figcaption>
</figure>

<h3 id="4-event-propagation">4. Event Propagation</h3>
<p>In Brisa, the Server Actions are propagated between Server Components as if they were DOM events. That is to say, from a Server Action you can call an event of a prop of a Server Component and then the Server Action of the parent Server Component is executed, etc.</p>
<pre><code class="hljs language-tsx"><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">Example</span>(<span class="hljs-params">{ onAfterMyAction }</span>) {
  <span class="hljs-keyword">return</span> (
    <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">ChildComponent</span>
      <span class="hljs-attr">indicateSubmit</span>=<span class="hljs-string">{pending}</span>
      <span class="hljs-attr">onSubmit</span>=<span class="hljs-string">{(e)</span> =&gt;</span> {
        const username = e.formData.get(&quot;username&quot;);
        /* Process data */
        onAfterMyAction(username); // call server component prop
        e.target.reset();
      }}
    /&gt;</span>
  );
}
</code></pre><p>In this case, the <code>onAfterMyAction</code> event is executed on the parent component and an action can be done on the server. This is very useful to make actions on the server that effect <strong>several server components</strong>.</p>
<figure align="center">
  <img class="center" src="https://aralroca.com/images/blog-images/after-my-action.jpg" alt="Propagate action" />
  <figcaption><small>Propagate action</small></figcaption>
</figure>

<h3 id="4-comunication-between-both-worlds">4. Comunication between both worlds</h3>
<p>Especially after the last few weeks Web Components have been a bit frowned upon after several discussions on X (formelly Twitter). However, being <strong>part of the HTML</strong>, it is the <strong>best way</strong> to <strong>interact with Server Actions</strong> for several reasons:</p>
<ol>
<li>You can <strong>capture</strong> any <strong>Web Component event</strong> from the <strong>server</strong> and generate client-server communication. Example <code>&lt;web-component onEvent={serverAction} /&gt;</code>. This is very powerful, since all the events inside the Web Component is only client logic without putting any server logic there, simply from the server when consuming the Web Component you can do server actions.</li>
<li>The <strong>HTTP protocol</strong> can be used for what it was designed for, to <strong>transfer Hypertext</strong> (HTML) in <strong>streaming</strong>, this way if after a re-rendering from a Server Action any attribute of a Web Component is updated, the diffing algorithm of the RPC Client makes the Web Component to be updated without much effort. The Web Components <strong>attributes</strong> in Brisa <strong>are signals</strong> that make the internal parts of Web Component react without having to rerender. This process in other frameworks becomes very complicated, making the RPC server have to process JSON or JS over the wire, instead of HTML, which makes the streaming implementation more complicated.</li>
</ol>
<p>Using attributes in Web Components requires serialization in the same way as transmitting data from server to client without using Web Components, therefore, using both, there is <strong>no extra serialization</strong> to manage.</p>
<p><em><strong>Note:</strong> Streaming HTML and processing it with the diffing algorithm is something I explained in this other <a href="https://aralroca.com/blog/html-streaming-over-the-wire">article</a> if you are interested.</em></p>
<figure align="center">
  <img class="center" src="https://aralroca.com/images/blog-images/hypertext-over-the-wire.jpeg" alt="Hypertext in streaming over the wire" />
  <figcaption><small>Hypertext in streaming over the wire</small></figcaption>
</figure>

<h3 id="5-new-concept-action-signals">5. New concept: Action Signals</h3>
<p>In Brisa, we have added a new concept to give even more power to the Server Actions, this concept is called <a href="https://brisa.build/building-your-application/data-management/server-actions#action-signals"><strong>&quot;Action Signals&quot;</strong></a>. The idea of the &quot;Action Signals&quot; is that you have <strong>2 stores</strong>, one on the <strong>server</strong> and one on the <strong>client</strong>.</p>
<p><strong>Why 2 stores?</strong></p>
<p>The default <strong>server store</strong> <strong>lives</strong> only at the <strong>request level</strong>. And you can <strong>share data</strong> that will <strong>not be visible</strong> to the <strong>client</strong>. For example you can have the middleware set the user and have access to sensitive user data in any Server Component. By living at request level it is impossible to have conflicts between different requests, since <strong>each request</strong> has its <strong>own store</strong> and is <strong>NOT stored in any database</strong>, when the request is finished, it dies by default.</p>
<p>On the other hand, in the <strong>client store</strong>, it is a store that <strong>each property</strong> when consumed is a <a href="https://brisa.build/building-your-application/components-details/reactivity"><strong>signal</strong></a>, that is to say, if it is updated, the Web Component that was listening to that signal reacts.</p>
<p>However, the new concept of <strong>&quot;Action Signal&quot;</strong> is that we can <strong>extend the life of the server store beyond the request</strong>. To do this it is necessary to use this code:</p>
<pre><code class="hljs language-tsx">store.<span class="hljs-title function_">transferToClient</span>([<span class="hljs-string">&quot;some-key&quot;</span>]);
</code></pre><p>This <a href="https://brisa.build/api-reference/components/request-context#transfertoclient"><code>transferToClient</code></a> method, <strong>share server data</strong> to the <strong>client store</strong> and converted into signals. In this way, many times it will not be necessary to make <strong>any re-rendering</strong> from the server, you can simply from a Server Action make react the signals of the Web Components that were listening to that signal.</p>
<p>This store transfer makes the <strong>life of the server store</strong> now:</p>
<p><em>Render initial Server Component → Client → Server Action → Client → Server Action...</em></p>
<p>So it goes from living from only at request level to <strong>live permanently</strong>, compatible with navigation between pages.</p>
<figure align="center">
  <img class="center" src="https://aralroca.com/images/blog-images/share-data.jpg" alt="Share data between both worlds" />
  <figcaption><small>Share data between both worlds (server/client)</small></figcaption>
</figure>

<p>Example:</p>
<pre><code class="hljs language-tsx"><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">Form</span>(<span class="hljs-params">{}, { store }: <span class="hljs-title class_">RequestContext</span></span>) {
  <span class="hljs-keyword">const</span> errors = store.<span class="hljs-title function_">get</span>(<span class="hljs-string">&quot;errors&quot;</span>);

  <span class="hljs-comment">// You extend the life of the store from request-time:</span>
  <span class="hljs-comment">//  render (server) → 💀</span>
  <span class="hljs-comment">// to:</span>
  <span class="hljs-comment">//  render (server) → client → action (server) → rerender (server) → client → ...</span>
  store.<span class="hljs-title function_">transferToClient</span>([<span class="hljs-string">&quot;errors&quot;</span>]);

  <span class="hljs-keyword">return</span> (
    <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">form</span>
      <span class="hljs-attr">onSubmit</span>=<span class="hljs-string">{(e)</span> =&gt;</span> {
        const email = e.formData.get(&quot;email&quot;);
        const result = schema.safeParse({ email });

        store.set(&quot;errors&quot;, result.success ? null : result.error.format());

        // rerenderInAction is used to make the server components reactively react
        // to the store change as well. If rerenderInAction is not used, only the
        // web components that are listening to the store.get(&#x27;errors&#x27;) signal
        // react to the changes.
        rerenderInAction({ type: &quot;targetComponent&quot; });
      }}
    &gt;
      <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">name</span>=<span class="hljs-string">&quot;email&quot;</span> <span class="hljs-attr">type</span>=<span class="hljs-string">&quot;text&quot;</span> /&gt;</span>
      {errors?.email &amp;&amp; <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{errors.email._errors.toString()}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>}
      <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">type</span>=<span class="hljs-string">&quot;submit&quot;</span>&gt;</span>Submit<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span></span>
  );
}
</code></pre><p>In this example, we extend the life of the <code>errors</code> store property, not to be used on the client, but to be reused in the Server Action and then finally in the rerender of the Server Action. In this case, being a non-sensitive data, it is not necessary to encrypt it. This example code all happens on the server, even the rerendering and the user will see the errors after this rendering on the server where the Server RPC will send the HTML chunks in streaming and the Client RPC will process it to make the diffing and show the errors to give feedback to the user.</p>
<h3 id="6-encrypt-only-the-sensitive-data">6. Encrypt only the sensitive data</h3>
<p>If within a server action some variable is used that existed at render level, at security level many frameworks like <a href="https://nextjs.org/blog/security-nextjs-server-components-actions#closures"><strong>Next.js 14</strong></a> what they do is to encrypt this data to create an snapshot of data used at the time of rendering. This is more or less fine, but <strong>encrypting data always</strong> has an associated <strong>computational cost</strong> and it is not always sensitive data.</p>
<p>In Brisa, to solve this, there are different requests, where in the initial render it has a value, and in the server action you can capture the value that it has in this request.</p>
<pre><code class="hljs language-tsx"><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">Page</span>(<span class="hljs-params"></span>) {
  <span class="hljs-keyword">const</span> foo = <span class="hljs-string">&quot;bar&quot;</span>;

  <span class="hljs-keyword">function</span> <span class="hljs-title function_">onServerAction</span>(<span class="hljs-params"></span>) {
    <span class="hljs-keyword">if</span> (foo === <span class="hljs-string">&quot;bar&quot;</span>) {
      <span class="hljs-comment">// It works without transferring &quot;foo&quot; to the client</span>
    }
  }

  <span class="hljs-keyword">return</span> <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{onServerAction}</span>&gt;</span>Click<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span></span>;
}
</code></pre><p>This is useful in some cases but not always, for example if you do a <code>Math.random</code> it will be different between the initial render and the Server Action execution for sure.</p>
<pre><code class="hljs language-tsx"><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">Page</span>(<span class="hljs-params"></span>) {
  <span class="hljs-keyword">const</span> value = <span class="hljs-title class_">Math</span>.<span class="hljs-title function_">random</span>();
  <span class="hljs-comment">// Ex: 0.123456789 (Initial render) - 0.987654321 (Server Action)</span>

  <span class="hljs-keyword">function</span> <span class="hljs-title function_">onServerAction</span>(<span class="hljs-params"></span>) {
    <span class="hljs-keyword">if</span> (value === <span class="hljs-number">0.123456789</span>) {
      <span class="hljs-comment">// 😕 Ups! It&#x27;s wrong condition</span>
    }
  }

  <span class="hljs-keyword">return</span> <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">web-component</span> <span class="hljs-attr">onAction</span>=<span class="hljs-string">{onServerAction}</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">web-component</span>&gt;</span></span>;
}
</code></pre><p>This is why we created the concept of <strong>&quot;Action Signals&quot;</strong>, to <strong>transfer data</strong> from the <strong>server store</strong> to the <strong>client store</strong>, and the <strong>developer</strong> can <strong>decide</strong> whether to <strong>encrypt</strong> it <strong>or not</strong> at will.</p>
<p>Sometimes, instead of querying the database from the Server Action, you may want to transfer data that already exists in the initial render even if it requires an associated encryption. To do this, you simply use:</p>
<pre><code class="hljs language-js">store.<span class="hljs-title function_">transferToClient</span>([<span class="hljs-string">&quot;some-key&quot;</span>], { <span class="hljs-attr">encrypt</span>: <span class="hljs-literal">true</span> });
</code></pre><p>When you do:</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">const</span> value = store.<span class="hljs-title function_">get</span>(<span class="hljs-string">&quot;some-key&quot;</span>);
</code></pre><p>Inside a Web Component (client) will always be encrypted, but on the server it will always be decrypted.</p>
<p><em><strong>Note</strong>: Brisa uses aes-256-cbc for encryption, a combination of cryptographic algorithms used to securely encrypt information recommended by OpenSSL. Encryption keys are generated during the build of your project.</em></p>
<figure align="center">
  <img class="center" src="https://aralroca.com/images/blog-images/encrypt.jpg" alt="Share encrypted data between both worlds" />
  <figcaption><small>Share encrypted data between both worlds (server/client)</small></figcaption>
</figure>

<h2 id="conclusion">Conclusion</h2>
<p>In Brisa, although we like to support writing Web Components easily, the goal is to be able to make a SPA without client code and only use Web Components when it is a purely client interaction or the Web API has to be touched. That&#39;s why Server Actions are so important, as they allow interactions with the server without having to write client code.</p>
<p>We encourage you to <a href="https://brisa.build/getting-started/quick-start">try Brisa</a>, you just have to run this command in the terminal: <code>bun create brisa</code>, or try some <a href="https://brisa.build/examples">example</a> to see how it works.</p>
<h2 id="references">References</h2>
<ul>
<li><a href="https://brisa.build/building-your-application/data-management/server-actions#convention">Server Actions Convetion</a></li>
<li><a href="https://brisa.build/building-your-application/data-management/server-actions#behavior">Server Actions Behavior</a></li>
<li><a href="https://brisa.build/building-your-application/data-management/server-actions#forms">Forms with Server Actions</a></li>
<li><a href="https://brisa.build/building-your-application/data-management/server-actions#nested-actions">Nested Actions</a></li>
<li><a href="https://brisa.build/building-your-application/data-management/server-actions#server-side-validation-and-error-handling">Server-side validation and error handling</a></li>
<li><a href="https://brisa.build/building-your-application/data-management/server-actions#debounce">Debounce a Server Action</a></li>
<li><a href="https://brisa.build/building-your-application/data-management/server-actions#optimistic-updates">Optimistic Updates</a></li>
<li><a href="https://brisa.build/building-your-application/data-management/server-actions#rerenderinaction">Re-render in Action</a></li>
<li><a href="https://brisa.build/building-your-application/data-management/server-actions#navigate">Navigate to another page with Server Actions</a></li>
<li><a href="https://brisa.build/building-your-application/data-management/server-actions#cookies">Access to Cookies</a></li>
<li><a href="https://brisa.build/building-your-application/data-management/server-actions#security">Security in Server Actions</a></li>
<li><a href="https://brisa.build/building-your-application/data-management/server-actions#action-signals">Action Signals</a></li>
<li><a href="https://brisa.build/building-your-application/data-management/server-actions#transfer-sensitive-data">Transfer sensitive data</a></li>
<li><a href="https://brisa.build/building-your-application/data-management/server-actions#props-in-server-actions">Props in Server Actions</a></li>
<li><a href="https://brisa.build/building-your-application/data-management/server-actions#using-server-actions-in-a-reverse-proxy">Using Server Actions in a Reverse Proxy</a></li>
</ul>
]]></content:encoded>
            </item>
            <item>
              <title>SPA-Like Navigation Preserving Web Component State</title>
              <description>Learn how to keep your web components' state intact while navigating, just like in a SPA!</description>
              <link>https://aralroca.com/blog/spa-navigation</link>
              <guid isPermaLink="false">https://aralroca.com/blog/spa-navigation/</guid>
              <pubDate>Thu Apr 25 2024 00:00:00 GMT+0000 (Coordinated Universal Time)</pubDate>
              <content:encoded><![CDATA[<p>In this third and final article in the series on HTML Streaming, we will explore the practical implementation of the <a href="https://github.com/aralroca/diff-dom-streaming"><strong>Diff DOM Streaming</strong></a> library in web browsing. This approach will allow <strong>any website</strong> using <strong>web components</strong> to <strong>retain its state during browsing</strong>. We will discuss in detail how to achieve this step by step using <strong>VanillaJS</strong> and <a href="https://bun.sh/"><strong>Bun</strong></a>.</p>
<p>It is important to note that it is not necessary to have a complex server, as we can use static files. However, we will also explore how this technique works with HTML streaming and we will stream from Bun.</p>
<h2 id="table-of-contents">Table of Contents</h2>
<ul>
<li><a href="#1-creating-hello-world-page">1. Creating &quot;Hello World&quot; page</a></li>
<li><a href="#2-adding-more-than-one-page">2. Adding more than one page</a></li>
<li><a href="#3-adding-a-web-component">3. Adding a Web Component</a></li>
<li><a href="#4-maintaining-the-state-during-navigation">4. Maintaining the state during navigation</a></li>
<li><a href="#5-manage-new-scripts-of-the-page-to-be-navigated-to">5. Manage new scripts of the page to be navigated to</a></li>
<li><a href="#6-working-with-streaming-and-suspense">6. Working with streaming and suspense</a></li>
<li><a href="#7-transitions-between-pages-view-transition-api">7. Transitions between pages (View Transition API)</a></li>
<li><a href="#8-caching-the-navigation">8. Caching the navigation</a></li>
<li><a href="#9-prefetch-the-navigation">9. Prefetch the navigation</a></li>
<li><a href="#10-pros-and-cons-of-using-diff-dom-streaming-for-web-navigation">10. Pros and Cons of Using Diff DOM Streaming for Web Navigation</a></li>
<li><a href="#final-conclusions">Final conclusions</a></li>
<li><a href="#references">References</a></li>
</ul>
<h2 id="1-creating-hello-world-page">1. Creating &quot;Hello World&quot; page</h2>
<p>The first thing we are going to do is to create the home page with a &quot;Hello World&quot; with Bun:</p>
<pre><code class="hljs language-ts"><span class="hljs-keyword">const</span> server = <span class="hljs-title class_">Bun</span>.<span class="hljs-title function_">serve</span>({
  <span class="hljs-attr">port</span>: <span class="hljs-number">1234</span>,
  <span class="hljs-title function_">fetch</span>(<span class="hljs-params"></span>) {
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Response</span>(
      <span class="hljs-string">`
      &lt;html&gt;
        &lt;head&gt;
          &lt;title&gt;Hello, World!&lt;/title&gt;
        &lt;/head&gt;
        &lt;body&gt;
          &lt;h1&gt;Hello, World!&lt;/h1&gt;
        &lt;/body&gt;
      &lt;/html&gt;
    `</span>,
      {
        <span class="hljs-attr">headers</span>: {
          <span class="hljs-string">&#x27;Content-Type&#x27;</span>: <span class="hljs-string">&#x27;text/html&#x27;</span>,
        },
      }
    )
  },
})

<span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">`Done! http://<span class="hljs-subst">${server.hostname}</span>:<span class="hljs-subst">${server.port}</span>`</span>)
</code></pre><p>Now if we run it with:</p>
<pre><code class="hljs language-sh">bun run index.ts
</code></pre><p>And we open <a href="http://localhost:1234">http://localhost:1234</a> We will see our page with an <code>h1</code> that says <code>Hello, World!</code>. Ok, all good.</p>
<figure align="center">
  <img src="https://aralroca.com/images/blog-images/hello-world.png" width="300px" height="122px" alt="Hello World with Bun" class="center" />
  <figcaption><small>Hello World with Bun</small></figcaption>
</figure>

<h2 id="2-adding-more-than-one-page">2. Adding more than one page</h2>
<p>Let&#39;s create the page <code>/foo</code> and the page <code>/bar</code> and instead of &quot;Hello World&quot; put &quot;Foo&quot; and &quot;Bar&quot;.</p>
<p>To do this, we must get the <code>pathname</code> from the url of the <code>request</code>:</p>
<pre><code class="hljs language-ts"><span class="hljs-comment">// Hardcoded examples of pathname-dependent example</span>
<span class="hljs-keyword">const</span> names = {
  <span class="hljs-string">&#x27;/foo&#x27;</span>: <span class="hljs-string">&#x27;Foo&#x27;</span>,
  <span class="hljs-string">&#x27;/bar&#x27;</span>: <span class="hljs-string">&#x27;Bar&#x27;</span>,
}

<span class="hljs-keyword">const</span> server = <span class="hljs-title class_">Bun</span>.<span class="hljs-title function_">serve</span>({
  <span class="hljs-attr">port</span>: <span class="hljs-number">1234</span>,
  <span class="hljs-title function_">fetch</span>(<span class="hljs-params"><span class="hljs-attr">req</span>: <span class="hljs-title class_">Request</span></span>) {
    <span class="hljs-comment">// request URL:</span>
    <span class="hljs-keyword">const</span> url = <span class="hljs-keyword">new</span> <span class="hljs-title function_">URL</span>(req.<span class="hljs-property">url</span>)
    <span class="hljs-comment">// Getting the hardcoded name via the pathname</span>
    <span class="hljs-keyword">const</span> name = names[url.<span class="hljs-property">pathname</span>] ?? <span class="hljs-string">&#x27;Hello, World&#x27;</span>

    <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Response</span>(
      <span class="hljs-string">`
      &lt;html&gt;
        &lt;head&gt;
          &lt;title&gt;Hello, World!&lt;/title&gt;
        &lt;/head&gt;
        &lt;body&gt;
          &lt;nav&gt;
            &lt;a href=&quot;/&quot;&gt;Home&lt;/a&gt;
            &lt;a href=&quot;/foo&quot;&gt;Foo&lt;/a&gt;
            &lt;a href=&quot;/bar&quot;&gt;Bar&lt;/a&gt;
          &lt;/nav&gt;
          &lt;h1&gt;<span class="hljs-subst">${name}</span>!&lt;/h1&gt;
        &lt;/body&gt;
      &lt;/html&gt;
    `</span>,
      {
        <span class="hljs-attr">headers</span>: {
          <span class="hljs-string">&#x27;Content-Type&#x27;</span>: <span class="hljs-string">&#x27;text/html&#x27;</span>,
        },
      }
    )
  },
})

<span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">`Done! http://<span class="hljs-subst">${server.hostname}</span>:<span class="hljs-subst">${server.port}</span>`</span>)
</code></pre><p>If we run it and go to <a href="http://localhost:1234/foo">http://localhost:1234/foo</a> we will see our page with an <code>h1</code> that says <code>Foo!</code>, and if we go to <a href="http://localhost:1234/foo">http://localhost:1234/bar</a> we will see our page with an <code>h1</code> that says <code>Bar!</code>. Ok, all good.</p>
<p>In addition, we have added three <code>a</code> elements to navigate between pages 👌</p>
<pre><code class="hljs language-html"><span class="hljs-tag">&lt;<span class="hljs-name">nav</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">&quot;/&quot;</span>&gt;</span>Home<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">&quot;/foo&quot;</span>&gt;</span>Foo<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">&quot;/bar&quot;</span>&gt;</span>Bar<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">nav</span>&gt;</span>
</code></pre><p>For now, there is nothing special about this type of navigation, it is the default navigation of the browsers.</p>
<figure align="center">
  <img src="https://aralroca.com/images/blog-images/hello-world-mpa.webp" width="500px" height="216px" alt="Hello World using MPA with Bun" class="center" />
  <figcaption><small>Hello World using MPA with Bun</small></figcaption>
</figure>

<h2 id="3-adding-a-web-component">3. Adding a Web Component</h2>
<p>For the example, we are going to add a web component with VanillaJS that acts as a counter, since it is a simple example to test a client interaction:</p>
<pre><code class="hljs language-ts"><span class="hljs-comment">// Counter Web Component</span>
<span class="hljs-keyword">class</span> <span class="hljs-title class_">CounterComponent</span> <span class="hljs-keyword">extends</span> <span class="hljs-title class_ inherited__">HTMLElement</span> {
  <span class="hljs-title function_">connectedCallback</span>(<span class="hljs-params"></span>) {
    <span class="hljs-keyword">const</span> shadowRoot = <span class="hljs-variable language_">this</span>.<span class="hljs-title function_">attachShadow</span>({ <span class="hljs-attr">mode</span>: <span class="hljs-string">&#x27;open&#x27;</span> })
    <span class="hljs-keyword">let</span> count = <span class="hljs-number">0</span>

    shadowRoot.<span class="hljs-property">innerHTML</span> = <span class="hljs-string">`
      &lt;button id=&quot;inc&quot;&gt;Increment&lt;/button&gt;
      &lt;button id=&quot;dec&quot;&gt;Decrement&lt;/button&gt;
      &lt;p id=&quot;count&quot;&gt;Count: <span class="hljs-subst">${count}</span>&lt;/p&gt;
    `</span>
    <span class="hljs-keyword">const</span> countEl = shadowRoot.<span class="hljs-title function_">querySelector</span>(<span class="hljs-string">&#x27;#count&#x27;</span>)
    shadowRoot.<span class="hljs-title function_">querySelector</span>(<span class="hljs-string">&#x27;#inc&#x27;</span>).<span class="hljs-title function_">addEventListener</span>(<span class="hljs-string">&#x27;click&#x27;</span>, <span class="hljs-function">() =&gt;</span> {
      count++
      countEl.<span class="hljs-property">textContent</span> = <span class="hljs-string">`Count: <span class="hljs-subst">${count}</span>`</span>
    })
    shadowRoot.<span class="hljs-title function_">querySelector</span>(<span class="hljs-string">&#x27;#dec&#x27;</span>).<span class="hljs-title function_">addEventListener</span>(<span class="hljs-string">&#x27;click&#x27;</span>, <span class="hljs-function">() =&gt;</span> {
      count--
      countEl.<span class="hljs-property">textContent</span> = <span class="hljs-string">`Count: <span class="hljs-subst">${count}</span>`</span>
    })
  }
}

<span class="hljs-comment">// Register Counter Web Component</span>
<span class="hljs-keyword">if</span> (!customElements.<span class="hljs-title function_">get</span>(<span class="hljs-string">&#x27;counter-component&#x27;</span>)) {
  customElements.<span class="hljs-title function_">define</span>(<span class="hljs-string">&#x27;counter-component&#x27;</span>, <span class="hljs-title class_">CounterComponent</span>)
}
</code></pre><p>To add it, after using the <a href="https://developer.mozilla.org/en-US/docs/Web/API/CustomElementRegistry/define">CustomElementRegistry</a>, we can consume it as another HTML element with <code>&lt;counter-component&gt;&lt;/counter-component&gt;</code>.</p>
<p>What we are going to do is to add it under the heading of each page:</p>
<pre><code class="hljs language-ts"><span class="hljs-keyword">import</span> path <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;node:path&#x27;</span>

<span class="hljs-keyword">const</span> names = {
  <span class="hljs-string">&#x27;/foo&#x27;</span>: <span class="hljs-string">&#x27;Foo&#x27;</span>,
  <span class="hljs-string">&#x27;/bar&#x27;</span>: <span class="hljs-string">&#x27;Bar&#x27;</span>,
}

<span class="hljs-keyword">const</span> server = <span class="hljs-title class_">Bun</span>.<span class="hljs-title function_">serve</span>({
  <span class="hljs-attr">port</span>: <span class="hljs-number">1234</span>,
  <span class="hljs-title function_">fetch</span>(<span class="hljs-params"><span class="hljs-attr">req</span>: <span class="hljs-title class_">Request</span></span>) {
    <span class="hljs-keyword">const</span> url = <span class="hljs-keyword">new</span> <span class="hljs-title function_">URL</span>(req.<span class="hljs-property">url</span>)
    <span class="hljs-keyword">const</span> name = names[url.<span class="hljs-property">pathname</span>] ?? <span class="hljs-string">&#x27;Hello, World&#x27;</span>

    <span class="hljs-keyword">if</span> (url.<span class="hljs-property">pathname</span> === <span class="hljs-string">&#x27;/code&#x27;</span>) {
      <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Response</span>(<span class="hljs-title class_">Bun</span>.<span class="hljs-title function_">file</span>(path.<span class="hljs-title function_">join</span>(<span class="hljs-keyword">import</span>.<span class="hljs-property">meta</span>.<span class="hljs-property">dir</span>, <span class="hljs-string">&#x27;code.js&#x27;</span>)))
    }

    <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Response</span>(
      <span class="hljs-string">`
      &lt;html&gt;
        &lt;head&gt;
          &lt;title&gt;Hello, World!&lt;/title&gt;
        &lt;/head&gt;
        &lt;body&gt;
          &lt;nav&gt;
            &lt;a href=&quot;/&quot;&gt;Home&lt;/a&gt;
            &lt;a href=&quot;/foo&quot;&gt;Foo&lt;/a&gt;
            &lt;a href=&quot;/bar&quot;&gt;Bar&lt;/a&gt;
          &lt;/nav&gt;
          &lt;h1&gt;<span class="hljs-subst">${name}</span>!&lt;/h1&gt;
          &lt;counter-component&gt;&lt;/counter-component&gt;
          /* 👇 Counter Web component code here */
          &lt;script src=&quot;/code.js&quot;&gt;&lt;/script&gt;
        &lt;/body&gt;
      &lt;/html&gt;
    `</span>,
      {
        <span class="hljs-attr">headers</span>: {
          <span class="hljs-string">&#x27;Content-Type&#x27;</span>: <span class="hljs-string">&#x27;text/html&#x27;</span>,
        },
      }
    )
  },
})

<span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">`Done! http://<span class="hljs-subst">${server.hostname}</span>:<span class="hljs-subst">${server.port}</span>`</span>)
</code></pre><p>Now if we run it again we will see that there is a counter on each page and we can interact with it to change its value!</p>
<figure align="center">
  <img src="https://aralroca.com/images/blog-images/web-component.webp" width="500px" height="259px" alt="Our first web component" class="center" />
  <figcaption><small>Our first web component</small></figcaption>
</figure>

<p>However, when we navigate between pages, the counter is reset to 0 😬</p>
<p>The thing is that each navigation loads the page again and therefore the web component script is loaded again and returns to its initial state.</p>
<p>The browsers traditional navigation what they do is to replace all the current document, for the new one. What we want is that it only updates what has changed of the DOM, if the web component does not change, why reset it?</p>
<h2 id="4-maintaining-the-state-during-navigation">4. Maintaining the state during navigation</h2>
<p>There are many strategies to preserve the state, such as saving the value in <code>sessionStorage</code> so that at startup it loads its initial value again, but that is not what we are going to do, we are going to do something better, and that is to update during navigation only the DOM changes that change, and not the rest, therefore, only the heading should change, preserving the web component between pages so that it maintains its state.</p>
<p>To do this, we cannot use the browser&#39;s natural navigation, we have to apply a diff between the current document and the HTML stream, for this to be possible, we have to register the <code>navigate</code> event for the browsers that support it:</p>
<pre><code class="hljs language-ts"><span class="hljs-keyword">if</span> (<span class="hljs-string">&#x27;navigation&#x27;</span> <span class="hljs-keyword">in</span> <span class="hljs-variable language_">window</span>) {
  <span class="hljs-variable language_">window</span>.<span class="hljs-property">navigation</span>.<span class="hljs-title function_">addEventListener</span>(<span class="hljs-string">&#x27;navigate&#x27;</span>, spaNavigation)
}
</code></pre><p>Now what would be missing would be to implement the logic of the <code>spaNavigation</code> function to do the diff. As we have commented previously, we are going to use the library <a href="https://github.com/aralroca/diff-dom-streaming"><code>Diff DOM Streaming</code></a>. For it, we will only intercept the navigations that are with the same origin:</p>
<pre><code class="hljs language-ts"><span class="hljs-keyword">function</span> <span class="hljs-title function_">spaNavigation</span>(<span class="hljs-params">event</span>) {
  <span class="hljs-keyword">const</span> url = <span class="hljs-keyword">new</span> <span class="hljs-title function_">URL</span>(event.<span class="hljs-property">destination</span>.<span class="hljs-property">url</span>)

  <span class="hljs-comment">// Avoid intercepting different origins</span>
  <span class="hljs-keyword">if</span> (location.<span class="hljs-property">origin</span> !== url.<span class="hljs-property">origin</span>) <span class="hljs-keyword">return</span>

  event.<span class="hljs-title function_">intercept</span>({
    <span class="hljs-comment">/* TODO */</span>
  })
}
</code></pre><p>The <code>event.intercept</code> allows us to add a <code>handler</code> to implement our own navigation, the browser will change the URL automatically, but instead of processing the request and updating the DOM by the browser here it will be done by us:</p>
<pre><code class="hljs language-ts"><span class="hljs-keyword">function</span> <span class="hljs-title function_">spaNavigation</span>(<span class="hljs-params">event</span>) {
  <span class="hljs-keyword">const</span> url = <span class="hljs-keyword">new</span> <span class="hljs-title function_">URL</span>(event.<span class="hljs-property">destination</span>.<span class="hljs-property">url</span>)

  <span class="hljs-keyword">if</span> (location.<span class="hljs-property">origin</span> !== url.<span class="hljs-property">origin</span>) <span class="hljs-keyword">return</span>

  event.<span class="hljs-title function_">intercept</span>({
    <span class="hljs-keyword">async</span> <span class="hljs-title function_">handler</span>(<span class="hljs-params"></span>) {
      <span class="hljs-comment">// Fetch new HTML Streaming document</span>
      <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> <span class="hljs-title function_">fetch</span>(url.<span class="hljs-property">pathname</span>, { <span class="hljs-attr">signal</span>: event.<span class="hljs-property">signal</span> })

      <span class="hljs-keyword">if</span> (res.<span class="hljs-property">ok</span>) {
        <span class="hljs-comment">// Lazy import of the diff-dom-streaming library</span>
        <span class="hljs-keyword">const</span> diffModule = <span class="hljs-keyword">await</span> <span class="hljs-keyword">import</span>(
          <span class="hljs-string">&#x27;https://unpkg.com/diff-dom-streaming@latest&#x27;</span>
        )
        <span class="hljs-keyword">const</span> diff = diffModule.<span class="hljs-property">default</span>

        <span class="hljs-comment">// Scroll to the top of the page</span>
        <span class="hljs-variable language_">document</span>.<span class="hljs-property">documentElement</span>.<span class="hljs-property">scrollTop</span> = <span class="hljs-number">0</span>

        <span class="hljs-comment">// Applies the diff between the current document and the stream reader.</span>
        <span class="hljs-keyword">await</span> <span class="hljs-title function_">diff</span>(<span class="hljs-variable language_">document</span>, res.<span class="hljs-property">body</span>.<span class="hljs-title function_">getReader</span>())
      }
    },
  })
}
</code></pre><p>In the <code>handler</code> the first thing we do is request the new page, once the request has been processed, we load the library in a lazy way (it will only do it the first time), make the page scroll up and apply the diff with the reader.</p>
<p>In this way, it now only updates the parts that change during browsing and therefore keeps the web components alive without resetting them again.</p>
<p>Now if we run the file and check the navigation, we see that it keeps the last state of the counter!</p>
<figure align="center">
  <img src="https://aralroca.com/images/blog-images/web-component-state-perserve.webp" width="500px" height="259px" alt="Preserving Web Component State" class="center" />
  <figcaption><small>Preserving Web Component State</small></figcaption>
</figure>

<p>Ok, we are doing well!</p>
<h2 id="5-manage-new-scripts-of-the-page-to-be-navigated-to">5. Manage new scripts of the page to be navigated to</h2>
<p>If we put another script with a <code>console.log</code> of the <code>name</code> (what you see as heading) <code>&lt;script&gt;console.log(&#39;${name}&#39;)&lt;/script&gt;</code>, our expectation is that this <code>console.log</code> will come up every time you browse, since the script is different, in fact it could even be another totally different script that needs the page to work.</p>
<p>If we add it, we see that this does not happen, it only runs on the initial loading of the first page, but then during navigation the <code>console.log</code> is not repeated again!</p>
<figure align="center">
  <img src="https://aralroca.com/images/blog-images/console-log-fail.webp" width="500px" height="259px" alt="New scripts are not executed" class="center" />
  <figcaption><small>New scripts are not executed</small></figcaption>
</figure>

<p><strong>How can we solve it?</strong></p>
<p>The Diff DOM Streaming, only makes a change at DOM level, however, to execute the new scripts in this one we can do it by means of an extension:</p>
<pre><code class="hljs language-ts"><span class="hljs-title function_">registerCurrentScripts</span>()

<span class="hljs-keyword">await</span> <span class="hljs-title function_">diff</span>(<span class="hljs-variable language_">document</span>, res.<span class="hljs-property">body</span>.<span class="hljs-title function_">getReader</span>(), {
  <span class="hljs-attr">onNextNode</span>: loadScripts,
})
</code></pre><p>Where <code>registerCurrentScripts</code> and <code>loadScripts</code> are:</p>
<pre><code class="hljs language-ts"><span class="hljs-keyword">const</span> scripts = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Set</span>()

<span class="hljs-keyword">function</span> <span class="hljs-title function_">registerCurrentScripts</span>(<span class="hljs-params"></span>) {
  <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> script <span class="hljs-keyword">of</span> <span class="hljs-variable language_">document</span>.<span class="hljs-property">scripts</span>) {
    <span class="hljs-keyword">if</span> (script.<span class="hljs-property">id</span> || script.<span class="hljs-title function_">hasAttribute</span>(<span class="hljs-string">&#x27;src&#x27;</span>)) {
      scripts.<span class="hljs-title function_">add</span>(script.<span class="hljs-property">id</span> || script.<span class="hljs-title function_">getAttribute</span>(<span class="hljs-string">&#x27;src&#x27;</span>))
    }
  }
}

<span class="hljs-comment">// Load new scripts</span>
<span class="hljs-keyword">function</span> <span class="hljs-title function_">loadScripts</span>(<span class="hljs-params">node</span>) {
  <span class="hljs-keyword">if</span> (node.<span class="hljs-property">nodeName</span> !== <span class="hljs-string">&#x27;SCRIPT&#x27;</span>) <span class="hljs-keyword">return</span>

  <span class="hljs-keyword">const</span> src = node.<span class="hljs-title function_">getAttribute</span>(<span class="hljs-string">&#x27;src&#x27;</span>)

  <span class="hljs-keyword">if</span> (scripts.<span class="hljs-title function_">has</span>(src) || scripts.<span class="hljs-title function_">has</span>(node.<span class="hljs-property">id</span>)) <span class="hljs-keyword">return</span>

  <span class="hljs-keyword">const</span> script = <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">createElement</span>(<span class="hljs-string">&#x27;script&#x27;</span>)

  <span class="hljs-keyword">if</span> (src) script.<span class="hljs-property">src</span> = src

  script.<span class="hljs-property">innerHTML</span> = node.<span class="hljs-property">innerHTML</span>

  <span class="hljs-comment">// Remove after load the script</span>
  script.<span class="hljs-property">onload</span> = script.<span class="hljs-property">onerror</span> = <span class="hljs-function">() =&gt;</span> script.<span class="hljs-title function_">remove</span>()

  <span class="hljs-variable language_">document</span>.<span class="hljs-property">head</span>.<span class="hljs-title function_">appendChild</span>(script)

  <span class="hljs-comment">// Remove after append + execute (only for inline script)</span>
  <span class="hljs-keyword">if</span> (!src) script.<span class="hljs-title function_">remove</span>()
}
</code></pre><p>What the previous code does is that during the application of the diff, all the scripts that have been detected that have some change and at the same time have not been executed previously, we execute them.</p>
<figure align="center">
  <img src="https://aralroca.com/images/blog-images/console-log.webp" width="500px" height="259px" alt="New scripts now are executed" class="center" />
  <figcaption><small>New scripts now are executed</small></figcaption>
</figure>

<p>If we now run it again, we will see that:</p>
<ul>
<li>It preserves the state of the web component when browsing.</li>
<li>The <code>console.log</code> is executed every time we navigate.</li>
</ul>
<p>That is to say, now we have a functional SPA, without changing the navigation history, that works in streaming and it works both with <code>&lt;a href=&quot;/foo&quot; /&gt;</code> and with imperative navigation as <code>location.assign(&#39;/foo&#39;)</code> 🥳.</p>
<h2 id="6-working-with-streaming-and-suspense">6. Working with streaming and suspense</h2>
<p>In the previous examples we have used a normal <code>Response</code> from Bun, without streaming. With streaming we could use a <code>Suspense</code> as explained in the <a href="https://aralroca.com/blog/html-node-streaming">first article</a> and keep it running during navigation.</p>
<pre><code class="hljs language-ts"><span class="hljs-keyword">let</span> suspensePromise
<span class="hljs-comment">// ...</span>
<span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Response</span>(
  <span class="hljs-keyword">new</span> <span class="hljs-title class_">ReadableStream</span>({
    <span class="hljs-keyword">async</span> <span class="hljs-title function_">start</span>(<span class="hljs-params">controller</span>) {
      <span class="hljs-comment">// Some initial chunks...</span>

      <span class="hljs-comment">// Add &quot;Suspense&quot; placeholder</span>
      controller.<span class="hljs-title function_">enqueue</span>(encoder.<span class="hljs-title function_">encode</span>(<span class="hljs-string">&#x27;&lt;div id=&quot;suspense&quot;&gt;Loading...&lt;/div&gt;&#x27;</span>))

      <span class="hljs-comment">// Expensive chunk:</span>
      suspensePromise = <span class="hljs-title class_">Bun</span>.<span class="hljs-title function_">sleep</span>(<span class="hljs-number">2000</span>).<span class="hljs-title function_">then</span>(handleExpensiveChunk)

      <span class="hljs-comment">// &quot;Unsuspense&quot; code</span>
      <span class="hljs-keyword">function</span> <span class="hljs-title function_">handleExpensiveChunk</span>(<span class="hljs-params"></span>) {
        controller.<span class="hljs-title function_">enqueue</span>(
          encoder.<span class="hljs-title function_">encode</span>(<span class="hljs-string">`
            &lt;template id=&quot;suspensed-content&quot;&gt;&lt;div&gt;Expensive content&lt;/div&gt;&lt;/template&gt;
          `</span>)
        )
        controller.<span class="hljs-title function_">enqueue</span>(
          encoder.<span class="hljs-title function_">encode</span>(<span class="hljs-string">`
            &lt;script&gt;
              function unsuspense() {
                const suspensedElement = document.getElementById(&#x27;suspense&#x27;);
                const ususpensedTemplate = document.getElementById(&#x27;suspensed-content&#x27;);

                if (suspensedElement &amp;&amp; ususpensedTemplate) {            
                  suspensedElement.replaceWith(ususpensedTemplate.content.cloneNode(true));
                  ususpensedTemplate.remove();
                }
              }
              unsuspense();
            &lt;/script&gt;
          `</span>)
        )
      }

      <span class="hljs-comment">// ... Rest of chunks</span>
      <span class="hljs-keyword">await</span> suspensePromise
      controller.<span class="hljs-title function_">close</span>()
    },
  })
)
</code></pre><p>In order to create the suspense, it&#39;s important to take into account the following:</p>
<ul>
<li>Add the chunk with placeholder so that it occupies the place that the expensive chunk will occupy later.</li>
<li>Save the promise that loads the content of the expensive chunk and do not make the <code>await</code> until the end of the streaming. In this example I have used <code>Bun.sleep</code> of 2 seconds to simulate this load.</li>
<li>In the <code>.then</code> of the promise that makes the modification of the temporary chunk by the end.</li>
</ul>
<p>Suspense with streaming by default works both for the initial load, as well as during normal browser navigation. Nevertheless, with our navigation implementation it also works 🥳</p>
<figure align="center">
  <img src="https://aralroca.com/images/blog-images/suspense.webp" width="500px" height="259px" alt="SPA that works with suspense and streaming" class="center" />
  <figcaption><small>SPA that works with suspense and streaming</small></figcaption>
</figure>

<p>So we already have a SPA that works with suspense and streaming! And we haven&#39;t had to make any changes to our client code. However, we have benefits, while navigating to the other page with suspense mode, we can interact with our web components, this is wonderful!</p>
<h2 id="7-transitions-between-pages-view-transition-api">7. Transitions between pages (View Transition API)</h2>
<p>By using our own implementation, apart from making SPA-like navigation behave without having to handle the navigation history with <a href="https://developer.mozilla.org/en-US/docs/Web/API/History/pushState"><code>pushState</code></a> and being able to use more standard things like <code>location.assign</code> or HTML hyperlinks (<code>a</code>), we can also add styled transitions thanks to the <a href="https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API">View Transition API</a>.</p>
<p>The View Transitions API provides a mechanism for easily creating animated transitions between different DOM states while also updating the DOM contents in a single step, and yes, they can be used during streaming, and divide it into different steps. To activate it, it is only necessary to pass the <code>transition: true</code> setting to <a href="https://github.com/aralroca/diff-dom-streaming">Diff DOM Streaming</a> library:</p>
<pre><code class="hljs language-diff">await diff(document, res.body.getReader(), {
  onNextNode: loadScripts,
<span class="hljs-addition">+ transition: true</span>
})
</code></pre><p>The chunk of the &quot;unsuspense&quot; script can also be set to use the View Transition API:</p>
<pre><code class="hljs language-diff">&lt;script&gt;
<span class="hljs-deletion">-  function unsuspense() {</span>
<span class="hljs-addition">+  async function unsuspense() {</span>
<span class="hljs-addition">+    if (window.lastDiffTransition) await window.lastDiffTransition.finished;</span>
    const suspensedElement = document.getElementById(&#x27;suspense&#x27;);
    const ususpensedTemplate = document.getElementById(&#x27;suspensed-content&#x27;);

    if (suspensedElement &amp;&amp; ususpensedTemplate) {
<span class="hljs-deletion">-      suspensedElement.replaceWith(ususpensedTemplate.content.cloneNode(true));</span>
<span class="hljs-addition">+      document.startViewTransition(() =&gt; {</span>
<span class="hljs-addition">+        suspensedElement.replaceWith(ususpensedTemplate.content.cloneNode(true));</span>
<span class="hljs-addition">+      });</span>
       ususpensedTemplate.remove();
    }
  }
  unsuspense();
&lt;/script&gt;
</code></pre><p>The Diff DOM Streaming library exposes the <code>window.lastDiffTransition</code> so we can get the <a href="https://developer.mozilla.org/en-US/docs/Web/API/ViewTransition"><code>ViewTransition</code></a> and do things like wait for it to be done to make sure the template is rendered.</p>
<figure align="center">
  <img src="https://aralroca.com/images/blog-images/view-transition.webp" width="800px" height="308px" alt="SPA with suspense, streaming and View Transition API" class="center" />
  <figcaption><small>SPA with suspense, streaming and View Transition API</small></figcaption>
</figure>

<p>To better visualize the transition animation, we can exaggerate its timing with CSS:</p>
<pre><code class="hljs language-html"><span class="hljs-tag">&lt;<span class="hljs-name">style</span>&gt;</span><span class="language-css">
  ::<span class="hljs-built_in">view-transition-old</span>(root),
  ::<span class="hljs-built_in">view-transition-new</span>(root) {
    <span class="hljs-attribute">animation-duration</span>: <span class="hljs-number">0.5s</span>;
  }
</span><span class="hljs-tag">&lt;/<span class="hljs-name">style</span>&gt;</span>
</code></pre><h3 id="incremental-vs-full-transition">Incremental vs full transition</h3>
<p>Many times it will make more sense to use a complete transition instead of incremental, especially if we do not use suspense and we want a single transition at once instead of several, in this case, instead of using the configuration, we can use the View Transition API directly:</p>
<pre><code class="hljs language-diff"><span class="hljs-addition">+ document.startViewTransition(async () =&gt; {</span>
await diff(document, res.body.getReader(), {
  onNextNode: loadScripts,
<span class="hljs-deletion">-  transition: true,</span>
});
<span class="hljs-addition">+});</span>
</code></pre><h2 id="8-caching-the-navigation">8. Caching the navigation</h2>
<p>Naturally, if you have already visited the page before, it should be cached during navigation, so that it would not be necessary to display the suspense again and show the actual content of the page directly.</p>
<p>To make this possible, from Bun we can add the following header to the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Response"><code>Response</code></a>:</p>
<pre><code class="hljs language-ts"><span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Response</span>(
  <span class="hljs-keyword">new</span> <span class="hljs-title class_">ReadableStream</span>({
    <span class="hljs-comment">/* ... chunks of html ... */</span>
  }),
  {
    <span class="hljs-attr">headers</span>: {
      <span class="hljs-string">&#x27;cache-control&#x27;</span>: <span class="hljs-string">&#x27;public, max-age=31536000, immutable&#x27;</span>,
    },
  }
)
</code></pre><figure align="center">
  <img src="https://aralroca.com/images/blog-images/cached-suspense.webp" width="700px" height="516px" alt="The suspense is no longer shown when caching" class="center" />
  <figcaption><small>The suspense is no longer shown when caching</small></figcaption>
</figure>

<p>When being cached instead of receiving different chunks in streaming and processing the suspense while waiting for the expensive chunk, the diff have everything at once and therefore the suspense stops coming out because the browser has already cached the real content.</p>
<blockquote>
<p>👉 In development it will be better to set <code>&quot;no-store, must-revalidate&quot;</code> to be able to work and see the changes without being cached.</p>
</blockquote>
<h2 id="9-prefetch-the-navigation">9. Prefetch the navigation</h2>
<p>Sometimes it is necessary to use <code>prefetch</code>, because we can know what will be the next page that the user will visit. An example would be if an e-commerce checkout process has different steps on different pages, we know what the next page will be and we want that when the user performs the action it is displayed as easy as possible. In this case, we could add using the normal <code>prefetch</code> of the platform that would continue to work with our SPA-like implementation:</p>
<pre><code class="hljs language-html"><span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">&quot;prefetch&quot;</span> <span class="hljs-attr">href</span>=<span class="hljs-string">&quot;/foo&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">link</span>&gt;</span>
</code></pre><p>In this case, even if we clear the cache, navigating to <code>/foo</code> will always go fast, because on the previous page its HTML is requested to improve navigation.</p>
<figure align="center">
  <img src="https://aralroca.com/images/blog-images/prefetch.webp" width="700px" height="455px" alt="Prefetching the /foo page" class="center" />
  <figcaption><small>Prefetching the /foo page</small></figcaption>
</figure>

<p>If we look at the Network tab, refreshing the home page already downloads the foo page, so that navigating to it is instantaneous without showing the suspense.</p>
<h2 id="10-pros-and-cons-of-using-diff-dom-streaming-for-web-navigation">10. Pros and Cons of Using Diff DOM Streaming for Web Navigation</h2>
<h3 id="pros">Pros:</h3>
<ol>
<li><p><strong>State Maintenance:</strong> The implementation allows for maintaining the state of web components during navigation, enhancing user experience by avoiding data loss.</p>
</li>
<li><p><strong>Load Optimization:</strong> Utilizing Diff DOM Streaming can significantly improve page load times by streaming and updating only the elements that have changed, rather than reloading the entire page. This avoids reloading a lot of resources.</p>
</li>
<li><p><strong>Smooth Transitions:</strong> Integration with the View Transition API enables adding animated transitions between different page states, improving aesthetics and the feeling of fluidity in the application.</p>
</li>
<li><p><strong>Navigation Caching:</strong> The implemented caching strategy optimizes user experience by caching previously visited pages, reducing load times on future visits.</p>
</li>
<li><p><strong>Prefetching:</strong> Prefetching capability anticipates user actions and prepares corresponding pages in advance, enhancing responsiveness and load speed.</p>
</li>
</ol>
<figure align="center">
  <img src="https://aralroca.com/images/blog-images/positive.webp" width="700px" alt="Image from lexica.art" class="center" />
  <figcaption><small>Image from lexica.art</small></figcaption>
</figure>

<h3 id="cons">Cons:</h3>
<ol>
<li><p><strong>Limited Compatibility:</strong> The functionality of intercepting navigation with the <code>navigate</code> event may have limited compatibility, as it currently only supports certain browsers. This may limit portability and widespread adoption of the proposed solution.</p>
</li>
<li><p><strong>Limitations in Using Component Libraries:</strong> Those familiar with development using component libraries like React may find limitations in not being able to fully leverage the potential of HTML Streaming over the wire. Instead, they may need to resort to other strategies to maintain component state during navigation.</p>
</li>
</ol>
<figure align="center">
  <img src="https://aralroca.com/images/blog-images/negative.webp" width="700px" alt="Image from lexica.art" class="center" />
  <figcaption><small>Image from lexica.art</small></figcaption>
</figure>

<h2 id="final-conclusions">Final conclusions</h2>
<p>Concluding this series on HTML Streaming, we&#39;ve explored the practical application of the <a href="https://github.com/aralroca/diff-dom-streaming">Diff DOM Streaming</a> library in web browsing. By integrating this approach, websites utilizing web components can maintain their state during browsing sessions. Throughout this journey, we&#39;ve delved into detailed implementations using VanillaJS and Bun.</p>
<p>If you want to see the final code that we have been talking about in the article, you can access here:</p>
<ul>
<li><a href="https://github.com/aralroca/diff-dom-streaming/tree/7dd2981b01eb8fb8eed6dc8512742e2c5487dca8/examples/spa-like">Code example</a></li>
</ul>
<p>As a note, we have disabled the cache and prefetch so you can comfortably test the suspense and see how the animations look when browsing, but feel free to play and test.</p>
<p><strong>Final note</strong>:</p>
<p>In Brisa, the framework we are developing, one of the features is that you can write web components as JSX components to make it more comfortable, in fact it is very similar to Brisa server components, the only difference is that instead of web components they are translated to pure HTML, and using signals you can share state between pages to make the pages react. I invite you to <a href="https://aralroca.com/blog">subscribe to my blog newsletter to be notified when we release Brisa open-source</a> in a few months and you can try it.</p>
<h2 id="references">References</h2>
<ul>
<li><a href="https://github.com/aralroca/diff-dom-streaming">diff-dom-streaming library</a> - Library used for implementing HTML Streaming in web browsing.</li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/API/CustomElementRegistry/define">CustomElementRegistry</a> - Documentation on the <code>CustomElementRegistry</code> interface for registering and defining custom elements for use in a document.</li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API">View Transition API</a> - Information about the View Transition API, which provides a mechanism for creating animated transitions between different DOM states.</li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/API/History/pushState">pushState</a> - Documentation on the <code>pushState</code> method of the History interface, used for adding states to the browser&#39;s session history stack.</li>
</ul>
]]></content:encoded>
            </item>
            <item>
              <title>Static Readme Regeneration</title>
              <description>Use a cron with Github Actions to always have your GitHub README.md profile updated.</description>
              <link>https://aralroca.com/blog/static-readme-regeneration</link>
              <guid isPermaLink="false">https://aralroca.com/blog/static-readme-regeneration/</guid>
              <pubDate>Wed Jul 29 2020 00:00:00 GMT+0000 (Coordinated Universal Time)</pubDate>
              <content:encoded><![CDATA[<p>GitHub has recently introduced a &quot;secret&quot; feature to show a markdown template on your profile page. You may have heard about this. All you need to do is create a repo named with your username and create the <code>README.md</code> file there.</p>
<p>When we think of a template markdown on GitHub we normally think of static content. However, in this article, I want to go a little further. I&#39;ll tell you how to add content that is going to be <strong>updated from time to time</strong>; whether it&#39;s about your latest tweets, your latest youtube video or your latest blog posts.</p>
<img src="https://aralroca.com/images/blog-images/my-gh-profile.png" alt="My GH profile" class="center" />

<p><small class="center">My GitHub README profile under <a href="https://github.com/aralroca/aralroca">aralroca</a> repo</small></p>
<p><strong>We&#39;ll cover the following:</strong></p>
<ul>
<li><a href="#about-static-readme-regeneration-srr">About Static Readme Regeneration (SRR)</a></li>
<li><a href="#implementation">Implementation</a><ul>
<li><a href="#readmetpl">README.tpl</a></li>
<li><a href="#script-to-generate-the-readmemd">Script to generate the README.md</a></li>
<li><a href="#github-action-with-a-cron">GitHub action with a cron</a></li>
</ul>
</li>
<li><a href="#conclusion">Conclusion</a></li>
<li><a href="#references">References</a></li>
</ul>
<h2 id="about-static-readme-regeneration-srr">About Static Readme Regeneration (SRR)</h2>
<p>By &quot;Static Readme Regeneration&quot; I mean that the file <code>README.md</code> is generated by our script, and then we update the content through a bot that periodically (programmed by us) makes a re-build of the <code>README.md</code>. The beauty of this is in the fact that the bot only commits to the repo if <code>README.md</code> has really changed. This way the content of the README can&#39;t be entirely static, it can change every day, every hour, or even every minute.</p>
<p>In order to do this, we&#39;ll use a GitHub Action with a cron:</p>
<img src="https://aralroca.com/images/blog-images/regeneration.png" alt="Static regeneration diagram for GitHub README profile" class="center" />

<p><small class="center">Diagram about static regeneration with GitHub Actions</small></p>
<h2 id="implementation">Implementation</h2>
<p>I&#39;m going to use as example what I did in my profile. It always shows the last 5 articles of my blog and updates every day (if necessary). This way I can relax because I know that when I upload a new post in my blog, the <code>README.md</code> file of my profile will be automatically updated.</p>
<h3 id="readmetpl">README.tpl</h3>
<p>Let&#39;s create a <code>README.md.tpl</code> file, the <code>.tpl</code> format is used in template files. This file will contain all the static content of the <code>README.md</code>. We&#39;ll write the markdown here as if we were writing in the <code>README.md</code> file.</p>
<p>The main difference is that we&#39;ll add what we want to be dynamic with some <strong>interpolation symbols</strong>. This way, our script will be able to replace them with dynamic content.</p>
<img src="https://aralroca.com/images/blog-images/tpl.png" alt="README.md.tpl diagram" class="center" />
<small class="center">Diagram about interpolation from <code>.md.tpl</code> to <code>.md</code></small>

<h3 id="script-to-generate-the-readmemd">Script to generate the README.md</h3>
<p>The script have to:</p>
<ul>
<li>Read the file <code>README.tpl.md</code>.</li>
<li>Fetch all the posts from <a href="https://aralroca.com/rss.xml">https://aralroca.com/rss.xml</a>.</li>
<li>Sort by <code>pub_date</code> + filter 5.</li>
<li>Write the <code>README.md</code> file replacing the interpolation from <code>README.tpl.md</code> to the 5 articles as markdown string.</li>
</ul>
<br />
<img src="https://aralroca.com/images/blog-images/script.png" alt="Steps to implement" class="center" />

<p>This can be implemented in any language; JavaScript, Rust, Python, Go, C... In this case, I chose <strong>Rust</strong>, mostly because I have no experience with it and so I took the opportunity to learn a little <em>(feel free to create an issue on the repo if you are a Rust expert and see things that could be improved)</em>.</p>
<p><small><b>&gt; main.rs</b></small></p>
<pre><code class="hljs language-rs"><span class="hljs-keyword">mod</span> create_readme;

<span class="hljs-keyword">use</span> create_readme::create_readme;

<span class="hljs-keyword">fn</span> <span class="hljs-title function_">main</span>() {
    <span class="hljs-keyword">match</span> <span class="hljs-title function_ invoke__">create_readme</span>() {
        <span class="hljs-title function_ invoke__">Ok</span>(_v) =&gt; <span class="hljs-built_in">println!</span>(<span class="hljs-string">&quot;README.md file generated correctly&quot;</span>),
        <span class="hljs-title function_ invoke__">Err</span>(e) =&gt; <span class="hljs-built_in">println!</span>(<span class="hljs-string">&quot;Opps! there was an error: {:?}&quot;</span>, e),
    }
}
</code></pre><p><small><b>&gt; create_readme.rs</b></small></p>
<pre><code class="hljs language-rs"><span class="hljs-keyword">extern</span> <span class="hljs-keyword">crate</span> chrono;
<span class="hljs-keyword">extern</span> <span class="hljs-keyword">crate</span> rss;

<span class="hljs-keyword">use</span> chrono::DateTime;
<span class="hljs-keyword">use</span> rss::Channel;
<span class="hljs-keyword">use</span> std::cmp::Ordering;
<span class="hljs-keyword">use</span> std::fs;

<span class="hljs-keyword">struct</span> <span class="hljs-title class_">FeedItem</span> {
    title: <span class="hljs-type">String</span>,
    link: <span class="hljs-type">String</span>,
    pub_date: <span class="hljs-type">String</span>,
}

<span class="hljs-keyword">pub</span> <span class="hljs-keyword">fn</span> <span class="hljs-title function_">create_readme</span>() <span class="hljs-punctuation">-&gt;</span> std::io::<span class="hljs-type">Result</span>&lt;()&gt; {
    <span class="hljs-keyword">let</span> <span class="hljs-variable">tpl</span> =
        fs::<span class="hljs-title function_ invoke__">read_to_string</span>(<span class="hljs-string">&quot;README.md.tpl&quot;</span>)
        .<span class="hljs-title function_ invoke__">expect</span>(<span class="hljs-string">&quot;Something went wrong reading the README.tpl file&quot;</span>);

    <span class="hljs-keyword">let</span> <span class="hljs-variable">last_articles</span> = <span class="hljs-title function_ invoke__">get_latest_articles</span>();

    <span class="hljs-keyword">return</span> fs::<span class="hljs-title function_ invoke__">write</span>(
        <span class="hljs-string">&quot;README.md&quot;</span>,
        tpl.<span class="hljs-title function_ invoke__">replace</span>(<span class="hljs-string">&quot;%{{latest_articles}}%&quot;</span>, &amp;last_articles),
    );
}

<span class="hljs-keyword">fn</span> <span class="hljs-title function_">get_latest_articles</span>() <span class="hljs-punctuation">-&gt;</span> <span class="hljs-type">String</span> {
    <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut </span><span class="hljs-variable">posts</span>: <span class="hljs-type">Vec</span>&lt;FeedItem&gt; = <span class="hljs-title function_ invoke__">get_blog_rss</span>();

    <span class="hljs-comment">// Sort articles by pub_date</span>
    posts.<span class="hljs-title function_ invoke__">sort_by</span>(|a, b| {
        <span class="hljs-keyword">let</span> <span class="hljs-variable">date_a</span> = DateTime::<span class="hljs-title function_ invoke__">parse_from_rfc2822</span>(&amp;a.pub_date).<span class="hljs-title function_ invoke__">unwrap</span>();
        <span class="hljs-keyword">let</span> <span class="hljs-variable">date_b</span> = DateTime::<span class="hljs-title function_ invoke__">parse_from_rfc2822</span>(&amp;b.pub_date).<span class="hljs-title function_ invoke__">unwrap</span>();

        <span class="hljs-keyword">if</span> date_b &lt; date_a {
            Ordering::Less
        } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> date_b &gt; date_a {
            Ordering::Greater
        } <span class="hljs-keyword">else</span> {
            Ordering::Equal
        }
    });

    <span class="hljs-comment">// Filter las 5 articles + format each one as markdown list string</span>
    <span class="hljs-keyword">return</span> posts[..<span class="hljs-number">5</span>].<span class="hljs-title function_ invoke__">iter</span>().<span class="hljs-title function_ invoke__">fold</span>(<span class="hljs-string">&quot;&quot;</span>.<span class="hljs-title function_ invoke__">to_string</span>(), |acc, item| {
        <span class="hljs-built_in">format!</span>(<span class="hljs-string">&quot;{} \n* [{}]({})&quot;</span>, acc, item.title, item.link)
    });
}

<span class="hljs-comment">// Fetch all articles of my blog on rss.xml</span>
<span class="hljs-keyword">fn</span> <span class="hljs-title function_">get_blog_rss</span>() <span class="hljs-punctuation">-&gt;</span> <span class="hljs-type">Vec</span>&lt;FeedItem&gt; {
    <span class="hljs-keyword">let</span> <span class="hljs-variable">items</span> = Channel::<span class="hljs-title function_ invoke__">from_url</span>(<span class="hljs-string">&quot;https://aralroca.com/rss.xml&quot;</span>)
        .<span class="hljs-title function_ invoke__">unwrap</span>()
        .<span class="hljs-title function_ invoke__">items</span>()
        .<span class="hljs-title function_ invoke__">iter</span>()
        .<span class="hljs-title function_ invoke__">map</span>(|item| FeedItem {
            title: item.<span class="hljs-title function_ invoke__">title</span>().<span class="hljs-title function_ invoke__">unwrap</span>().<span class="hljs-title function_ invoke__">to_string</span>(),
            link: item.<span class="hljs-title function_ invoke__">link</span>().<span class="hljs-title function_ invoke__">unwrap</span>().<span class="hljs-title function_ invoke__">to_string</span>(),
            pub_date: item.<span class="hljs-title function_ invoke__">pub_date</span>().<span class="hljs-title function_ invoke__">unwrap</span>().<span class="hljs-title function_ invoke__">to_string</span>(),
        })
        .<span class="hljs-title function_ invoke__">collect</span>();

    items
}
</code></pre><br />

<h3 id="github-action-with-a-cron">GitHub Action with a cron</h3>
<p>Once we have the script that builds our <code>README.md</code>, we just need to generate the cron using GitHub Action.</p>
<p>In order to create an Action, I recommend first uploading your script to the master branch and then clicking the &quot;Actions&quot; tab of GitHub to create it. This way, GitHub detects the script language (Rust in our case) and creates a default yaml.</p>
<img src="https://aralroca.com/images/blog-images/action-tab.png" alt="GitHub Actions tab" class="center" />

<p>We&#39;re going to replace some things from the default yaml in order to:</p>
<ul>
<li>Schedule a cron</li>
<li>Run the script <em>(<code>cargo run</code> instead of <code>cargo build &amp;&amp; cargo test</code>)</em></li>
<li>Commit the regenerated README <em>(only if has changes)</em></li>
</ul>
<p><small><b>&gt; .github/workflows/rust.yml</b></small></p>
<pre><code class="hljs language-yml"><span class="hljs-attr">name:</span> <span class="hljs-string">Rust</span>

<span class="hljs-attr">on:</span>
  <span class="hljs-comment"># Schedule a cron</span>
  <span class="hljs-attr">schedule:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">cron:</span> <span class="hljs-string">&#x27;0 0 */1 * *&#x27;</span> <span class="hljs-comment"># each day at 00:00 UTC</span>

<span class="hljs-attr">env:</span>
  <span class="hljs-attr">CARGO_TERM_COLOR:</span> <span class="hljs-string">always</span>

<span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">build:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>

    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v2</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Build</span>

        <span class="hljs-comment"># Replace &quot;cargo build&quot; to &quot;cargo run&quot; to run the script</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">cargo</span> <span class="hljs-string">run</span>

      <span class="hljs-comment"># Commit the regenerated README only when it change</span>
      <span class="hljs-comment"># (git diff --quiet &amp;&amp; git diff --staged --quiet )</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">run:</span> <span class="hljs-string">|
          git config user.name aralroca
          git config user.email aral-rg@hotmail.com
          git add README.md
          git diff --quiet &amp;&amp; git diff --staged --quiet || git commit -m &quot;[gh-action] Update README&quot;
          git push origin master</span>
</code></pre><h2 id="conclusion">Conclusion</h2>
<p>To conclude, although in most repos the README file is always static, thanks to GitHub Actions and this new feature of GitHub we can build our <code>README.md</code> to always have our profile up to date with the latest updates (releases, pr, tweets, posts...).</p>
<h2 id="references">References</h2>
<ul>
<li><a href="https://www.aboutmonica.com/blog/how-to-create-a-github-profile-readme">https://www.aboutmonica.com/blog/how-to-create-a-github-profile-readme</a></li>
<li><a href="https://github.com/midudev/midudev">https://github.com/midudev/midudev</a></li>
</ul>
]]></content:encoded>
            </item>
            <item>
              <title>Teaful DevTools Released!</title>
              <description>Teaful DevTools is a browser extension to inspect Teaful applications</description>
              <link>https://aralroca.com/blog/teaful-devtools</link>
              <guid isPermaLink="false">https://aralroca.com/blog/teaful-devtools/</guid>
              <pubDate>Thu Dec 02 2021 00:00:00 GMT+0000 (Coordinated Universal Time)</pubDate>
              <content:encoded><![CDATA[<p>Very recently, <strong>2 weeks ago</strong>, we released <a href="https://github.com/teafuljs/teaful">Teaful</a>; a tiny, easy and powerful React State management. If you want to know more about Teaful, I recommend <a href="https://aralroca.com/blog/teaful">this article</a>.</p>
<p>Teaful was <strong>well received</strong> (+500 GH stars) and one of the most requested features was to implement a devtool to debug the stores. Today we released <strong><a href="https://github.com/teafuljs/teaful-devtools">Teaful DevTools</a></strong>. In this short article I will explain a little about how to use it and its benefits.</p>
<img alt="Teaful DevTools" src="https://aralroca.com/images/blog-images/teaful-devtools.png" />

<h2 id="how-to-use-it">How to use it</h2>
<p>4 simple steps:</p>
<ul>
<li><p><strong>Install DevTools extension</strong>:</p>
<ul>
<li><a href="https://chrome.google.com/webstore/detail/teaful-devtools/lficdnnjoackdnaddfcgllmjdocofadc">Chrome</a></li>
<li><a href="https://addons.mozilla.org/es/firefox/addon/teaful-devtools/">Firefox</a></li>
<li><a href="https://microsoftedge.microsoft.com/addons/detail/teaful-devtools/kcplohinngjiammdehjlimgdpamhhkmn?hl=en-GB">Edge</a></li>
</ul>
</li>
<li><p><strong>Install the bridge</strong>: <code>yarn add teaful-devtools</code></p>
</li>
<li><p><strong>Use the bridge</strong>: <code>import &#39;teaful-devtools&#39;</code> <small>(~200 B)</small> Must be the first import.</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">import</span> <span class="hljs-string">&#x27;teaful-devtools&#x27;</span>
<span class="hljs-keyword">import</span> { render } <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;preact&#x27;</span>
<span class="hljs-keyword">import</span> <span class="hljs-title class_">App</span> <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;./components/App&#x27;</span>

<span class="hljs-title function_">render</span>(<span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">App</span> /&gt;</span></span>, <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">getElementById</span>(<span class="hljs-string">&#x27;root&#x27;</span>))
</code></pre></li>
<li><p><strong>Open the DevTools and try it</strong>.</p>
</li>
</ul>
<p>More details in the <a href="https://github.com/teafuljs/teaful-devtools">README</a>.</p>
<h2 id="debug-stores-changes">Debug stores changes</h2>
<p>For each store it is possible to view the <strong>history of changes</strong> that have been made to the store. To debug, we can know <strong>WHEN</strong> the change was made, <strong>WHAT</strong> / <strong>WHERE</strong> was changed, but also <strong>WHO</strong> / <strong>HOW</strong> / <strong>WHY</strong>.</p>
<img alt="Teaful DevTools" src="https://aralroca.com/images/blog-images/teaful-devtools-details.png" />

<h3 id="when">When</h3>
<p>There is no secret, the changes have their own timestamp and are sorted by arrival.</p>
<h3 id="what--where">What / Where</h3>
<p>For each modification, you&#39;ll be able to see which part of the store has changed and what&#39;s the new value (the diff).</p>
<h3 id="who--how--why">Who / How / Why</h3>
<p>You can view the <strong>entire stack trace</strong> and go to the line in the source file.</p>
<p>Clicking on a file link opens the <strong>source devtools tab</strong> where you can <strong>view the code</strong> part to see how the change was produced. To understand more of the context you can also navigate to the function that called this function to see all the code involved. For more context you can put a breakpoint in the source tab to see the value of each variable that caused the change.</p>
<div align="center">
<img width="800" alt="Teaful DevTools source" src="https://aralroca.com/images/blog-images/teaful-devtools-source.gif" />
</div>

<h2 id="modify-the-store-from-teaful-devtools">Modify the store from Teaful DevTools</h2>
<p>From Teaful DevTools you can generate a change to the store and see how the UI reacts.</p>
<div align="center">
<img width="600" alt="Teaful DevTools modify store" src="https://aralroca.com/images/blog-images/teaful-devtools-modify.gif" />
</div>

<h2 id="dark--light-mode">Dark &amp; light mode</h2>
<p>The dark / light theme adapts to your devtools configuration.</p>
<img alt="Teaful DevTools dark theme" src="https://aralroca.com/images/blog-images/teaful-devtools-dark.png" />
<img alt="Teaful DevTools light theme" src="https://aralroca.com/images/blog-images/teaful-devtools-light.png" />

<h2 id="view-rerenders--performance">View rerenders / performance</h2>
<p>It&#39;s a feature that has not been implemented in Teaful DevTools because we consider that React DevTools does it very well.</p>
<p>You can use React DevTools to debug the rerenders and the performance.</p>
<div align="center">
<img width="600" alt="React DevTools Teaful performance" src="https://aralroca.com/images/blog-images/teaful-devtools-performance.gif" />
</div>

<h2 id="how-to-strip-devtools-from-production">How to strip devtools from production</h2>
<p>Most bundlers allow you strip out code when they detect that a branch inside an if-statement will never be hit. We can use this to only include <code>teaful-devtools</code> during development and save those precious bytes in a production build.</p>
<pre><code class="hljs language-js"><span class="hljs-comment">// Must be the first import</span>
<span class="hljs-keyword">if</span> (process.<span class="hljs-property">env</span>.<span class="hljs-property">NODE_ENV</span> === <span class="hljs-string">&#x27;development&#x27;</span>) {
  <span class="hljs-comment">// Must use require here as import statements are only allowed</span>
  <span class="hljs-comment">// to exist at top-level.</span>
  <span class="hljs-built_in">require</span>(<span class="hljs-string">&#x27;teaful-devtools&#x27;</span>)
}

<span class="hljs-keyword">import</span> { render } <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;preact&#x27;</span>
<span class="hljs-keyword">import</span> <span class="hljs-title class_">App</span> <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;./components/App&#x27;</span>

<span class="hljs-title function_">render</span>(<span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">App</span> /&gt;</span></span>, <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">getElementById</span>(<span class="hljs-string">&#x27;root&#x27;</span>))
</code></pre><p>Make sure to set the <code>NODE_ENV</code> variable to the correct value in your build tool.</p>
<h2 id="conclusions">Conclusions</h2>
<p>We have released Teaful DevTools to debug the changes from the stores: when, what, where, who, how, why. And vice versa, to trigger a change from Teaful DevTools to see how the UI reacts.</p>
<p>At the moment it&#39;s only available for Chrome, but in future releases it will also be available for Firefox and Edge.</p>
<p>You can leave any suggestions on <a href="https://github.com/teafuljs/teaful-devtools">GitHub</a> <em>(issue / PR)</em> and it will be taken into account. Remember that <a href="https://github.com/teafuljs/teaful">Teaful project</a> is still in an early version 0.X and together we&#39;ll make it evolve.</p>
]]></content:encoded>
            </item>
            <item>
              <title>Teaful: tiny, easy and powerful React state management</title>
              <description>Teaful is a new npm package where you can manage your stores with less than 1kb without the need of boilerplate: reducers, actions, selectors, connect, etc. And without unnecessary rerenders! It subscribes only to the changes of the used properties.</description>
              <link>https://aralroca.com/blog/teaful</link>
              <guid isPermaLink="false">https://aralroca.com/blog/teaful/</guid>
              <pubDate>Fri Nov 05 2021 00:00:00 GMT+0000 (Coordinated Universal Time)</pubDate>
              <content:encoded><![CDATA[<p>I&#39;ve recently talked about Fragmented-store in <a href="react-fragmented-store">another article</a>, a library I was developing, explaining future improvements. Well, we have made a reimplementation to make it tinier, easier to use and more powerful, and we have renamed it to <strong>Teaful</strong>. In this article I will talk about its benefits and how to use it.</p>
<p>This is <strong>the final name</strong>. Since the library was created, it has been called:</p>
<ul>
<li><del><code>Fragmented-store</code></del> -&gt; <del><code>Fragstore</code></del> -&gt; <strong><code>Teaful</code></strong> <small><em>(<a href="https://github.com/teafuljs/teaful">Teaful GitHub</a> )</em>.</small></li>
</ul>
<figure align="center">
  <img class="center" width="200" src="https://aralroca.com/images/blog-images/teaful-logo.svg" alt="Teaful logo" />
  <figcaption><small>Teaful new logo</small></figcaption>
</figure>

<h2 id="why-tiny">Why tiny?</h2>
<p>Teaful is <strong>less than 1kb</strong> and you won&#39;t need to write so much code. In other words, it will make your project much more lightweight.</p>
<pre><code class="hljs language-sh">874 B: index.js.gz
791 B: index.js.br
985 B: index.modern.js.gz
888 B: index.modern.js.br
882 B: index.m.js.gz
799 B: index.m.js.br
950 B: index.umd.js.gz
856 B: index.umd.js.br
</code></pre><h2 id="why-easy">Why easy?</h2>
<p>To consume and modify store properties sometimes requires a lot of boilerplate: actions, reducers, selectors, connect, etc. Teaful&#39;s goal is to be <strong>very easy to use</strong>, to consume a property and overwrite it <strong>without any boilerplate</strong>. &quot;Tiny&quot; and &quot;powerful&quot;, because if you have to write a lot to do a simple thing, your project takes more kb and becomes less maintainable.</p>
<figure align="center">
  <img class="center" src="https://aralroca.com/images/blog-images/easy.png" alt="Teaful easy to use" />
  <figcaption><small>Teaful: Easy to use without boilerplate</small></figcaption>
</figure>

<h2 id="why-powerful">Why powerful?</h2>
<p>Besides doing the code more maintainable, you avoid many <strong>unnecessary rerenders</strong> while the performance of your website gets better. When you only update one property of the store, it&#39;s not necessary to notify all the components that use the store. It only requires to notify who&#39;s consuming that updated property.</p>
<figure align="center">
  <img class="center" src="https://aralroca.com/images/blog-images/rerenders.gif" alt="Teaful rerenders" />
  <figcaption><small>Teaful rerenders</small></figcaption>
</figure>

<h2 id="what-other-benefits-does-it-have">What other benefits does it have?</h2>
<p>In this section of the article we&#39;ll see a few of the many things that can be done.</p>
<p>If you use Teaful in a small project you&#39;ll be able to move fast without tools like Redux or Mobx that can be overkill. Also, if you use it in large projects they will be more maintainable and won&#39;t grow in code.</p>
<h3 id="creating-store-properties-on-the-fly">Creating store properties on the fly</h3>
<p>You can consume and update nonexistent store properties and define them on the fly.</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">const</span> { useStore } = <span class="hljs-title function_">createStore</span>()

<span class="hljs-keyword">export</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">Counter</span>(<span class="hljs-params"></span>) {
  <span class="hljs-keyword">const</span> [count, setCount] = useStore.<span class="hljs-title function_">count</span>(<span class="hljs-number">0</span>) <span class="hljs-comment">// 0 as initial value</span>

  <span class="hljs-keyword">return</span> (
    <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>{count}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> setCount((c) =&gt; c + 1)}&gt;Increment counter<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> setCount((c) =&gt; c - 1)}&gt;Decrement counter<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  )
}
</code></pre><h3 id="works-with-heavily-nested-properties">Works with heavily nested properties</h3>
<p>You can consume / manipulate any property wherever it is in the store.</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">const</span> { useStore } = <span class="hljs-title function_">createStore</span>({
  <span class="hljs-attr">username</span>: <span class="hljs-string">&#x27;Aral&#x27;</span>,
  <span class="hljs-attr">counters</span>: [{ <span class="hljs-attr">name</span>: <span class="hljs-string">&#x27;My first counter&#x27;</span>, <span class="hljs-attr">counter</span>: { <span class="hljs-attr">count</span>: <span class="hljs-number">0</span> } }],
})

<span class="hljs-keyword">export</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">Counter</span>(<span class="hljs-params">{ counterIndex = <span class="hljs-number">0</span> }</span>) {
  <span class="hljs-keyword">const</span> [count, setCount] = useStore.<span class="hljs-property">counters</span>[counterIndex].<span class="hljs-property">counter</span>.<span class="hljs-title function_">count</span>()

  <span class="hljs-keyword">return</span> (
    <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>{count}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> setCount((c) =&gt; c + 1)}&gt;Increment counter<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> setCount((c) =&gt; c - 1)}&gt;Decrement counter<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  )
}
</code></pre><h3 id="using-more-than-one-store">Using more than one store</h3>
<p>Although it is not necessary, you can have several stores and rename the hooks with more personalized names.</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">import</span> createStore <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;teaful&#x27;</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> { <span class="hljs-attr">useStore</span>: useCart } = <span class="hljs-title function_">createStore</span>({ <span class="hljs-attr">price</span>: <span class="hljs-number">0</span>, <span class="hljs-attr">items</span>: [] })
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> { <span class="hljs-attr">useStore</span>: useCounter } = <span class="hljs-title function_">createStore</span>({ <span class="hljs-attr">count</span>: <span class="hljs-number">0</span> })
</code></pre><p>You can also use them in your components:</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">import</span> { useCounter, useCart } <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;./store&#x27;</span>

<span class="hljs-keyword">function</span> <span class="hljs-title function_">Cart</span>(<span class="hljs-params"></span>) {
  <span class="hljs-keyword">const</span> [price, setPrice] = useCart.<span class="hljs-title function_">price</span>()
  <span class="hljs-comment">// ... rest</span>
}

<span class="hljs-keyword">function</span> <span class="hljs-title function_">Counter</span>(<span class="hljs-params"></span>) {
  <span class="hljs-keyword">const</span> [count, setCount] = useCounter.<span class="hljs-title function_">count</span>()
  <span class="hljs-comment">// ... rest</span>
}
</code></pre><h3 id="using-customized-updaters">Using customized updaters</h3>
<p>If you want several components to use the same updaters without reimplementing them, you can predefine them thanks to the <code>getStore</code> helper.</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">import</span> createStore <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;teaful&#x27;</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> { useStore, getStore } = <span class="hljs-title function_">createStore</span>({ <span class="hljs-attr">count</span>: <span class="hljs-number">0</span> })

<span class="hljs-keyword">const</span> [, setCount] = getStore.<span class="hljs-title function_">count</span>()

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> <span class="hljs-title function_">incrementCount</span> = (<span class="hljs-params"></span>) =&gt; <span class="hljs-title function_">setCount</span>(<span class="hljs-function">(<span class="hljs-params">c</span>) =&gt;</span> c + <span class="hljs-number">1</span>)
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> <span class="hljs-title function_">decrementCount</span> = (<span class="hljs-params"></span>) =&gt; <span class="hljs-title function_">setCount</span>(<span class="hljs-function">(<span class="hljs-params">c</span>) =&gt;</span> c - <span class="hljs-number">1</span>)
</code></pre><p>And use them in your components:</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">import</span> { useStore, incrementCount, decrementCount } <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;./store&#x27;</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">Counter</span>(<span class="hljs-params"></span>) {
  <span class="hljs-keyword">const</span> [count] = useStore.<span class="hljs-title function_">count</span>()

  <span class="hljs-keyword">return</span> (
    <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>{count}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{incrementCount}</span>&gt;</span>Increment counter<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{decrementCount}</span>&gt;</span>Decrement counter<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  )
}
</code></pre><h3 id="optimistic-updates">Optimistic updates</h3>
<p>If you want to make an optimistic update (when you update the store and save the value by calling the api, if the api request fails revert to the previous value). You can do it thanks to the <code>onAfterUpdate</code> function.</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">import</span> createStore <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;teaful&#x27;</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> { useStore, getStore } = <span class="hljs-title function_">createStore</span>({ <span class="hljs-attr">count</span>: <span class="hljs-number">0</span> }, onAfterUpdate)

<span class="hljs-keyword">function</span> <span class="hljs-title function_">onAfterUpdate</span>(<span class="hljs-params">{ store, prevStore }</span>) {
  <span class="hljs-keyword">if</span> (store.<span class="hljs-property">count</span> !== prevStore.<span class="hljs-property">count</span>) {
    <span class="hljs-keyword">const</span> [count, setCount, resetCount] = getStore.<span class="hljs-title function_">count</span>()

    <span class="hljs-title function_">fetch</span>(<span class="hljs-string">&#x27;/api/count&#x27;</span>, { <span class="hljs-attr">method</span>: <span class="hljs-string">&#x27;PATCH&#x27;</span>, <span class="hljs-attr">body</span>: count }).<span class="hljs-title function_">catch</span>(<span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span>
      <span class="hljs-title function_">setCount</span>(prevStore.<span class="hljs-property">count</span>)
    )
  }
}
</code></pre><p>Your components won&#39;t need any changes:</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">import</span> { useStore } <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;./store&#x27;</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">Counter</span>(<span class="hljs-params"></span>) {
  <span class="hljs-keyword">const</span> [count, setCount] = useStore.<span class="hljs-title function_">count</span>()

  <span class="hljs-keyword">return</span> (
    <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>{count}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> setCount((c) =&gt; c + 1)}&gt;Increment counter<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> setCount((c) =&gt; c - 1)}&gt;Decrement counter<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  )
}
</code></pre><p>If you want the optimistic update to be only for one component and not for all, you can register it with:</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">const</span> [count, setCount] = useStore.<span class="hljs-title function_">count</span>(<span class="hljs-number">0</span>, onAfterUpdate)
</code></pre><h3 id="calculated-store-properties">Calculated store properties</h3>
<p>If we want the <code>cart.price</code> to always be a precomputed value of another property, for example from <code>cart.items</code>, we can do it in the <code>onAfterUpdate</code> function.</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> { useStore, getStore } = <span class="hljs-title function_">createStore</span>(
  {
    <span class="hljs-attr">cart</span>: {
      <span class="hljs-attr">price</span>: <span class="hljs-number">0</span>,
      <span class="hljs-attr">items</span>: [<span class="hljs-string">&#x27;apple&#x27;</span>, <span class="hljs-string">&#x27;banana&#x27;</span>],
    },
  },
  onAfterUpdate
)

<span class="hljs-keyword">function</span> <span class="hljs-title function_">onAfterUpdate</span>(<span class="hljs-params">{ store, prevStore }</span>) {
  <span class="hljs-title function_">calculatePriceFromItems</span>()
  <span class="hljs-comment">// ...</span>
}

<span class="hljs-keyword">function</span> <span class="hljs-title function_">calculatePriceFromItems</span>(<span class="hljs-params"></span>) {
  <span class="hljs-keyword">const</span> [price, setPrice] = getStore.<span class="hljs-property">cart</span>.<span class="hljs-title function_">price</span>()
  <span class="hljs-keyword">const</span> [items] = getStore.<span class="hljs-property">cart</span>.<span class="hljs-title function_">items</span>()
  <span class="hljs-keyword">const</span> calculatedPrice = items.<span class="hljs-property">length</span> * <span class="hljs-number">3</span>

  <span class="hljs-keyword">if</span> (price !== calculatedPrice) <span class="hljs-title function_">setPrice</span>(calculatedPrice)
}
</code></pre><p>Your components won&#39;t need any changes:</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">import</span> { useStore } <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;./store&#x27;</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">Counter</span>(<span class="hljs-params"></span>) {
  <span class="hljs-keyword">const</span> [price] = useStore.<span class="hljs-property">cart</span>.<span class="hljs-title function_">price</span>()

  <span class="hljs-comment">// 6€</span>
  <span class="hljs-keyword">return</span> <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>{price}€<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
}
</code></pre><h2 id="learn-more-about-teaful">Learn more about Teaful</h2>
<p>If you want to try it out, I encourage you to go to the <a href="https://github.com/teafuljs/teaful/blob/0.7.0/README.md">README</a> to read the Teaful documentation, see all the options and learn how to get started. There is also an example section where you can try it. We will upload more examples over time.</p>
<h2 id="conclusions">Conclusions</h2>
<p>Teaful is still at an early stage (version 0.x), so there may still be several improvements in the library to make version 1.0 even more tiny, easy and powerful. Any contribution to the library or suggestions will be very welcome.</p>
<p>For the short life of the library, the community is growing fast and I thank all those who have contributed.</p>
<p><a href="https://github.com/danielart">@danielart</a>, <a href="https://github.com/niexq">@niexq</a>, <a href="https://github.com/shinshin86">@shinshin86</a>, <a href="https://github.com/dididy">@dididy</a>. 👏 😊</p>
]]></content:encoded>
            </item>
      </channel>
    </rss>