<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Elijah</title>
    <description>The latest articles on Forem by Elijah (@elijahm).</description>
    <link>https://forem.com/elijahm</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1752636%2F1b708d0d-7d89-4711-bfa9-ce811d12498e.png</url>
      <title>Forem: Elijah</title>
      <link>https://forem.com/elijahm</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/elijahm"/>
    <language>en</language>
    <item>
      <title>Local First HTMX Part 2</title>
      <dc:creator>Elijah</dc:creator>
      <pubDate>Sun, 28 Jul 2024 16:40:02 +0000</pubDate>
      <link>https://forem.com/elijahm/local-first-htmx-pt2-gp3</link>
      <guid>https://forem.com/elijahm/local-first-htmx-pt2-gp3</guid>
      <description>&lt;h1&gt;
  
  
  Overview
&lt;/h1&gt;

&lt;p&gt;Welcome back to Meme Driven Development (MDD) for the second and more technical entry in our series on &lt;a href="https://elijahm.com/posts/local_first_htmx/" rel="noopener noreferrer"&gt;Local First HTMX&lt;/a&gt;. In this post I'll go over the technical details for my Local First HTMX Todo App. In the first post we discussed the general ideas and the general architecture, I'll recap just a little bit of that here and then start diving into the technical details of the project. If you, like me, just want to see the &lt;a href="https://github.com/elijahmorg/lhtmx" rel="noopener noreferrer"&gt;github here ya go&lt;/a&gt;. &lt;br&gt;
&lt;strong&gt;&lt;em&gt;Disclaimer: I am not an experienced web dev — I have literally written far more assembly than I have javascript. I am just having fun messing around with technology and learning a little bit more about the web.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In this example we are going to side step a lot of the complexity of local first, syncing data, etc. &lt;/p&gt;
&lt;h1&gt;
  
  
  Architecture Overview
&lt;/h1&gt;

&lt;p&gt;For our architecture we will have the main thread, a service worker, and a remote server. Nothing crazy so far. &lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9kh8j6ndsw8ncj10mcaj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9kh8j6ndsw8ncj10mcaj.png" alt="Image description" width="800" height="406"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Going in one level of detail in the main thread we have HTML and some HTMX — not that much as this is a very simple &lt;a href="https://github.com/chasefleming/elem-go/tree/main/examples/htmx-fiber-todo" rel="noopener noreferrer"&gt;example app that I modified to make Local First&lt;/a&gt;. We have a service worker that is running a Go program compiled to WASM. This service worker is responsible for proxying the fetch requests and returning rendered HTML to the main thread. The server runs the same code. In a real world example the server would have some additional code and be authoritative, but I'm bypassing that for the purpose of this POC. If for some reason the service worker is not installed when a fetch request is made, that request will go to the server, be handled by the server and rendered HTML will be returned just as if it was a SSR app. &lt;br&gt;
The difference is that once the service worker is installed we do not have to make a full round trip request to the server, instead we make a very fast request to the service worker. Finally in the background we have a simple sync that will run to keep our data somewhat in sync. &lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzf1x1eojsr68dde510e5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzf1x1eojsr68dde510e5.png" alt="Image description" width="800" height="406"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;
  
  
  Web Main Thread
&lt;/h1&gt;

&lt;p&gt;If you run the app on your local host and then go to the URL you'll get a page load. This page load should be noticeably &lt;strong&gt;&lt;em&gt;slow&lt;/em&gt;&lt;/strong&gt;. The reason is I've added a 1 second delay on handling all requests to simulate a case where you have a great deal of network latency. This demonstrates a bad case for the initial page load feeling slow. Any additional requests made to the server will incur this same 1 second artificial delay. &lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1cg86c19owyd10tk6s1n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1cg86c19owyd10tk6s1n.png" alt="page load diagram" width="800" height="672"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This page is immediately usable because it has the benefits of being fully SSR. &lt;strong&gt;We can add todos, etc. while the service worker is loading it because the calls will go directly to the server and rendered HTML will be returned.&lt;/strong&gt;&lt;br&gt;
In this initial page load we load a script called &lt;code&gt;start_worker.js&lt;/code&gt;. It contains a lot of very important logic. I'll go over this in its entirety.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;serviceWorker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sw.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ok. That actually wasn't bad at all. Here's some &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerContainer/register" rel="noopener noreferrer"&gt;MDN docs&lt;/a&gt;. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;loads from server rendered&lt;/li&gt;
&lt;li&gt;usable&lt;/li&gt;
&lt;li&gt;starts service worker&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Service Worker
&lt;/h1&gt;

