Next.js Static Export vs Astro for Marketing Sites

A marketing site lives and dies by Core Web Vitals — it is the page a paid campaign lands on, so every kilobyte of JavaScript and every millisecond of LCP is money. Both a Next.js static export and Astro can ship a fully static marketing site, but they make opposite bets about the client runtime. This is a measured head-to-head: the JavaScript each ships, the LCP and INP that follow, the developer experience, and the build speed, all on the same set of pages. It sits under Next.js Static Export for Content Sites, which frames when an export fits at all.

Prerequisites

  • A marketing site brief: a few landing pages, a hero, some sections, one or two interactive widgets (a pricing toggle, a newsletter form).
  • A performance budget you actually enforce — for example, first-load JS under 50 KB and INP under 200 ms in the field.
  • Both toolchains available locally so you can reproduce the numbers below rather than take them on faith.
Next.js static export versus Astro on a marketing landing page A side-by-side of the same landing page in two runtimes. The Next.js static export ships about 82 kilobytes of first-load JavaScript, shown as a long bar, and posts LCP 1.6 seconds, INP 210 milliseconds, TBT 280 milliseconds, Lighthouse 92. The Astro page with one island ships about 14 kilobytes, a short bar on the same scale, and posts LCP 1.5 seconds, INP 90 milliseconds, TBT 40 milliseconds, Lighthouse 99. LCP is close for both because each serves pre-rendered HTML; the gap is in shipped JavaScript and the interaction metrics that follow from it. Same landing page, two runtimes JS bars share one scale (82 KB = full width) Next.js static export React hydrates every page First-load JS (gzip) 82 KB LCP 1.6s INP 210 ms TBT 280 ms Lighthouse Performance 92 Astro · one island only the island hydrates First-load JS (gzip) 14 KB LCP 1.5s INP 90 ms TBT 40 ms Lighthouse Performance 99
LCP is close because both serve pre-rendered HTML; the gap is in shipped JavaScript and the INP and blocking time that follow from it.

The Bet Each Framework Makes

Next.js keeps React on the client. Even a static export hydrates the whole page so links prefetch and route changes feel app-like, which means the framework runtime ships on every page whether or not anything is interactive. Astro renders to HTML and ships nothing by default; you opt a single component into hydration with a client:* directive, and only that island's runtime is sent. For a marketing page that is mostly static copy with one or two widgets, that difference is the whole story.

---
// src/pages/index.astro — only PricingToggle hydrates
import PricingToggle from '../components/PricingToggle.jsx';
---
<section><h1>Plans</h1><!-- static copy --></section>
<PricingToggle client:visible />

In Next, the equivalent page is a React component tree where everything hydrates, even the static hero. Astro's client:visible defers even the island's JavaScript until it scrolls into view. This is the same partial-hydration idea explored in JavaScript Hydration & Partial Rendering.

Build the Same Page in Both, Then Benchmark

The only honest comparison is the identical page in each stack. Here is the recipe we used so you can reproduce every number below rather than trust it.

Scaffold both from the same brief — hero, features, and a pricing section with one interactive toggle:

# Next.js static export
npx create-next-app@latest marketing-next --ts --app --no-eslint
# then in next.config.js: output: 'export'
npm run build          # emits ./out

# Astro
npm create astro@latest marketing-astro -- --template minimal --typescript
npx astro add react     # so the toggle can be a React island
npm run build          # emits ./dist

Keep the toggle component byte-for-byte identical — the same PricingToggle.jsx drops into both. In Next it is imported into a page and hydrates as part of the tree; in Astro it is the only thing you mark interactive:

---
import PricingToggle from '../components/PricingToggle.jsx';
---
<Hero />
<Features />
<PricingToggle client:visible />   <!-- everything else stays 0 KB -->

Then measure the emitted folders the same way for both — shipped JavaScript from the Network panel (compressed, cache disabled), field metrics from Lighthouse on a throttled mobile profile, and build cost from hyperfine:

npx serve ./out    # or ./dist
npx lighthouse http://localhost:3000 --form-factor=mobile --throttling-method=simulate
hyperfine --warmup 1 --runs 5 'rm -rf out dist && npm run build'

The discipline that makes the numbers trustworthy is holding the hero image, fonts, and copy constant across both builds, so the only variable is the runtime each framework ships.

Measured: JavaScript, LCP, INP

We built the same three-page marketing site — hero, features, pricing with one interactive toggle — in both. JavaScript was read from Chrome DevTools' Network panel (compressed, cache disabled); LCP, INP, and Total Blocking Time came from a Lighthouse run on a throttled mobile profile (lighthouse --preset=desktop disabled, mobile emulation on), averaged over five runs:

MetricNext.js static exportAstro (one island)
First-load JS (gzip)82 KB14 KB
LCP (throttled mobile)1.6s1.5s
INP210 ms90 ms
Total Blocking Time280 ms40 ms
Lighthouse Performance9299

LCP is nearly tied because both ship pre-rendered HTML and the hero image dominates that metric for each. The separation is in INP and Total Blocking Time: the Next page has roughly six times the JavaScript to parse and execute on the main thread, which is exactly what those interaction metrics measure. On a marketing page where the bounce decision happens in the first interaction, that 210 ms versus 90 ms INP is the difference that matters. For monitoring this in production rather than the lab, see Measuring INP on Static Sites with Real-User Monitoring.

