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 comparison of two columns. The Next.js static export ships about 82 kilobytes of JavaScript and posts higher INP. The Astro page with one island ships about 14 kilobytes and posts lower INP. LCP is close for both because each serves pre-rendered HTML. Same landing page, two runtimes Next.js static export JS shipped ~82 KB LCP 1.6s INP 210 ms TBT 280 ms React hydrates every page Astro (one island) JS shipped ~14 KB LCP 1.5s INP 90 ms TBT 40 ms only the island hydrates
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.

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.

Conclusion

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.

Static Site Generators in Production