Part 2 is now live and available here
Overview
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 local first.
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 linear.app 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.
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 slow 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.
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.
Meme Driven Development
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 Local First HTMX.
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 really good 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.
HTMX isn't the right framework but maybe it is
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 HATEOS. 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 "server" 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.
Architecture Overview
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 fetch requests.
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.
A basic HTMX request looks something like this
<button hx-post="/clicked"
hx-trigger="click"
hx-target="#parent-div"
hx-swap="outerHTML"
>
Click Me!
</button>
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.
Stay tuned.
Top comments (0)