Building a Sub-1KB Web Framework — Live Debugging with AI
This is a raw live coding session where I work on rappstack — my composable web application framework built on rmemo and relysjs. The session covers infrastructure migration, architecture walkthrough, and real debugging with Claude Code.
Fair warning: This is an unscripted session. There's genuine frustration, dead-end debugging, and the kind of raw developer experience that polished tutorials edit out. That's the point.
The Performance Story
The headline number: sub-1KB for the reactive framework itself (relementjs — check the size-limit in package.json). But what matters more is what that translates to in real applications:
- briantakita.me — 1.2KB total JavaScript for the full interactive site, including search, hydration, and reactive state
- brookebrodack.com — 4.2KB total JavaScript driving video players, animations, and interactive content
These aren't framework overhead numbers — they're the total JS payload including actual application features. For comparison, React's hello world starts around 50KB before you write a single line of business logic.
Architecture: Composable Layers
Rappstack splits everything into three layers:
| Layer | Purpose | Example Package |
|---|---|---|
| domain | Business logic, shared types | @rappstack/domain--any--blog |
| server | Server-side rendering, middleware | @rappstack/domain--server |
| browser | Hydration, client interactivity | @rappstack/ui--browser |
This separation means your client bundle only contains browser-layer code. Server logic never ships to the browser. Domain logic is shared but tree-shaken per target.
Hyop: Minimal Hydration
The hydration system is called hyop (hydration operation). Instead of shipping a full virtual DOM diffing engine:
- Server renders HTML with
data-hyopattributes - A tiny script (~200 bytes) scans for hyop attributes on load
- Each hyop maps to a function that takes the element and adds interactivity
- No virtual DOM, no reconciliation, no framework runtime
This is conceptually similar to htmx but with a build system — you get module imports, tree-shaking, and TypeScript, at the cost of needing ESBuild (or Bun build).
Infrastructure Migration
This session captures the middle of migrating from:
- Contabo VPS → CloudFlare Workers (better edge performance, easier automation)
- Elysia → Hono (more portable across runtimes — Bun, Node, Deno, CloudFlare Workers natively)
- Local assets → CloudFlare R2 (edge-cached static assets via custom ESBuild plugins)
The @nicholasgasior/esbuild-plugin-object-store-assets plugin handles uploading build output to R2 and rewriting asset URLs.
ESBuild Plugin System
One of the more interesting architectural decisions: custom file imports via ESBuild plugins.
.md.ts— Markdown files compiled to ES modules with frontmatter + rendered HTML as exports.css.ts— CSS generated programmatically via TypeScript factory functions.svg.ts— SVG generated via TypeScript (used for CAD drawings, diagrams)
The generic esm-file plugin lets any file type become an importable module as long as you provide a factory function that returns the content. Blog posts are just markdown files auto-imported via a generated index.
Debugging with Claude Code
The most relatable part of the session: spending 30+ minutes debugging duplicate blog post imports and a broken code generator. The bugs:
statSyncis not a function — The code usedfs.statSyncwhich doesn't exist in the Bun compat layer. Claude instantly spotted it and replaced withexistsSync.- Missing
.mdextension pattern — The code generator only matched.ts,.js,.tsxfiles. Claude added.mdto the glob pattern.
The contrast is stark: I spent significant time manually navigating file trees trying to understand the problem. Claude identified and fixed both bugs in seconds. As I said in the video: "Before Claude, I probably spent like a couple hours trying to figure this out."
Reflections
Some candid takeaways from the session:
- TypeScript fatigue — Coming back after 2+ years in Python/Rust: "the luster has worn off." The IDE navigation issues (IntelliJ not jumping to implementations, only type definitions) add friction.
- Framework DX debt — Rappstack works and the output is impressively small, but the developer experience needs polish. Custom conventions (trailing underscores for factory functions,
b_prefix for reactive signals) create a learning curve. - AI as debugging accelerator — The session demonstrates both the power and the pattern: try manually, hit friction, hand off to Claude, get instant results. The AI doesn't replace understanding, but it dramatically reduces the time from "I know something's wrong" to "here's the fix."
Links
- rappstack packages — NPM organization
- rmemo — Reactive memo library
- relementjs — Sub-1KB reactive element library
- briantakita.me — 1.2KB JS blog
- brookebrodack.com — 4.2KB JS interactive site
- Claude Code — AI coding assistant used throughout
- agent-doc — Interactive document sessions (briefly shown)
