HTML-First Frontend in 2026: When to Ditch the SPA for Plain HTML + Islands

An HTML-first frontend ships server-rendered HTML on the first request and hydrates only the interactive bits — buttons, forms, widgets — as isolated islands. In 2026 that means Astro islands, htmx, or plain server-rendered HTML with a sprinkle of Alpine or vanilla JS. It is not a nostalgia trip. A rewrite that landed on the front page of Hacker News last month reported user numbers doubling overnight after the team tore out their React SPA and replaced it with server-rendered HTML plus targeted islands. The comments were full of engineers admitting they had the same drop-off charts and had been ignoring them.

This piece unpacks what html-first actually means today, when the signals in your own metrics mean you have outgrown the SPA model, and how to pick between Astro, htmx, and rolling your own with a backend template engine.

What does HTML-first actually mean in 2026?

HTML-first means the server sends fully rendered HTML on the first response, and JavaScript is opt-in per component rather than a bundle-wide runtime. The browser gets a working document before a single JS file parses.

Three flavours dominate right now:

All three share the same core bet: most of your app is not actually interactive. A product page has one interactive element (add to cart). A blog post has zero. A dashboard has a filter panel and a chart — the rest is text. Shipping 400 KB of React to render text is the waste.

server rack cables
server rack cables

Why did SPAs win in the first place — and why is the pendulum swinging back?

SPAs won because building interactive UIs in jQuery was genuinely miserable, and because the JAMstack era made "ship a static bundle to a CDN" the path of least resistance. React, Vue, and their routers gave you componentisation, state management, and hot reload in a coherent package.

What has changed since:

  1. Edge rendering is cheap. Cloudflare Workers, Vercel Edge, Fly.io — you can server-render HTML in 20ms from a datacentre near the user.
  2. Bundle sizes kept growing. The median React app in 2026 ships more JavaScript than a Windows 95 install. Real users on mid-range Android phones on 4G feel every kilobyte.
  3. Core Web Vitals became a ranking signal that actually bites. LCP over 2.5s tanks your SEO. INP (Interaction to Next Paint) replaced FID in March 2024 and is brutal to hydration-heavy SPAs.
  4. React Server Components made "render on the server" fashionable again inside the React ecosystem itself. Once the framework authors admit it, the debate is over.

Which signals mean you have outgrown the SPA model?

Check your production metrics before you rewrite anything. If three or more of these are red, an html-first migration will pay for itself.

If your app is a Figma clone, a spreadsheet, or a real-time collab editor — none of this applies. Keep your SPA. Islands architecture is for content-heavy apps with sparse interactivity, which is 80% of the web.

Astro vs htmx: which one should you pick?

Verdict: Astro if you are coming from React/Vue and want to keep writing components. htmx if your backend is Rails, Django, Laravel, Phoenix, or Go and you want to delete your frontend build step entirely.

Astro

Astro is a build tool and framework. You write .astro files that look like JSX with frontmatter, and you can drop in React, Vue, Svelte, or Solid components as islands. Content collections give you type-safe Markdown. The output is static HTML plus per-island JS chunks. If you have already read our Astro 5 vs Next.js 15 comparison, you know the shape of it. Deploy anywhere static — Cloudflare Pages, Netlify, Vercel.

---
import Counter from '../components/Counter.tsx';
const posts = await Astro.glob('./posts/*.md');
---
<html>
  <body>
    <h1>Blog</h1>
    <ul>{posts.map(p => <li>{p.frontmatter.title}</li>)}</ul>
    <Counter client:visible />
  </body>
</html>

Only the Counter ships JS, and only when it scrolls into view.

htmx

htmx is a single script tag. Your server returns HTML fragments; htmx swaps them in based on attributes.

<button hx-post="/cart/add/42" hx-target="#cart" hx-swap="outerHTML">
  Add to cart
</button>
<div id="cart"><!-- server renders new cart HTML here --></div>

No build step. No npm install. No bundler debate. If your backend already generates HTML (any templating engine), you are 90% of the way there. The tradeoff: you now do UI work in server templates, which is a step backwards if your designers hand off Figma to React devs.

Plain server-rendered HTML + Alpine

The nuclear option. Use your backend's template engine, add Alpine.js for the x-show/x-data sprinkles, and call it a day. Rails 8 with Turbo is the polished version of this. Basecamp and GitHub have shipped serious products this way for years.

developer laptop code
developer laptop code

How do you actually migrate an existing React SPA?

Do not rewrite the whole app in a weekend. Strangler-fig it.

  1. Identify your leaf routes. Marketing pages, blog, docs, about, pricing — these are pure content. They are the easy wins.
  2. Set up a parallel Astro or htmx project on a subpath (e.g. /blog/*) behind your reverse proxy.
  3. Migrate one route at a time. Measure LCP before and after. You will see the numbers move immediately.
  4. Reserve the SPA for the genuinely app-shaped parts — the authenticated dashboard, the editor, the checkout flow. These may stay React forever, and that is fine.
  5. Kill the shared layout duplication. The main pain point is header/footer drift between the two stacks. Use a shared web component or server-include for the chrome.

Ship measurement before you ship the migration. If you cannot show LCP going from 4.2s to 1.1s in a Grafana panel, you have no way to defend the work in a retro. Sentry and DebugBear both track Core Web Vitals from real users — wire one up before you touch the code.

The honest downsides

HTML-first is not free. The tradeoffs:

The verdict

If you are building a content site, a blog, a marketing page, a docs site, an e-commerce catalogue, or a mostly-read dashboard — start html-first. Use Astro if your team knows React. Use htmx if your backend is not JavaScript. Reserve full SPAs for genuinely app-shaped products.

The HN story that kicked this off was not a fluke. Doubling users overnight is what happens when your median page load goes from 4.5s to 1.2s and Google finally serves your content to people on 4G. The signals were in your dashboards all along.

FAQs

Is HTML-first the same as server-side rendering?

No. SSR still ships a full JavaScript framework runtime to hydrate the entire page. HTML-first ships zero JS by default and only hydrates specific interactive islands. An SSR React app can still weigh 300 KB gzipped; an Astro page with one island might weigh 8 KB.

Does htmx work with a Node.js backend?

Yes. htmx is backend-agnostic — any server that returns HTML fragments works. Express, Fastify, Hono, or Elysia all pair fine with htmx. Bun's built-in templating or a lightweight engine like Eta covers the rendering side.

Will Google index htmx-loaded content?

The initial page load is static HTML, which Googlebot indexes normally. Content loaded via htmx swaps after user interaction is not indexed — same as any AJAX-loaded content. Anything you need in search results must be in the initial server response.

Can I use React components inside an Astro site?

Yes. Astro supports React, Vue, Svelte, Solid, Preact, and Lit as first-class integrations. You add @astrojs/react, mark the component with a client:* directive, and it ships as an island. The rest of the page stays zero-JS.

When should I NOT go HTML-first?

Skip html-first if your product is a real-time collaboration tool (Figma, Notion), a heavy in-browser editor (Photoshop clones, DAWs), a spreadsheet, or anything with persistent client state across dozens of routes. The SPA model exists for a reason — use it where the reason applies.