When Astro Beats Next.js for Content Sites
Next.js is the default answer for a new web project right now. If you ask three engineers what to build a content site on in 2026, two will say Next.js and the third will say Next.js with caveats. The framework has won the mindshare battle so thoroughly that it has become invisible — the choice that doesn’t feel like a choice.
That’s a problem, because for a large class of sites Next.js is the wrong tool. Not catastrophically wrong, not unbuildable-on, just more than the project needs in ways that compound across years of maintenance. Astro, in the same slot, would ship less JavaScript, build faster, have a smaller dependency tree, and let the team keep React for the handful of components that actually need to be interactive.
This essay is the honest case for picking Astro over Next.js on content sites. Not a religious argument. A category argument: Next.js is built for applications, Astro is built for content, and most of what gets built on Next.js is content.
What “Content Site” Actually Means
The line between “content site” and “application” is fuzzier than the discourse pretends, but it’s not actually that hard to draw in practice.
A content site is a site where the page is the unit of value. Marketing pages. Documentation. A blog. A portfolio. A news site. A product catalog. The reader lands, reads, maybe clicks one CTA, and either leaves or moves to another page. Interactivity exists — a navbar, a search input, a contact form, maybe a comments widget — but it’s local, optional, and doesn’t define the experience.
An application is a site where the session is the unit of value. A dashboard. An editor. A multi-step booking flow. A SaaS product. The user logs in, does work over many minutes or hours, and the JavaScript is the product, not a garnish on top of content.
Roughly: if you turned JavaScript off and most of your pages were still useful, you have a content site. If they all became broken, you have an application.
The vast majority of sites I’m asked to build or rebuild are content sites. Marketing sites for SaaS companies. Documentation for libraries. Blogs. Small e-commerce catalogs with five product pages. Internal wikis. And almost all of them, in 2026, are being built on Next.js — usually with the App Router, usually with the full React Server Components architecture, usually with a package.json that has 140 dependencies.
That’s the mismatch.
What You’re Buying With Next.js
Next.js is genuinely excellent. I want to be unambiguous about that before the rest of this essay. The App Router has matured, React Server Components are now well-understood, the streaming model handles complex applications gracefully, and the deployment story on Vercel is among the smoothest in the industry. For application work — anything with auth, sessions, user-specific data, complex client-side state — Next.js is at or near the top of my list every time.
The capabilities Next.js bundles are roughly:
A React runtime in the browser for client components, plus a server runtime for SSR and Server Components.
A routing system that does file-based routing, dynamic segments, parallel routes, intercepting routes, and the new layout-based composition model.
A data layer with fetch caching, revalidatePath, revalidateTag, ISR, on-demand revalidation, and the whole infrastructure for dynamic-but-cacheable pages.
A bundler and compiler stack including Turbopack (or Webpack), SWC, the Image component’s optimization pipeline, the Font module, and the OG image generation runtime.
A deployment substrate that assumes a Node-like serverless function will be available at request time for any page that isn’t pure static.
That’s a lot of capability. If you’re building a SaaS dashboard, every line item earns its place. If you’re building a marketing site with six pages and a blog, you’re paying for all of it and using maybe two items on the list.
What Astro Does Differently
Astro starts from the opposite premise. The default is that the page is HTML, generated at build time, with zero JavaScript shipped to the browser. JS is opt-in, component by component, via the “island” model.
The mental model is worth slowing down on because it inverts the React-default world:
In a Next.js app, every page starts as a JavaScript bundle that hydrates the server-rendered HTML on load. The “Server Component” architecture reduces this — RSCs don’t ship to the browser — but the moment you mark a component 'use client', the bundle grows. The default trajectory of a Next.js page is “more JS over time” because adding interactivity is the easy path.
In an Astro project, every page starts as static HTML. Components — even React ones — render to HTML at build time and ship no JavaScript. When you need a component to be interactive, you mark it explicitly: <MyComponent client:load /> or client:visible or client:idle. Only that component’s JS gets bundled and shipped. Everything around it stays static. The default trajectory is “no JS until you ask for it.”
This is the entire game. Everything else — Astro’s content collections, its file-based routing, its integrations system — is plumbing. The defining feature is “static HTML unless you opt out.”
A typical Astro content site ships under 10KB of JS on the home page. A typical Next.js content site ships somewhere between 80KB and 250KB. That’s a 10x to 25x difference, and it shows up directly in Lighthouse scores, time-to-interactive, and the user’s data bill.
Where Next.js Is Overkill
Concretely, the places where I now reach for Astro over Next.js:
Marketing sites. Five to fifty pages. Hero, features, pricing, about, contact. Maybe a blog attached. Astro renders these as flat HTML, ships zero JS by default, and the build takes under five seconds for a typical small site. The Next.js version would have the App Router boilerplate, the 'use client' annotations on anything interactive, the next/image optimization that requires a runtime in some deploy modes, and a bundle three to five times larger.
Documentation sites. Where Astro has an outright dominant tool — Starlight — that’s purpose-built and ships a beautiful default. Next.js has Nextra, which works, but the community has largely moved on from “Next.js as a docs platform” because the framework is fighting the use case. Docs are content. Astro treats content as the primitive.
Personal blogs and developer portfolios. The “this is a Next.js blog” tutorial that everyone followed for years was always overengineered for the result. A static blog needs MDX (or just markdown), syntax highlighting, RSS, and an OG image generator. Astro ships these as first-class features with one-line integrations. The Next.js equivalent is an essay’s worth of configuration.
Small e-commerce sites with mostly-static catalogs. Where the product list rarely changes, prices are stable, and the cart is the only meaningfully dynamic surface. Astro renders the catalog statically, and the cart can be a React island on the relevant page. Shopify hydration, Snipcart, or a small server endpoint handles checkout. You don’t need an entire SSR runtime to support twelve product pages.
Internal wikis, changelogs, knowledge bases. Anything that’s “long-lived content rendered from markdown.” Astro’s content collections give you typed frontmatter, MDX support, and zero-config full-text via Pagefind. The Next.js version is a custom MDX pipeline and a lot of glue code.
In all of these, Next.js still works. It just brings capabilities the project will never use, at the cost of bundle size, install time, build time, and a learning curve the team didn’t need to climb.
Where Next.js Still Wins
I want to be specific about the cases where I would not recommend Astro:
Anything with significant client-side state. Multi-step forms with branching logic. A dashboard with charts that filter and recompute. An editor. A booking flow with persistent cart state across pages. Astro can do these — you can drop in a React island — but at some point the “island” is the page, and you’ve taken on the worst of both worlds: an Astro shell wrapping a React app that doesn’t need the Astro shell.
Auth-gated applications. Anything where the page must be rendered per-user, where sessions matter, where the server has to know who’s asking before it can produce HTML. Next.js’s RSC + middleware story handles this elegantly. Astro has SSR support and you can build it, but you’re swimming against the framework’s default current.
Realtime or highly interactive UIs. Live-updating data, websockets driving the UI, optimistic mutations across many components. The “island” model fights this because islands are by definition isolated. A single global state across the page is awkward in Astro and natural in Next.js.
Apps that already have substantial React expertise on the team. This is a soft factor but a real one. Switching tools has a cost. If the team is fluent in App Router conventions, has internal libraries built on top of Next.js patterns, and the project is genuinely application-shaped, “Astro would be smaller” is not a strong enough argument on its own.
The dividing line is roughly: does the page need to be interactive enough that React conventions help, or is interactivity the exception rather than the rule? If the latter, Astro. If the former, Next.js.
What the Numbers Look Like
I migrated a 22-page marketing site from Next.js (App Router, static export) to Astro last quarter as a side-by-side benchmark. Same content, same design, same image assets, same hosting (Cloudflare Pages, identical edge config).
Bundle size, home page first load JS:
- Next.js: 187KB gzipped
- Astro: 0 bytes shipped JS (one interactive contact form on
/contactships a 14KB island)
Lighthouse performance, home page mobile:
- Next.js: 87
- Astro: 99
Build time, cold:
- Next.js: 38 seconds
- Astro: 4 seconds
node_modules size:
- Next.js: 487MB
- Astro: 184MB
Total dependencies in package.json:
- Next.js: 31 direct (147 with transitives counted at depth=2)
- Astro: 12 direct (64 with transitives)
Deployed asset size (gzipped):
- Next.js: 1.8MB total
- Astro: 412KB total
None of these are surprising if you understand what each framework defaults to. They are the predictable outcome of “page-as-bundle” versus “page-as-HTML.” But they accumulate. On a slow connection in a region with high data costs, the Astro version loads in under a second; the Next.js version takes three to four. Search engines see the same difference in their crawl behavior.
The migration itself took a long afternoon, mostly because the codebase was already organized around content files and small React components — exactly the shape Astro handles natively.
What Migration Actually Costs
If you’re considering moving a Next.js content site to Astro, the honest cost breakdown:
Pages convert one-to-one. A page.tsx in Next becomes an index.astro or [slug].astro in Astro. The routing model is similar enough that the conversion is mechanical.
React components mostly work as-is. Astro renders React components to HTML at build time without modification. You just import them in your .astro file. The one wrinkle is that hooks and effects don’t run unless you mark the component as a client island.
Layouts translate well. Next’s layout.tsx is similar to Astro’s Layout.astro. The composition model is slightly different but the mental model carries over.
Data fetching gets simpler. Astro’s getStaticPaths and top-level await in frontmatter replace Next’s generateStaticParams and Server Component data fetching. Both work; Astro’s is less ceremony.
Image handling needs attention. next/image is excellent and you’ll have to replace it. Astro’s <Image /> from astro:assets does most of what it does (automatic format conversion, responsive variants, lazy loading) but the API is different and you’ll touch every image.
MDX migrates cleanly. If you were using @next/mdx, switching to @astrojs/mdx is a config change and a content-collections setup.
The things you lose: next/font (Astro has font integrations but they’re less polished), next/script (replaced with regular <script> tags or is:inline), middleware that does substantial work (Astro has middleware but the integration with hosting providers is less mature), and any code that depended on App Router conventions like parallel routes or intercepting routes (which a content site shouldn’t have been using anyway).
For most content sites, the migration is a one-to-three-day project depending on size. The result is a substantially smaller, faster site running on a substantially smaller toolchain.
The Decision Heuristic
If you’re starting a new project and trying to decide, the question I ask is: what percentage of the components on the most complex page need to be interactive?
- Under 20% interactive: Astro, almost always. The “static by default” model maps to your reality.
- 20-50% interactive: Either works. Astro will give you smaller bundles; Next.js will give you a more consistent mental model. Depends on the team.
- Over 50% interactive: Next.js. The “island” model becomes a tax once islands outnumber the static parts.
For most marketing sites, blogs, documentation, and small e-commerce, the answer is in the first bucket. For SaaS dashboards and apps, it’s almost always the third. The middle case is where most of the debate lives, and that’s where team familiarity and project trajectory matter more than the framework benchmarks.
The Boring Conclusion
Next.js is a great framework. It’s also the default, and being the default has hidden a category mismatch from a lot of teams: most of what’s being built on Next.js is content, and content is what Astro is built for.
If your site is mostly pages, mostly static, mostly read rather than interacted with — try Astro. The first build will surprise you with how fast it is. The first deploy will surprise you with how small it is. The first Lighthouse run will surprise you with how green it is. None of those wins required clever engineering. They came from a framework that didn’t assume your content site was secretly an application.
Pick the tool that matches what you’re actually building, not the one that matches what everyone else is building.