Astro vs Next.js for a content site: numbers from building both
For a content site, Astro ships less JavaScript, and Next.js does not win the difference back at build time. I rebuilt both versions of this site on 2026-10-21. The Next.js 13 build sends 214 KB of gzipped JavaScript to its homepage. The Astro 7 build sends 620 bytes of inline script. Cold builds took about the same time.
How I measured the two builds of this site
This site ran on Next.js until 2026-07-27, when I converted it to Astro. Both versions are still in the repository, so I checked each one out into its own git worktree and built it on the same machine, an Apple M4 Pro on Node 24.13.0.
- Next.js: the last commit before the conversion, from 2022-11-05. Next.js
13.0.2 on the pages router, React 18.2.0, Chakra UI 2.3.7 with Emotion 11.10.0
and framer-motion 7.2.0. I installed from its own
yarn.lockwith yarn 1.22.22. It builds today without changes. - Astro: the current production build, Astro 7.3.5 with MDX, installed from
its
pnpm-lock.yaml.
Every gzip figure is gzip -9 over the files a browser loads. I left out the
Next.js noModule polyfill script, because modern browsers do not fetch it.
The two sites do not have the same content. The 2022 site had 17 routes, most of them agency pages and blog posts. The 2026 site has 62 pages. So these numbers compare what each build ships per page, not two copies of one page. The three-frameworks essay covers the experiment where the content was identical; this page adds the measurements.
JavaScript per page: 214 KB against zero
| Page | Next.js 13, raw | Next.js 13, gzip | Astro 7 |
|---|---|---|---|
| Homepage | 648,819 bytes | 214,033 bytes | 620 bytes inline, 0 files |
| One blog post | 627,054 bytes | 205,947 bytes | 620 bytes inline, 0 files |
| 404 page | 493,341 bytes | 160,604 bytes | 620 bytes inline, 0 files |
The Astro dist/ folder contains no .js file at all. The 620 bytes are two
inline scripts on every page: one keeps the theme choice and one handles the
toggle. The JSON-LD blocks are scripts too, but no browser executes them, so I
did not count them.
The Next.js 404 page is the floor, because it has almost no code of its own. Its 160,604 gzipped bytes come in four files:
| File | Gzipped | What it holds |
|---|---|---|
framework |
45,391 bytes | React and React DOM |
main |
26,281 bytes | The Next.js client runtime and router |
webpack |
1,045 bytes | The chunk loader |
_app |
87,887 bytes | Chakra UI, Emotion, framer-motion and my layout |
The 45 KB React figure is the one the three-frameworks essay quotes. Every page pays all four files before its own code loads.
Is a 2022 Next.js build fair to Next.js today?
Partly. Most of the 2022 floor was my choice of UI kit: the _app file is 55%
of it. A Next.js site without Chakra pays only the first three files, 72,717
gzipped bytes.
To check whether that floor got smaller, I built a control app with Next.js 16.4.2 and React 19.3.1: the app router, one static page, and no client components. That page loads five scripts, 455,990 bytes raw and 134,910 bytes gzipped, plus 3,638 bytes of inline payload. The current framework floor is larger than the 2022 one, not smaller. Server components remove my components’ code from the bundle, but the runtime that hydrates the page still loads.
HTML and CSS: where each build puts the styles
The Next.js homepage HTML is 152,521 bytes, or 23,917 gzipped. Of that,
92,165 bytes are 109 inline <style> tags that Emotion writes during server
rendering. Each page carries its own copy of the styles it uses.
The Astro homepage HTML is 12,525 bytes, or 4,109 gzipped. All 62 pages share one stylesheet of 7,571 bytes, or 2,157 gzipped, which the browser caches after the first page.
Build time is about the same
| Build | Pages | Cold build times |
|---|---|---|
Next.js 13, next build |
17 routes | 7.57 s, 9.12 s, 9.89 s |
Astro 7, astro build |
62 pages | 7.09 s, 6.77 s, 6.66 s |
next build includes linting and type checking. astro build does neither,
and with astro check before it the Astro total is 10.50 seconds. The Astro
build also renders a 1,200 × 630 Open Graph image for each of the 62 pages with
satori. A warm Next.js build, with its cache kept, took 3.74 seconds.
Per page, Astro builds faster. Per build, the times are close enough that I would not choose a framework on them.
Dependency counts: fewer direct, more installed
| Count | Next.js 13 | Astro 7 |
|---|---|---|
| Direct runtime dependencies | 15 | 8 |
| Packages in the production tree | 258 | 481 |
The Astro site declares fewer dependencies and installs nearly twice as many production packages. Astro brings its whole build toolchain into the production tree: Vite, esbuild, sharp for images and Shiki for code. Next.js 13 ships its compiler as one native binary, so its tree counts fewer packages. None of those packages reach the browser in either build. A dependency count measures what you install, not what you ship.
Which one I would use for a content site
For a site where every page is a document, I use Astro. Its default is zero client JavaScript, and on this site that default held across 62 pages without any effort from me. In Next.js, my 2022 floor was 160 KB gzipped with a UI kit, and today’s floor is 135 KB without one. That cost comes before any component of my own.
I would use Next.js when the pages are an application: sessions, forms, and state that changes after load. There, the runtime does work that you would otherwise write yourself. The reasons behind the choice are in the three-frameworks essay, and the conversion itself is in the plan that moved this site to Astro.
Revisions
- Created from two builds of this site measured that day on Node 24.13.0: the last Next.js commit (2022-11-05; Next.js 13.0.2, React 18.2.0, Chakra UI 2.3.7) and the Astro build at master (Astro 7.3.5), plus a one-page Next.js 16.4.2 control app.
- Published five days after its Oct 16 slot; backfilled by the publishing run because no draft existed for the row.