In the Astro vs Next.js decision, choose Astro for content-driven sites (marketing pages, blogs, docs, portfolios, catalogues) where most of the page is content and a few parts are interactive. Choose Next.js for application-like products built in React, where most screens are interactive, share state, and depend on per-user data. Astro ships no JavaScript by default and adds it per component; Next.js ships React to every page and lets you reduce what runs on the client with Server Components.
Both are mature, both can render statically or on the server, and both deploy almost anywhere. The rest of this comparison covers where they differ in practice, with a decision table and a migration path from Next.js to Astro.
Key Takeaways
- The core difference is the default: Astro starts at zero client JavaScript, Next.js starts with the React runtime.
- Astro can use React components (and Vue, Svelte, Solid) as islands. Next.js is React only.
- Astro’s content collections are built in. Next.js leaves the content model to you.
- Next.js has deeper app features: nested layouts with shared client state, streaming, and a large React-specific ecosystem.
- Migration is incremental in practice: React components move over as islands, pages are rewritten as
.astrofiles.
Which to choose by project type
| Project | Better fit | Why |
|---|---|---|
| Marketing site, landing pages | Astro | Least JavaScript, fastest first load |
| Blog, publication, newsletter archive | Astro | Content collections, Markdown/MDX built in |
| Documentation | Astro (Starlight) | Purpose-built docs theme, search, i18n |
| Portfolio, agency site | Astro | Image handling, view transitions, small bundles |
| E-commerce storefront | Either | Astro for content-heavy catalogues; Next.js for app-like shopping flows |
| SaaS marketing site + separate app | Astro for the site | Keep the app in whatever framework it uses |
| SaaS dashboard, internal tool | Next.js | Mostly interactive UI, shared client state |
| Social or real-time product | Next.js | Constant client-side updates |
The mixed case is common: an Astro marketing site at example.com and a Next.js app at app.example.com. Each framework does the job it’s best at.
Rendering model: islands vs React Server Components
Astro renders .astro components to HTML on the server. Framework components render to HTML too, and only those with a client:* directive hydrate in the browser, each as an independent island. A page with no islands has no JavaScript.
---
import Hero from '../components/Hero.astro'; // HTML only
import Pricing from '../components/Pricing.tsx'; // React
---
<Hero />
<Pricing client:visible /> <!-- the only JS on the page -->
Next.js (App Router) uses React Server Components. Components are server components by default and don’t ship their code to the client; components marked 'use client' do. The page still loads the React runtime and the router, because navigation and client components rely on them.
Both approaches reduce client JavaScript compared to a classic single-page app. The difference is the floor: Astro’s is zero, Next.js’s is React plus its router. For a content page, that floor is most of the JavaScript you’d ship.
JavaScript shipped
On a typical content page, Astro sends no framework runtime. Next.js sends React, React DOM and the Next.js client router, plus whatever client components the page uses.
That matters most for:
- Mobile users on mid-range devices, where parsing and executing JavaScript is the bottleneck.
- Interaction to Next Paint (INP), which suffers when the main thread is busy hydrating.
- Pages with many visitors and one visit each, such as blog posts from search, where nothing is cached yet.
It matters less for logged-in apps where users stay for a long session and the runtime cost is paid once.
Don’t take any framework’s word for performance, including ours. Build the same page in both and compare the network tab and Lighthouse on a throttled mobile profile.
Content and Markdown
Astro has content collections built in: define a schema in src/content.config.ts, drop Markdown or MDX in a folder, and query typed entries. Loaders pull CMS content into the same API. See the Astro content collections guide.
Next.js supports MDX through @next/mdx, and leaves loading, frontmatter parsing and validation to you or a library. That flexibility is fine for apps, but on a content site you end up building what Astro gives you.
Data fetching and server features
Both frameworks can fetch data at build time or per request.
| Feature | Astro | Next.js |
|---|---|---|
| Build-time data | Frontmatter await, content loaders |
Server components, generateStaticParams |
| Per-request rendering | Adapter + prerender = false |
Dynamic rendering by default for dynamic APIs |
| Partial dynamic content | Server islands (server:defer) |
Suspense boundaries with streaming |
| Mutations | Actions (astro:actions) |
Server Actions |
| Middleware | src/middleware.ts |
proxy.ts (middleware.ts before Next.js 16) |
| Caching | HTTP headers, CDN, your host | Built-in data and route caches, plus HTTP |
Next.js has more built-in machinery around caching and streaming, which is powerful for apps and something you need to understand to avoid stale data. Astro relies on plain HTTP caching and keeps per-request work explicit. The Astro SSR guide covers Astro’s server features in detail.
Routing
Both use file-based routing.
- Astro:
src/pages/blog/[slug].astro, withgetStaticPaths()for static routes. - Next.js:
app/blog/[slug]/page.tsx, withgenerateStaticParams()for static routes.
Next.js nested layouts keep client state as you navigate between child routes, which suits apps with persistent sidebars or editors. Astro layouts are components that re-render per page; for app-like navigation, <ClientRouter /> adds client-side transitions and persistent elements with transition:persist.
Hosting and cost
A static Astro site is a folder of files, so free static hosting on Cloudflare Pages, Netlify, Vercel or GitHub Pages covers most sites with no server to run.
Next.js can do a static export (output: 'export'), but loses server features like middleware and on-demand rendering. Full Next.js runs best on Vercel, and runs on other hosts through Node.js, containers or community adapters. Costs then depend on function invocations and server time.
Ecosystem and team
Next.js has a larger ecosystem of React-specific libraries, examples and developers who already know it. If your team is React-only and your product is an app, that’s a real advantage.
Astro’s ecosystem is smaller but framework-neutral: you can use React components inside Astro, so most of the React ecosystem still works in islands. Astro also has official integrations for MDX, sitemaps, RSS, Tailwind CSS and every major host, and Starlight for docs.
The same page in both frameworks
A blog post page shows the difference in everyday code. In Astro:
---
// src/pages/blog/[slug].astro
import { getCollection, render } from 'astro:content';
import Layout from '../../layouts/Layout.astro';
import ShareButtons from '../../components/ShareButtons.tsx';
export async function getStaticPaths() {
const posts = await getCollection('blog');
return posts.map((post) => ({ params: { slug: post.id }, props: { post } }));
}
const { post } = Astro.props;
const { Content } = await render(post);
---
<Layout title={post.data.title}>
<h1>{post.data.title}</h1>
<Content />
<ShareButtons client:visible url={Astro.url.href} />
</Layout>
In Next.js (App Router), with MDX files and a helper you write to read them:
// app/blog/[slug]/page.tsx
import { getAllPosts, getPost } from '@/lib/posts';
import ShareButtons from '@/components/ShareButtons'; // 'use client' inside
export async function generateStaticParams() {
return (await getAllPosts()).map((p) => ({ slug: p.slug }));
}
export default async function Page({ params }: { params: Promise<{ slug: string }> }) {
const { slug } = await params;
const { meta, Content } = await getPost(slug);
return (
<article>
<h1>{meta.title}</h1>
<Content />
<ShareButtons url={`https://example.com/blog/${slug}`} />
</article>
);
}
The shape is similar. The differences are in what you don’t see: Astro validates the post’s frontmatter against a schema and provides getCollection(); in Next.js, getAllPosts() and getPost() are yours to write or install. And the Astro page ships JavaScript only for ShareButtons, loaded when it scrolls into view, while the Next.js page also ships React and the router.
Comparing performance fairly
If you’re running your own comparison, control the variables:
- Same content and design. Same images, same fonts, same third-party scripts. Most differences in published comparisons come from these, not the framework.
- Production builds. Dev servers aren’t representative in either framework.
- Same host and region. Deploy both to the same provider.
- Mobile profile. Lighthouse’s default throttled mobile run, at least five times each, comparing medians.
- Look beyond the score. Compare total JavaScript transferred, Total Blocking Time and INP in field data if you have traffic.
Expect the biggest gap on content-heavy pages with little interactivity, and a small gap on highly interactive pages, where both frameworks ship similar amounts of component code.
Where Next.js is the better choice
To be specific about it:
- Your UI is mostly interactive: dashboards, editors, builders, chat.
- Many screens share client state that should survive navigation.
- You rely on React libraries that need a single app-wide provider tree.
- Your team already runs Next.js in production and the site is part of that app.
- You want streaming of slow server data into a page as it renders.
In those cases, Astro’s island boundaries become something you work around.
If your project sits on the Astro side of this comparison, every Komal theme is a complete Astro 7 site you can edit and deploy today. Browse Astro themes by site type.
Using both together
Many companies don’t choose. They run the marketing site and blog on Astro and the product on Next.js, each on its own subdomain:
example.com → Astro (static, CDN) marketing, pricing, blog, docs
app.example.com → Next.js (server) dashboard, settings, billing
To make the split invisible to users:
- Share design tokens. Keep colours, spacing and fonts in one CSS file (or a small package) imported by both projects. Both can use Tailwind with the same theme.
- Share React components where it helps. A button or pricing table written in React can render in Next.js and inside Astro (statically, or as an island). Keep shared components free of framework-specific imports like
next/link. - Link across cleanly. The Astro site’s “Log in” and “Start free trial” buttons are plain links to
app.example.com. The app’s “Help” links point back to the docs on the main domain. - Keep analytics consistent. Use the same analytics property on both, with cross-subdomain tracking configured.
This gives the marketing team a fast, simple site they can update through Markdown or a CMS without touching the product codebase, and gives the product team a framework built for apps.
Questions to settle the decision
If you’re still unsure, answer these for your project:
- What share of a typical page is interactive? Under a third points to Astro; most of the page points to Next.js.
- Do most visitors arrive from search and read one or two pages? Astro’s low JavaScript floor matters most there.
- Do users stay logged in for long sessions, moving between screens that share state? That favours Next.js.
- Who edits content? Writers working in Markdown or a CMS fit Astro’s content collections well.
- What does your team already know? A React-only team can still use Astro, since React works inside it, but the conventions are different.
- Where will you host, and do you want a server at all? A fully static Astro site needs none.
Migrating from Next.js to Astro
Migration works best page by page:
- Create an Astro project and add React:
npx astro add react. - Copy
components/over. React components work as-is inside Astro. - Rewrite pages as
.astrofiles. Replacepage.tsxwithsrc/pages/<route>.astro, move data fetching into frontmatter, and render React components with or without aclient:*directive depending on whether they’re interactive. - Replace
next/imagewith<Image />fromastro:assets, andnext/linkwith plain<a>tags plusprefetch. - Move Markdown or MDX into a content collection.
- Replace API routes with Astro endpoints or actions, and add an adapter if you need them at runtime.
- Redirect any changed URLs in
astro.config.mjsor with your host’s_redirects.
Expect to rewrite components that depend on Next.js APIs (next/router, next/headers) or on React context spanning the whole app. The official migration guide from Next.js maps each Next.js concept to its Astro equivalent.
FAQ
Is Astro faster than Next.js?
For content pages, Astro usually ships much less JavaScript because it doesn’t load React by default, which tends to improve load time and responsiveness on mobile. For app-like pages that need most of their UI interactive, the gap narrows. Measure your own pages.
Can I use React components in Astro?
Yes. Run npx astro add react and import .jsx or .tsx components into .astro files. Add a client:* directive to make them interactive.
Is Astro replacing Next.js?
No. They target different projects. Astro is aimed at content-driven sites; Next.js at React applications. Many teams use both.
Can Astro do server-side rendering like Next.js?
Yes. With an adapter, Astro renders pages on request, and supports middleware, actions, sessions and server islands.
Should I use Astro or Next.js for a blog?
Astro. Content collections, MDX, RSS and sitemaps are built in or one command away, and posts ship with no JavaScript unless you add an island. The what is Astro guide covers the basics.