I built the same site in Next.js, Remix, and Astro
I built this site three times — once each in Next.js, Remix, and Astro — and served them on their own subdomains. The frameworks differed almost entirely in how they treat the server; they barely differed in what visitors received. For a content site, the deciding factor was how much JavaScript each one ships by default.
Why I built the same site three times
The honest reason: I wanted to form opinions from building, not from reading other people’s
benchmark threads. A personal site is the perfect test subject — small enough to rebuild in a
weekend, real enough to expose each framework’s defaults. So for a while, next.umar.codes,
remix.umar.codes, and astro.umar.codes all served the same content from three different
codebases.
That experiment is over, and this essay absorbs it. The subdomains now redirect here — three identical sites competing for the same search index entry turned out to be a good way to learn about frameworks and a bad way to run a website.
What differed: the server model
Each framework has a different answer to “where does your code run?”
Next.js wants to be everywhere: server components, client components, static generation, incremental regeneration — several rendering models in one framework, each with its own rules. The pages-router version of this site used exactly one of them (static generation), which meant most of the framework was surface area I carried but didn’t use.
Remix is the most opinionated: everything is a server request, loaders feed routes, forms post back. It’s the cleanest mental model of the three — and the least necessary one for a site where every page could be built once at deploy time. Running a server so that unchanging HTML can be re-rendered per request is architecture without a requirement behind it.
Astro treats pages as templates that run at build time and components as islands that only hydrate if you ask. The default is zero client JavaScript, and that default is the whole argument.
What didn’t differ
The HTML was the same. The CSS was the same. The content was identical by design. Lighthouse scores converged in the high 90s on all three once images were handled properly — for a small static site, any of these frameworks can be made fast. What differed was how much work “made fast” took: in Astro it was the default, in Next.js and Remix it was a discipline.
What the production build looks like now
The version of this site you’re reading is the Astro rebuild. The full production build is 940 KB on disk, and 608 KB of that is self-hosted fonts. It contains ten HTML pages, one CSS file, and zero JavaScript bundles — the only scripts on any page are two inline snippets (theme persistence and a toggle handler) and two JSON-LD blocks. A production build takes about three seconds on my machine.
The React versions couldn’t get near that floor, because shipping React at all costs roughly 45 KB gzipped before the first line of my own code.
Which one I’d pick, by project
- Content site, blog, docs, marketing: Astro, without hesitation. The zero-JS default and the content-collection layer fit the problem exactly.
- Product with heavy interactivity and sessions: Remix’s loader/action model, or Next.js if the team already lives in the Vercel ecosystem.
- The real lesson: pick by what the framework makes default, not by what it makes possible. Everything is possible in all three. You will ship the defaults.
Revisions
- First published, absorbing the retired next./remix./astro. subdomains.