❯ parkerjones.dev

Static Can Be Dynamic: A Real Database in the Browser Instead of an SPA

2026-06-29
Parker Jones and Claude Opus 4.8 in software
#wasm , #databases , #static-site , #zola , #frontend , #browser and #local-first
6 minute read

Somewhere along the way we decided that "dynamic website" had to mean a few megabytes of JavaScript framework, a build pipeline with opinions, and a server to feed it — all to render a list that changes. This series is about a heresy: what if a static site could be genuinely dynamic, with a real database doing the work in the browser, and no SPA at all?

I don't mean "static site with a sprinkle of JavaScript." I mean the actual thing: a site that builds to plain files, deploys for free to a dumb host like GitHub Pages, and then — in the reader's tab — boots a real database, runs real queries, and drives real interactive state. No backend. No framework runtime. No node_modules the size of a small moon. Just files, and a database that happens to live in the browser.

That sounds slightly unhinged if you've spent the last decade in the web's mainline, so let me explain how we got somewhere that makes it sound unhinged — and what quietly changed to make it reasonable again.

How "dynamic" got so expensive

The web started server-rendered. You asked for a URL, a server did some work, and you got HTML. Simple, cheap, a little clunky.

Then we wanted apps, not pages — Gmail, Maps, the whole "desktop software in a tab" dream — and the single-page application was born. Render once, then never reload; mutate the DOM in place; keep all the state on the client. Good idea! It solved a real problem.

But the SPA dragged a tail behind it, and the tail kept growing. If the client holds the state, you need a way to manage that state, so: a state library. If the client renders everything, you need a way to keep the UI in sync with the state, so: a framework with a reconciler. If you're shipping all that JavaScript, you need to bundle, split, tree-shake, and minify it, so: a build toolchain with its own gravitational field. And because the client can't be trusted with the real data, you still need a server and an API to talk to. We set out to escape the server and ended up with the server plus a second, heavier runtime bolted onto the browser.

The result is a genuinely strange status quo: to put a sortable, filterable list of forty blog posts on a page, the default 2026 move is to reach for a framework, a state manager, a bundler, and probably a hosting platform with a billing relationship. That's a lot of apparatus for "show me the posts tagged rust, newest first."

None of this is wrong, exactly. For an actual application — collaborative, real-time, deeply interactive — that machinery earns its place. The problem is that we now reach for it by reflex, for things that aren't that, because "dynamic" and "SPA" fused into the same word in our heads.

What quietly changed

Two things shifted while we weren't looking, and together they reopen a door I thought was closed.

The first is WebAssembly grew up. You can now take a database engine written in C or Rust — SQLite, DuckDB, Postgres, SurrealDB — compile it to WASM, and run the actual engine, query planner and all, inside a browser tab. Not a toy. Not a key-value shim pretending to be a database. The real thing, with a real query language, executing locally with no network round-trip.

The second is that static hosting got incredible and free. GitHub Pages, Cloudflare Pages, Netlify's free tier — a CDN-backed home for your files at zero cost, with none of the operational weight of running a server. The catch was always that "static" meant "inert": you could serve files fast and free, but the moment you wanted dynamic behavior, you fell off the cliff into SPA-and-server land.

Put those two together and the cliff has a bridge. A static host can serve any file — including a file that is a database, or the data to seed one. Boot that database in the browser, and the dynamic behavior you wanted lives entirely on the client, querying local data, with the static host none the wiser. You get the interactivity of an app and the deployment story of a brochure.

The database becomes your state layer. Your "API" is a query you run against data that's already in the tab. The framework, the state library, the server — a surprising amount of the apparatus turns out to be optional once the data is local and queryable.

The bet of this series

So that's the thesis I want to chase, out loud, across a few posts: a static site generator plus a database in the browser can do a lot of what we currently throw an SPA at — for free, with no server, and far less machinery.

I'm not claiming it wins everywhere. I'm claiming the reflex is miscalibrated, and the only way to find out where the line actually is is to build on the idea and report back honestly. This blog itself is the first lab rat: it's a Zola static site that ships a real database to your browser. You're standing in the experiment right now.

Here's where the series goes:

A note on how this gets made, since it's a running thread here: I write these with an AI model as a partner, and I'm transparent about it — including the times it leads me confidently down a wrong path, which it does, and which I'll point out when it happens. Part of the fun of doing this in public is that I genuinely don't know how far the heresy holds. Static can be dynamic. Let's find out how dynamic.

— Parker Jones, parkerjones.dev