New Stack: 11ty, sql.js-httpvfs, and Preact.js

New Stack: 11ty, sql.js-httpvfs, and Preact.js

The Problem

Hello readers of 2coffee.dev, four months have flown by like the wind, and this time I'm back with a new article. How have you been? How are you adapting and changing in this era of AI dominance, where almost everything in work is being automated? Every now and then, a few friends ask if I'm still writing articles. Do I use AI to write? Hmm... definitely not. Why would I use AI to write when anyone can use it to churn out countless articles to their liking? I mean, if they want, they can use AI to generate articles and read them. But if you ever feel the need for something fresh, come back here — because all the content here is crafted by me, with many hours of reflection.

Ah! I recently changed jobs, which has kept me busier, so I'm gradually adapting my habits. For the past month, I've been waking up early to walk, and some days I even squeeze in some work before heading to the office. Waking up early makes the day feel longer, life feel slower, and gives me more time for other things.

As I shared a few weeks ago, I've once again torn down and rebuilt my blog. I know this might sound tiresome to longtime readers. But few people realize how hard it is to decide to start over — nobody likes abandoning something stable for something new without fully assessing the risks. The tech stack powering the blog you're looking at right now? I had never used it before. I didn't know how to deploy it, what problems might arise. Normally, I wouldn't take such risks. But "times make the man" — artificial intelligence and AI Agents proved to be formidable allies in getting this done.

In this era where anyone can write code, few can confidently say they don't need the help of AI. And if they do, they might be missing out on new learning opportunities. Think of AI as an encyclopedia — a vast but fragmented treasure trove of knowledge. How you extract and use that information depends entirely on you. If you know how to search, you'll see a bright learning path ahead.

Rebuilding the blog once again wasn't a hasty decision — it required a great deal of deliberation. On this occasion, I want to share the full story behind it. Along the way, I'll point out some interesting aspects of this new tech stack, and I hope it proves useful to you.

When Deno Deploy Changed Its Pricing

Deno keeps getting better — more stable, more features. It's a Rust-built product that has earned a lot of goodwill in the tech community. Deno Fresh was the framework I used for my blog. It's simple, approachable, and has everything I need, as I mentioned in my article Tear Down and Rebuild — creating a website with minimal JavaScript, only serving JS in a few specific places. Less bloat, fewer resources, faster processing.

Deno Deploy was the platform for running Deno and Deno Fresh applications. Like many platforms, it had a generous free tier. Back then, Deno Deploy offered 1 million requests per month with 100GB of bandwidth — more than enough for a personal blog. With all the essentials covered, what could be better than using everything within the Deno ecosystem?

Two months ago, Deno Deploy announced they were sunsetting the platform (they called it Deno Deploy Classic) to prepare for a new version focused on improved features and a better dashboard. I was excited but also anxious. Excited because it showed the team still cared about the product — investing effort could make it even better than the old version. Anxious because I didn't know how difficult the transition would be, whether it would be backward compatible, and especially whether it would be smooth.

My blog had a rather unique setup. I used Turso as the database, but one fine day Turso was approaching its free tier limit, forcing me to migrate data into JSON files and add logic to read from them. This created a new problem: data in the Threads section was updated daily, so every new post required rebuilding the JSON and uploading it to the server. Over time, I improved this by adding data to KV (a key-value database similar to Redis) as a caching layer, eliminating the need to rebuild. The impending shutdown of Deno Deploy Classic also blocked new deployments. That meant I couldn't continue with my existing deployment approach — the platform no longer accepted new deployments. If I wanted to keep going, I had to move to the new Deploy platform.

Within just a few days, I migrated the entire application from the old system to the new one. It ran smoothly, and I was overjoyed. But just one day later, when I checked the admin dashboard, the "CPU Limits" indicator was sitting at over 30%. I couldn't believe my eyes — after refreshing several times, it was true. This metric shows how much CPU time the app uses for page rendering, since it operates on Server Side Rendering. The platform gives 15 free hours per month; once that's exhausted, you either pay or accept that your site becomes inaccessible.

In the end, I had to point my domain back to the old system in read-only mode so users could still access the site. Meanwhile, I had to find a solution — or a new approach that wouldn't tie me to Deno's platform.

Nursing the Idea of a "Dynamic Yet Static" Website

If you remember, I've mentioned sql.js-httpvfs before — a library for querying SQLite data via HTTP-Range-requests. How it works is fascinating: it uses the page size of the database to read exactly the region containing the data. SQLite organizes data in pages, so when querying, it knows precisely where the data lives without downloading the entire database file. With just a file stored on a server that supports HTTP-Range-requests, when you run an SQL query, it pinpoints the exact start and end positions and fetches only what's needed — that's why the server must support HTTP-Range.