&lt;p&gt;A service worker is a separate thread in the browser that has special privileges. &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API#" rel="noopener noreferrer"&gt;MDN documentation&lt;/a&gt; describes it: "&lt;em&gt;Service workers essentially act as proxy servers that sit between web applications, the browser, and the network (when available)&lt;/em&gt;."&lt;br&gt;
Now the service worker does have many of the same restrictions as a web worker, such as no access to the DOM. It can also only be used over https because of security concerns. &lt;br&gt;
The main point for us is that a service worker acts as a proxy and can choose how to handle network requests. This is what allows us to use HTMX and do a post to a URL and capture that request and render the HTML in client or as I like to call it &lt;strong&gt;SSR in the browser&lt;/strong&gt;. &lt;br&gt;
As we get into some Javascript code snippets I just want to remind you, dear reader, that JS is not my mainstay. I've written more assembly than I've written JS. So if something stands out as particularly bad please tell me. &lt;/p&gt;

&lt;p&gt;At the beginning of our &lt;code&gt;sw.js&lt;/code&gt; file we need to import &lt;code&gt;wasm_exec.js&lt;/code&gt; which provides bindings for Go to call.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;importScripts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;wasm_exec.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I could not find much in the way of official documentation on this, but I believe it provides the &lt;code&gt;syscall/js&lt;/code&gt; &lt;a href="https://pkg.go.dev/syscall/js" rel="noopener noreferrer"&gt;package&lt;/a&gt; bindings. It is important your &lt;code&gt;wasm_exec.js&lt;/code&gt; exactly matches your Go version. The best way to do this is to copy it from your Go installation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;go &lt;span class="nb"&gt;env &lt;/span&gt;GOROOT&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;/misc/wasm/wasm_exec.js"&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="sb"&gt;```&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;% endraw %&lt;span class="o"&gt;}&lt;/span&gt;