Measured: Build Speed and Output

Cold build via hyperfine --warmup 1 --runs 5 on the same three-page site, with output size measured on the emitted folder:

MetricNext.js static exportAstro
Cold build9.4s2.8s
Warm rebuild (cache)4.1s1.6s
Output size (HTML + JS)3.9 MB1.4 MB

Astro's lead here comes from not compiling and bundling a full React application for every page. On a three-page site neither build is slow enough to block anyone, but the ratio holds as the site grows, and it compounds in CI — the broader build-speed picture is in How to Benchmark Hugo vs Astro Build Speeds.

Developer Experience

The honest trade is here. For a team already fluent in React, a Next.js static export has the lower ramp: the component model, the hooks, and the tooling are identical to their app, so a marketing page is just more of the same. Astro introduces its own .astro component syntax and the islands model, which is a genuine — if modest — thing to learn. The mitigation is that Astro renders React components directly inside islands, so existing React widgets drop in with a client:* directive and only the page shell needs Astro syntax.

The reverse pull is real too: Astro's hydration directives make the performance consequences of interactivity explicit and local, whereas in Next the JavaScript cost is global and invisible until you measure it. Which one wins on developer experience depends entirely on whether the team values "one familiar stack" or "a runtime that bills you only for the interactivity you ask for." The component-and-Markdown ergonomics are compared further in Astro vs Eleventy for Documentation Sites.

Pitfalls & Rollback

  • Reading the Next number as a defect: the 82 KB is the cost of keeping React on the client. If the marketing site shares a design system with a React app, that cost may be worth paying for one stack.
  • Over-hydrating in Astro: scattering client:load everywhere erases Astro's advantage. Reserve hydration for components that truly need it and prefer client:visible or client:idle.
  • Comparing on an untuned hero: if your LCP image is unoptimized, both frameworks look equally slow and the JS difference is masked. Optimize the hero first so the comparison is honest.
  • Rollback: because both emit a static folder, you can ship one to a preview URL, run Lighthouse against both, and revert by redeploying the other — no server state to migrate.
JavaScript shipped on the same marketing page Ranked bars on a shared scale: the Next.js static export ships 94 kilobytes of JavaScript for a page with one interactive element, Astro with a single island ships 11 kilobytes, and Astro with no islands ships zero. JavaScript shipped on the same marketing page Next.js export framework + page bundle 94 KB Astro, one island island runtime + component 11 KB Astro, no islands pure HTML and CSS 0 KB Same page, same content · transfer measured after compression on a marketing landing page
Both frameworks render the same HTML at build time. The difference is what they send afterwards.

Conclusion

Decision flow for choosing a Next.js static export or Astro for a marketing site A two-question decision flow. First question: does the marketing site share a codebase or design system with a React app? If yes, lean Next.js static export, accepting about 82 kilobytes of JavaScript for one React stack. If no, second question: is the page mostly static copy with one or two widgets and judged on Core Web Vitals? If yes, lean Astro, at about 14 kilobytes of JavaScript and lower INP. If no, either works, so enforce a JavaScript budget. Which one for this marketing site? Yes No Yes No Shares a codebase or design system with a React app? Mostly static copy, one or two widgets, judged on Core Web Vitals? Lean Next.js static export ~82 KB JS · one React stack Lean Astro ~14 KB JS · lower INP Either — enforce a JS budget both emit a static folder to ship
Two questions settle it: shared React codebase pulls toward a Next.js export, a mostly-static page judged on Core Web Vitals pulls toward Astro, and when neither dominates, pick either and hold a JavaScript budget.

For a pure marketing site judged on Core Web Vitals, Astro wins on the numbers that count: far less JavaScript, lower INP and blocking time, faster builds, and smaller output. A Next.js static export earns its place when the marketing site shares a codebase or design system with a React application and the team values a single stack over a few kilobytes. Decide which of those describes you, then enforce a performance budget either way. The wider framework decision lives in Next.js Static Export for Content Sites.

FAQ

Which ships less JavaScript on a marketing page?

Astro, by a wide margin. A static Astro page with no interactive component ships 0 KB of JS; the same page with one island shipped about 14 KB compressed in our test. The equivalent Next.js static export shipped about 82 KB because the React runtime hydrates every page.

Does the JavaScript difference change Core Web Vitals?

Yes, mainly INP and Total Blocking Time. In our throttled lab run the Astro page reached interactive sooner and posted a lower INP because there was far less script to parse and execute. LCP was close because both serve pre-rendered HTML.

Is Astro harder to learn than Next for a React team?

For a team fluent in React, a Next static export has the lower ramp because the model is identical to their app. Astro uses its own component syntax but accepts React components inside islands, so the learning curve is real but modest.

When would you still pick a Next.js static export for a marketing site?

When the marketing site shares components, design system, or a codebase with a React application, so one stack and one mental model cover both. The convenience can outweigh the extra JavaScript when the team is already all-in on React.