# I built the same site in Next.js, Remix, and Astro

I built this site three times — in Next.js, Remix, and Astro — and served each build from its own subdomain. What differed was the server model and the JavaScript shipped by default; what didn't was the HTML, the CSS, or the content. This essay is what that experiment taught me, and why the production site is now Astro.

Published: 2026-07-27
Canonical: https://umar.codes/three-frameworks

## Revisions

- 2026-07-27 — First published, absorbing the retired next./remix./astro. subdomains.

---

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&nbsp;KB on disk, and 608&nbsp;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&nbsp;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.
