Vanilla HTML beats your framework
I rebuilt felixhellstrom.com in vanilla HTML over a weekend, after three years of Astro / Next.js / SvelteKit rewrites. The new site is faster to load, faster to edit, and a tenth of the size of the previous build. I have not missed a single framework feature.
The actual reason I switched isn't on any of the bullet-point lists you'll see in framework debates. It's that I work with a fleet of AI agents as coworkers, and vanilla HTML is the right primitive for working WITH agents. The categories of friction this article walks through, framework taxes, build pipelines, abstraction layers between the file and the browser, are friction for me. They're a much bigger tax when an agent is paying the cost on every iteration of an edit loop.
This article is about what changed and why I expect to stay vanilla for at least the next two years.
The framework tax I stopped paying
Every framework levies the same set of taxes whether or not you need the features behind them.
You pay in install time: npm install for a Next.js site is around 400 dependencies and a measurable fraction of your machine's free disk. You pay in build time: a static export still has to walk the framework's bundling pipeline before it produces an HTML file. You pay in cognitive overhead: each page is a .tsx file that compiles to HTML, so the file you see is not the file the browser sees, which is fine until you're in a hurry and need to check what actually shipped.
For a solo project where the deliverable is HTML, all of those taxes are paid for features you usually don't use. I'm not running A/B tests. I don't need server-side rendering for a portfolio. I don't need Suspense boundaries. I don't need code-splitting for a five-page site. I don't need image optimization more sophisticated than running sips -Z 1600 once.
What I do need is to ship an HTML file. Vanilla HTML lets me do that without paying any of those taxes.
What vanilla HTML actually is in 2026
Two things have changed since I last took vanilla HTML seriously, around 2018.
First, CSS has caught up. Container queries, subgrid, scroll-driven animations, native nesting via &, design-token primitives via custom properties. There is almost nothing in a 2026 framework's component library that you cannot build cleanly in CSS. The clamp() function alone has retired three CSS-in-JS libraries in my codebase.
Second, modern browsers' baseline is high enough that polyfills mostly aren't needed. I write aspect-ratio, gap, grid-template-columns, and prefers-color-scheme directly. Last year's React-team-blessed solutions are now five lines of CSS, and they are five lines you can read.
Vanilla HTML in 2026 is not the same thing as vanilla HTML in 2018. The capability gap that justified frameworks has narrowed.
Agents read and write HTML, not frameworks
The thing I underweighted when I switched: how much faster the agentic loop runs against vanilla HTML.
When an agent edits a .tsx page, the artifact it produces is one transformation away from what the browser sees. The agent has to model the framework's compile step, the bundler's chunking, the runtime's hydration, plus the actual semantic intent of the change. If anything in that chain breaks, the failure surfaces as a build error or a runtime crash, and the agent has to reason backwards through the chain to figure out where it went wrong.
When an agent edits an HTML file, the artifact IS the file. There is no chain to reason backwards through. If the change is wrong, the wrongness is visible in the file itself: missing tag, wrong attribute, busted nesting. Diff against last known good, fix, ship.
Frameworks were optimized for a workflow where humans write code and a watcher rebuilds the artifact. Agents change the assumed asymmetry. The agent isn't waiting on a watcher; the agent IS the watcher, the editor, and often the reviewer. Tools that minimize the distance between source and deliverable cut the agent's loop time, and that compounds.
I notice this most when an agent is making fifty small edits in a row across the site. With vanilla HTML, fifty edits is fifty file writes, no rebuild between any of them. With Astro, fifty edits is fifty rebuilds, each one a 2-3 second tax. The wall-clock difference is enormous, and the agent's reasoning quality drops measurably when the loop slows down because context evicts faster than it accumulates.
Vanilla HTML gives agents the shortest possible loop. That's the actual reason I switched.
The numbers that changed
The previous Webflow build of this site loaded a 146 KB CSS bundle, jQuery, three Webflow runtime chunks, GSAP, ScrollTrigger, split-type, plus Google Tag Manager and Plausible. About 350 KB of JavaScript and CSS before any content rendered.
The vanilla rebuild ships ~46 KB of CSS (Webflow Client-First, tree-shaken to what's actually referenced) plus ~1.5 KB of inline JavaScript for the mobile nav toggle and a couple of IntersectionObserver-driven reveals. No external scripts. No tracking. No fonts loaded over the network, system fonts and Georgia for body type.
Page weight on the homepage dropped from ~1.2 MB (mostly hero PNGs I'm not shipping anymore) to ~150 KB.
These are the visible numbers. The invisible win is that I can edit any element of any page without waiting on a build pipeline. The HTML file in my repo is the file the browser renders. There is no transpile step between me and the deliverable.
What I lost and didn't miss
Hot module replacement. I miss this in theory; in practice, with a static-file dev server, a refresh is fast enough that I haven't noticed the absence.
Component reuse. The shared blocks on this site (nav, footer, head meta) are managed by a 200-line Rust binary called fragments that does literal text substitution between marker comments. Every page on the site stays valid HTML at all times. There is no virtual DOM. There is no render-prop pattern. There is exactly one composition primitive: include this file's content here.
Type-checking against my data layer. The previous site had no data layer. The new site has no data layer. Whatever I thought I was getting from TypeScript-checked CMS responses, I am not currently feeling the absence of.
State management. I have no state. The pages are HTML. They render once.
In the categories where I expected to miss framework features most, I have not.
Where this stops working
I want to be honest about the boundary.
If your site has authenticated views, dynamic data per request, or shared mutable state across pages, vanilla HTML is the wrong primitive. You want a framework with server-rendering and a real data layer. The choice between Next.js, Remix, and SvelteKit is then about your preferences, not about whether to use a framework.
If your site has more than ~50 pages, the marker-pair fragment approach starts to creak. You want a real templating layer or a static site generator like Astro or Eleventy.
If your team is more than one person, you want a build pipeline that fails loudly when someone breaks the contract. Vanilla HTML's looseness becomes a liability.
But: most personal sites, portfolio sites, marketing sites, and small product sites are within the boundary where vanilla HTML is the boring correct answer, and the framework choice is doing more harm than good. I have stopped reaching for a framework as my default for these projects. After a year on the other side, I expect the default to hold.
The interesting work, for me, is no longer in choosing the framework. It is in deciding when the framework should not be involved at all.