Ever since I stumbled upon this library through an article, I spent time understanding how it works and tried applying it to simple projects. It worked exactly as expected. Truly amazing! From then on, I nurtured the idea of one day creating a "dynamic yet static" website. Meaning, just package the data into an SQLite file, upload it to a storage server, and run SQL queries like normal. This approach has the advantage of more flexible querying and data retrieval compared to "baking" static data into HTML files. There are certain areas where data needs to change continuously — like related articles — to avoid embedding data rigidly into a page and making it stale. That's where sql.js-httpvfs truly shines, independent of any third-party SQL service.

Together with 11ty, I began testing the feasibility of a "dynamic yet static" website for this rebuild.

Meeting 11ty (Eleventy)

11ty is a well-known simple static site generator. Many people use it to generate static HTML pages from a dynamic data source. If you've worked with Next.js, Nuxt.js, etc., you're familiar with the Static Site Generator (SSG) concept. You have a data source, you create templates for your pages, and through the build process, it generates all static HTML pages from that data. Just upload everything to an HTML-serving host — no need for a server to render pages on every visit.

Adopting 11ty for this project was a no-brainer — it's a perfect fit. Generating static HTML files improves page load speed and broadens maintenance options since there are plenty of free hosting services. My only concern was my lack of experience with 11ty — I didn't know how to organize the directory structure or follow best practices. But no matter — the hard stuff can be handled by large language models. Not long ago, Anthropic published an article about Claude Opus 4.6 rewriting a C compiler that successfully compiled the Linux 6.9 kernel. More recently, Anthropic acquired Bun, then launched a campaign to rewrite Bun in Rust... So migrating from Deno Fresh to 11ty was certainly within reach.

That said, I still had to plan this migration very carefully. It wasn't as simple as commanding AI Agents: "Hey! Help me completely convert this project from Deno Fresh to 11ty combined with sql.js-httpvfs"!

The Migration Process

First, you need to understand your old project's structure very well — know what pages it has, the UI, and the specific business logic. Then break it down into clear phases, step by step, guiding the AI to create a detailed plan before writing any code.

It's best to start with the most core functionality to test feasibility. Once that's built, expanding becomes much easier. The core feature I identified for this project was the ability to generate static pages from data. I had an SQLite file containing all article data, article detail templates, and then a single 11ty command to generate the HTML pages. Something like this instruction:

  • "Structure the project with an article detail page similar to the old project, then set up the basic 11ty functions and commands to generate data from SQLite into HTML pages."

Of course, AI had already created the basic project structure for 11ty. Then it began converting the article detail page UI and figuring out how to query data from SQLite as input for the static page generation process.

After many rounds of refinement, it finally ran successfully. Then I asked it to complete the remaining pages.

  • "Do the same for the homepage..."
  • "Do the same for the Posts page..."
  • ...

After creating all static pages, it was time for the harder part — integrating sql.js-httpvfs for dynamic data queries. Imagine: 11ty only generates static pages, so where would the data come from when users scroll to the bottom and it auto-loads more content? Initially, I identified the areas needing dynamic queries: infinite scroll on the homepage, article comments, related articles, and a few other spots. I already had the SQLite file as input data on my machine (exported from Turso — the SQLite database the blog had been using). Although it was large (about 16MB containing all tables), at this stage the goal was to speed up integration rather than optimize. I used Preact.js for client-side interactions — the dynamic data would be fetched via JavaScript, along with the sql.js-httpvfs library to query SQL from the SQLite file hosted on the server.

After many rounds of testing, everything finally clicked into place. Only when all major features were working did I feel confident the project was viable. Then I started improving the structure and optimizing the workflow — organizing code more cleanly and efficiently, making it maintainable for future feature development.

After two weeks of non-stop work — trial, error, fix, repeat — the result is what you see here: the site has been fully migrated from Deno Fresh to 11ty combined with sql.js-httpvfs and Preact.js. There are still some bugs and unfinished features, but overall everything works as expected, and I have a place to keep writing and sharing with my readers.

Summary

After two weeks of non-stop work, I completed the migration of my blog from Deno Fresh to a new tech stack: 11ty as the static site generator, sql.js-httpvfs for querying SQLite data directly in the browser, and Preact.js for client-side interactions. This was the most interesting rebuild I've ever done, not just because of the new technology but because of how I actively took control of the process with AI assistance.

What made it all worthwhile was turning the idea of a "dynamic yet static" website into reality. With just an SQLite file hosted on GitHub Pages, combined with sql.js-httpvfs and Preact.js, I can run dynamic data queries without any third-party cloud database service. This approach is both cost-effective and makes the blog snappier.

If you'd like to learn more about the technology and implementation, feel free to leave a comment below. And you — have you ever tried any novel approaches for your own projects?