Lighthouse 100 Without Next.js: Lessons From a Real Migration to Hono
By Arash Latifi
Why even a fully static page ships framework runtime in Next.js, how one blogger hit a perfect 100 with Hono, and what you can apply to your site.
TL;DR — For a fully static site, Next.js ships runtime JS and hydration payload even with zero client components; one real migration to Hono hit 100 on every Lighthouse category, cut build time ~40%, and dropped the pre-render long task from 309ms to 68ms.
What happened?
On 2026-10-26, a post titled "Removing Next.js Got My Blog to 100 on Every Lighthouse Category" passed 160 points on Hacker News. The author built a personal blog on Next.js 16.1.6, followed all the official guidance (ISR, caching, image optimization via Cloudflare Image Transformations) and got good Lighthouse scores — but not a clean 100.
The culprit: Lighthouse reported a substantial amount of legacy JavaScript that wasn't the author's code at all — it was the framework runtime.
Why removing client components wasn't enough
This is the most interesting part. As an experiment, the author removed every client component, leaving only server components. The score didn't budge. Why? Because Next.js always ships these, even for static pages:
- client runtime chunks and hydration code
- the router's RSC payload
Even next/link and next/image are 'use client' components — having just those two pulls in the runtime and triggers hydration. Strip those out too, and the App Router's own runtime (router and error boundaries) remains, with no official way to reduce it to zero. The only option was a hack: stripping the scripts out of the rendered HTML. That's when it clicked: "If I'm going to do that, why am I even using Next.js?"
The final numbers: unused code dropped from ~230KB to 407 bytes (total JS output: zero). Build time fell by about 40%. The long task before first render was 309.3ms on Next.js vs 68.01ms with Hono — roughly a 78% improvement.
When does Next.js make sense?
| Site type | Next.js? | Why | | --- | --- | --- | | Blog / docs / static landing | Usually not | No real interaction; you're paying for runtime JS and hydration you never use | | Store with live cart and checkout | Yes | Complex state, payments, ISR and API routes are genuinely used | | Dashboard / internal app | Yes | Heavy interaction needs JS anyway; the framework pays for itself | | Multilingual, SEO-heavy site | Depends | If pages are effectively static, a lighter SSG is enough; keep Next.js for the app parts |
Note: the author's blog admin panel is still built with Next.js. This isn't an all-or-nothing decision — it's a per-surface decision.
To measure for real (not just the online tool), use the CLI:
And with Cloudflare Image Transformations, optimization is just URL parameters:
https://example.com/cdn-cgi/image/width=800,format=avif/photo.jpg
What you can do on your site
- [ ] Run your pages through pagespeed.web.dev and read the "Reduce unused JavaScript" numbers
- [ ] In Next.js, check how much bundle your zero-interaction pages ship
- [ ] Keep client components only at genuinely interactive points
- [ ] Fix caching and CDN before swapping frameworks — the author did this first and got good-but-not-100
- [ ] Benchmark hydration-free alternatives (Hono, Hugo) for the fully static parts
FAQ
Q: So is Next.js bad?
No. The post itself says Next.js can be a great framework for interaction-heavy apps — the same author's admin panel runs on it. The argument only applies to pages that need no JavaScript at all.
Q: Does a perfect 100 actually matter?
The score itself isn't a business goal, but the path to it is a precise audit of performance tradeoffs. Dropping dead runtime means faster LCP, especially on mobile and slow connections.
Q: What are the lighter alternatives?
The HN thread surfaced several: Hugo on GitHub Pages scoring 100 with zero performance effort, SvelteKit with perfect scores, even plain HTML and vanilla JS. The common pattern: the less JavaScript, the more an entire category of problems disappears.