Next we need to fetch our WASM binary and &lt;span class="nb"&gt;install &lt;/span&gt;it and start it.
&lt;span class="o"&gt;{&lt;/span&gt;% raw %&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="sb"&gt;```&lt;/span&gt;js
  WebAssembly.instantiateStreaming&lt;span class="o"&gt;(&lt;/span&gt;fetch&lt;span class="o"&gt;(&lt;/span&gt;wasm&lt;span class="o"&gt;)&lt;/span&gt;, go.importObject&lt;span class="o"&gt;)&lt;/span&gt;.then&lt;span class="o"&gt;((&lt;/span&gt;
    &lt;span class="o"&gt;{&lt;/span&gt; instance &lt;span class="o"&gt;}&lt;/span&gt;,
  &lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; go.run&lt;span class="o"&gt;(&lt;/span&gt;instance&lt;span class="o"&gt;))&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We also need to add an event listener for "fetch". This registers that we are proxying network requests. For more in depth info see &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API/Using_Service_Workers#custom_responses_to_requests" rel="noopener noreferrer"&gt;MDN&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fetch&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;pathname&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;pathname&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/wasm_exec.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;pathname&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/sw.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
      &lt;span class="nx"&gt;pathname&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/start_worker.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;respondWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hostname&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;localhost&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;respondWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;handlerPromise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// For requests to other domains, just pass them along to the network&lt;/span&gt;
      &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;respondWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I do a little bit of hacking to ignore certain requests and pass them along to the underlying network. I don't think this part is particularly clean, but I left it like this to show how I handled an error where once the service worker was registered I couldn't load those files because I was proxying the network requests. &lt;br&gt;
The real magic is here.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;      &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;respondWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;handlerPromise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where the definition of that is.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handlerPromise&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;setHandler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;wasmhttp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;setHandler&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So what is happening is I'm using a package called &lt;a href="https://github.com/nlepage/go-wasm-http-server" rel="noopener noreferrer"&gt;go-wasm-http-server&lt;/a&gt;. It takes the JS network request and does some magic (I am not going to get into it this blog post because I don't fully understand all the internals yet) and then we get a network http request in Go. &lt;br&gt;
So let's start talking about our Golang some more in the next section. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;service worker starts&lt;/li&gt;
&lt;li&gt;loads WASM&lt;/li&gt;
&lt;li&gt;listen for fetch events&lt;/li&gt;
&lt;li&gt;does a little bit of routing logic (this is probably a skill issue)&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;
  
  
  Golang WASM
&lt;/h1&gt;

&lt;p&gt;I am using Go for Local First HTMX because of a few reasons. The first and most important is Go can be compiled to WASM and run in the browser. The second and also important is I like writing Go and I have a fair bit of experience with the language from using it over the years. It is a fast language, it is simple to write, understand and has excellent tooling built around it. I think if I was truly doing all of the memes this should be written using Rust, but that is not a journey I need to dive into at the moment. &lt;strong&gt;TLDR - I like writing Go.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The big downside of using Go in the browser is that the WASM binary size is large. I think mine is like 10mb at the moment. That is pretty large. There are some optimizations you can do for the binary size, but it remains pretty large no matter what you do. I did play around with &lt;a href="https://tinygo.org/docs/reference/lang-support/stdlib/#nethttp" rel="noopener noreferrer"&gt;TinyGo but it does not support a lot of the standard library packages we need&lt;/a&gt;. So the argument I'll make for why having a 10mb WASM download is not &lt;em&gt;that&lt;/em&gt; bad is that this site will load and be useful before the WASM download completes. You can even navigate around while it is downloading. It is a fully SSR site, but after the service worker is up and running you get a full local first experience. I would love to see the Go WASM binary get trimmed down by a lot — tho Go binaries are just large because of the language's design. &lt;/p&gt;
&lt;h2&gt;
  
  
  Go WASM HTTP Server
&lt;/h2&gt;

&lt;p&gt;The real magic is the &lt;a href="https://github.com/nlepage/go-wasm-http-server" rel="noopener noreferrer"&gt;go-wasm-http-server&lt;/a&gt; package. This is what enables us to have a Go server that can then route the network requests from the &lt;code&gt;sw.js&lt;/code&gt;. This package is a little bit old, only has 286 stars on Github and hasn't been updated in 2 years. But it works so we are going to use it. I may attempt to dive into the internals in another blog post in the future, we will just have to see what the future holds and where my interests lead. &lt;/p&gt;
&lt;h2&gt;
  
  
  Compiling for WASM/JS
&lt;/h2&gt;

&lt;p&gt;I was initially using Go &lt;a href="https://github.com/gofiber/fiber" rel="noopener noreferrer"&gt;Fiber&lt;/a&gt;, because that is what the &lt;a href="https://github.com/chasefleming/elem-go/tree/main/examples/htmx-fiber-todo" rel="noopener noreferrer"&gt;example app&lt;/a&gt; I was piggybacking off of used. It however does not compile when building for the browser. I tested out &lt;a href="https://github.com/gin-gonic/gin" rel="noopener noreferrer"&gt;Gin&lt;/a&gt; and &lt;a href="https://github.com/labstack/echo" rel="noopener noreferrer"&gt;Echo&lt;/a&gt;. They both compiled and since my current project at work uses Echo I chose to use that here. &lt;/p&gt;
&lt;h2&gt;
  
  
  Go in the browser
&lt;/h2&gt;

&lt;p&gt;Let's start digging into the code a little. First thing you can see is we have a build constraint — this file is only built when we compile for JS target. We import normal echo imports and wasmhttp server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;//go:build js&lt;/span&gt;

&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;api&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/labstack/echo/v4"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/labstack/echo/v4/middleware"&lt;/span&gt;
    &lt;span class="n"&gt;wasmhttp&lt;/span&gt; &lt;span class="s"&gt;"github.com/nlepage/go-wasm-http-server"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next we have our &lt;code&gt;EchoStart&lt;/code&gt; which registers routes and handlers and then takes the &lt;code&gt;echo.Server.Handler&lt;/code&gt; which is of type &lt;code&gt;http.Handler&lt;/code&gt; and passes that into &lt;code&gt;wasmhttp.Serve()&lt;/code&gt;. Then we do a bit of weirdness &lt;code&gt;select {}&lt;/code&gt;. Why do we do this? Well the &lt;a href="https://github.com/nlepage/go-wasm-http-server?tab=readme-ov-file#step-1-build-to-jswasm" rel="noopener noreferrer"&gt;wasmhttp documentation&lt;/a&gt; says to and it didn't work when I took it out.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;EchoStart&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// Echo instance&lt;/span&gt;
    &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;echo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c"&gt;// Middleware&lt;/span&gt;
    &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;middleware&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Logger&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;middleware&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Recover&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SyncToServer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;// Routes&lt;/span&gt;
    &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GET&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;renderTodosRoute&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;POST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/toggle/:id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;toggleTodoRoute&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;POST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/add"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;addTodoRoute&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;// Start server&lt;/span&gt;
    &lt;span class="n"&gt;wasmhttp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Serve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Server&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Handler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Other than passing the handler to &lt;code&gt;wasmhttp.Serve&lt;/code&gt; and the empty select this looks like very normal router code that you would have on the server. This is part of the idea that I am going for in this Local First HTMX app: we can write code once that runs in the browser and on server. &lt;br&gt;
Now we have our &lt;code&gt;main.go&lt;/code&gt; for the service worker &lt;em&gt;"server"&lt;/em&gt;. This is very simple, it merely syncs data from the server and then starts the &lt;em&gt;server&lt;/em&gt; code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetData&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"error syncing data with server"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;EchoStart&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For handling data syncing while the app is running I have a middleware function &lt;code&gt;SyncToServer&lt;/code&gt; that syncs the current data state with the server in a Go routine. This is a very rudimentary data sync method and it definitely has issues. It is another idea I'd like to follow up with to see how to do this better and more robustly. That is not the main point of this post, so I just got something very basic working.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;SyncToServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;next&lt;/span&gt; &lt;span class="n"&gt;echo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HandlerFunc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;echo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HandlerFunc&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="n"&gt;echo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;SyncData&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;SyncData&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="n"&gt;syncDataRoutine&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  A single language stack (kinda)
&lt;/h1&gt;

&lt;p&gt;There are a &lt;strong&gt;&lt;em&gt;lot of ways&lt;/em&gt;&lt;/strong&gt; to render HTML on the client. The reason I have chosen this architecture is because there is a movement to write JS or really TS on both the browser and the server and that way you can have a single language stack. You can see &lt;a href="https://www.laws-of-software.com/laws/atwood/" rel="noopener noreferrer"&gt;Atwood's law&lt;/a&gt; for the first instance of this thinking. &lt;br&gt;
This argument has its merit, but I think it also has serious downsides and so as this is all MDD I wanted to do a single language stack. Obviously this is not truly a single language, but most of the logic is in Go. It also has the distinct upside that the code you write and run on the server will be far faster than JS or TS. &lt;br&gt;
I also wanted to make sure that you only have to write your logic once and then run it in the browser and on the server. This gets back to that idea of making our technology stack simple and productive. This is at the heart of HTMX's philosophy. &lt;br&gt;
I work in a big enterprise on big projects with huge teams of engineers. We do a lot of unnecessary work and add process to help compensate for all of this complexity. I believe there needs to be concerted thought put into designing software that enables individuals and small teams to create great software that scales. &lt;br&gt;
A single language stack has benefits especially if you can write something once that can both work for your client side rendering and server side rendering. Or as I like to say &lt;strong&gt;&lt;em&gt;SSR in your browser&lt;/em&gt;&lt;/strong&gt;. I think this is doubly beneficial if the language you write is highly performant on the server. &lt;/p&gt;
&lt;h2&gt;
  
  
  What I mean by single language stack
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;main.go&lt;/code&gt; running on the server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/elijahmorg/lhmtx/api"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;EchoStart&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;EchoStart&lt;/code&gt; for the server is slightly different than for the browser.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;EchoStart&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"server side code"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;echo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;middleware&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Logger&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;middleware&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Recover&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ServerDelay&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;// Routes&lt;/span&gt;
    &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GET&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;renderTodosRoute&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;POST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/toggle/:id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;toggleTodoRoute&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;POST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/add"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;addTodoRoute&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GET&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/sync"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;getTodos&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;POST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/sync"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;syncTodos&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Static&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"../../public/"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;":3000"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;The different &lt;code&gt;EchoStart&lt;/code&gt; functions are compiled based on build constraints. &lt;br&gt;
As you can see I have very little custom code to handle the difference between my &lt;em&gt;server&lt;/em&gt; code and &lt;em&gt;client&lt;/em&gt; code. I think this is pretty nifty. I am sure that if I did more complex stuff, with real databases, auth, etc. I would have to handle a lot more corner cases and this &lt;strong&gt;would&lt;/strong&gt; become more complex. All that said, I think this is pretty interesting and I hope it makes you think about the possibilities of what you could do with something like this. &lt;/p&gt;

&lt;h1&gt;
  
  
  Local First
&lt;/h1&gt;

&lt;p&gt;Local First HTMX has the benefits of both being a SSR web framework. When the site first loads it behaves like an SSR. Here is an example of it in action. &lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1cg86c19owyd10tk6s1n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1cg86c19owyd10tk6s1n.png" alt="site load before service worker is installed and working. site acts like an SSR." width="800" height="672"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then after the service worker is installed and running it behaves like a local first app. &lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F98erraisaibugmn686gv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F98erraisaibugmn686gv.png" alt="diagram of request after it begins acting like a local first app" width="800" height="392"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So now we have a Local First HTMX app. It can behave like both an SSR web app giving it benefits for SEO, first page load, etc. and a local first app. Thanks for reading this, I hope you enjoyed it and you will check back in the future for my next post. &lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>go</category>
      <category>htmx</category>
    </item>
    <item>
      <title>Local First HTMX pt1</title>
      <dc:creator>Elijah</dc:creator>
      <pubDate>Mon, 22 Jul 2024 16:33:24 +0000</pubDate>
      <link>https://forem.com/elijahm/local-first-htmx-pt1-109p</link>
      <guid>https://forem.com/elijahm/local-first-htmx-pt1-109p</guid>
      <description>&lt;p&gt;&lt;a href="https://elijahm.com/posts/local_first_htmx_part2/" rel="noopener noreferrer"&gt;Part 2 is now live and available here&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Overview
&lt;/h1&gt;

&lt;p&gt;There is a common refrain on the internet that things have gotten worse and are continuing to get worse. There is a proliferation of horrible jumpy loading ads on every website, every search engine throws a crappy AI summary in front of your search result, every site/webapp seems to have gotten slower and slower. I cannot provide a solution for all of that, but I can point to a better paradigm for web site and web app design. That paradigm is &lt;strong&gt;local first&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Local first is a design principle for web apps where the UI and data are co-located and changes to the data are synced with the remote server. Local first apps feel snappy and highly performant because they do not require a network RTT between a users action and rendering the result of the action. I recommend playing around with &lt;a href="https://linear.app/" rel="noopener noreferrer"&gt;linear.app&lt;/a&gt; to experiene what a first class local first app feels like. I won't spend much time trying to convince about bad web apps - because if you are ignorant and happy I don't want to ruin that bliss.&lt;/p&gt;

&lt;p&gt;If you are familiar with Jira or Github issues you should be able to immediately tell how stark of a difference a local first app can be. Jira is slow because as far as I can tell it is just &lt;strong&gt;&lt;em&gt;slow&lt;/em&gt;&lt;/strong&gt; and it loads a lot of data slowly and if you click away and then go back you have to reload all of that same data again. Github is a SSR webapp meaning that the html is generated on the server and then sent to you. This means any interaction usually requires a complete round trip between your browser and the server which is usually very noticeable. Ironically Github's slow SSR performs much better than Jira in my experience - they do different things but gosh I hate using Jira. I can only hope that some day I'll be able to use Linear at work and it will be just as fast as it is today.&lt;/p&gt;

&lt;p&gt;I will pause here and just clarify that almost any app architecture can end up being painfully slow if implemented poorly. I would strongly argue that most websites, webapps, etc. that we visit daily are implemented poorly. There a variety of techniques that can be employed in all these different architectures (traditional SPA, SSR, etc) but local first provides the most upside as an architecture when it relates to performance.&lt;/p&gt;

&lt;h1&gt;
  
  
  Meme Driven Development
&lt;/h1&gt;

&lt;p&gt;That was more serious than I intended it to be so let's dive into some Meme Driven Development (MDD). Let's get into the main course of this post and talk about &lt;strong&gt;&lt;em&gt;Local First HTMX&lt;/em&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fihbo3exz12ys42uw3dbs.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fihbo3exz12ys42uw3dbs.jpeg" alt="mr htmx laser horse thumbs up"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;HTMX is... well a meme and also possibly serious, I am not sure if anyone really knows. HTMX is an anti-javascript javascript front end framework/library (idk frontend people use those terms very loosely). More importantly it is a &lt;strong&gt;&lt;em&gt;really good&lt;/em&gt;&lt;/strong&gt; meme and that is key to MDD. So I thought I should combine HTMX and local first to create something truly awful yet beautiful. I am not necessarily recommending this approach, but I am excited to share what I've done to create the first Local First HTMX Todo app.&lt;/p&gt;

&lt;h1&gt;
  
  
  HTMX isn't the right framework but maybe it is
&lt;/h1&gt;

&lt;p&gt;HTMX's goal to simplify frontend development while still maintaining a good level of interactivity. The geneal idea of HTMX is that your HTML will be rendered by the backend — à la Server Side Rendering. The technical term is hypermedia as the engine of state of &lt;a href="https://htmx.org/essays/hateoas/" rel="noopener noreferrer"&gt;HATEOS&lt;/a&gt;. If you recall that SSR (needing a RTT to the server for every interaction) has performance issues and can cause websites to feel sluggish (it is hard to fight the speed of light). If you are just sprinkling in interactivity it can work. But and this is the key idea of Local First HTMX - you don't have to render the HTML on the backend. You can build a &lt;em&gt;"server"&lt;/em&gt; and compile it to WASM and run it in the browser. This would give you all the snappiness of a first class Javascript Local First SPA with none of the JS — well less of the JS. The goal is not to avoid JS but to have a simpler app.&lt;/p&gt;

&lt;h1&gt;
  
  
  Architecture Overview
&lt;/h1&gt;

&lt;p&gt;To recap we are building a Local First HTMX app by compiling our SSR code to WASM and then running that in a service worker. Briefly and possibly incorrectly let me explain a few things about browsers. There is a main thread, this is where your JS and HTML stuff normally happens. The main thread is what has access to the DOM and can actually render content. Browsers have added many features, but I want to mention two. The first is web workers, which lets your run code in a different thread that has limited permissions (no access to the DOM). The second is a service worker - which is like a web worker but has an important disctinction. A service worker can be configured to intercept all &lt;strong&gt;fetch&lt;/strong&gt; requests.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9l5f6o4pp0uxxl1222gd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9l5f6o4pp0uxxl1222gd.png" alt="Overview of architecture showing browser main thread, service worker and remote server"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The service worker can do what it wants with them from proxying them, looking at cache, or handling the request itself. This is what I want to take advantage of - I want to proxy all fetch requests and optionally choose to render HTML and send it back.&lt;/p&gt;

&lt;p&gt;A basic HTMX request looks something like this&lt;/p&gt;

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

&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;hx-post=&lt;/span&gt;&lt;span class="s"&gt;"/clicked"&lt;/span&gt;
    &lt;span class="na"&gt;hx-trigger=&lt;/span&gt;&lt;span class="s"&gt;"click"&lt;/span&gt;
    &lt;span class="na"&gt;hx-target=&lt;/span&gt;&lt;span class="s"&gt;"#parent-div"&lt;/span&gt;
    &lt;span class="na"&gt;hx-swap=&lt;/span&gt;&lt;span class="s"&gt;"outerHTML"&lt;/span&gt;
&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    Click Me!
&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Normally this would send an HTTP request to the sever, but we want to intercept this request in the service worker, handle the request and return HTML. Then in the background the service worker can sync data with the server while maintaining its local data store. In a follow up post I'll go over the implementation details of how I did this, some issues I encountered, and then talk about some further ideas.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Figf4nxyu19k46bsp4hhk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Figf4nxyu19k46bsp4hhk.png" alt="diagram showing /clicked post being handled by service worker"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Stay tuned.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>go</category>
      <category>htmx</category>
    </item>
  </channel>
</rss>
