Next.js Comparison
Side-by-side feature mapping for developers migrating from Next.js App Router to Astro 6.
Architecture
| Aspect | Next.js | Astro |
|---|
| Mental model | Full-stack React app | Content site with islands |
| Default JS shipped | React runtime on every page | Zero JS (opt-in per component) |
| Component model | React everywhere | .astro (server-only) + framework islands |
| Rendering default | Server Components (on-demand) | Static (prerendered) |
| Bundler | Turbopack/Webpack | Vite |
| Framework lock-in | React | UI-agnostic (React, Svelte, Vue, Solid, none) |
Routing
| Feature | Next.js | Astro |
|---|
| File location | app/ | src/pages/ |
| Page file | page.tsx in directory | filename.astro directly |
| Dynamic route | [slug]/page.tsx | [slug].astro |
| Catch-all | [...slug]/page.tsx | [...slug].astro |
| Route groups | (group)/page.tsx | Not built-in (use directories) |
| Parallel routes | @slot/page.tsx | Not applicable |
| Intercepting routes | (.)/page.tsx | Not applicable |
| Nested layouts | layout.tsx (automatic nesting) | Manual layout wrapping |
| Loading states | loading.tsx | Not applicable (no React tree) |
| Error boundaries | error.tsx | Try/catch in frontmatter |
| API routes | route.ts | .ts files exporting HTTP methods |
Data Fetching
| Pattern | Next.js | Astro |
|---|
| Server data | fetch() in server component | fetch() in frontmatter (top-level await) |
| Static params | generateStaticParams() | getStaticPaths() |
| Client data | "use client" + useEffect/SWR/React Query | Client island with any fetching library |
| Form mutations | Server Actions ("use server") | Endpoints or form handlers |
| Request context | headers(), cookies() | Astro.request, Astro.cookies |
Rendering and Caching
| Feature | Next.js | Astro |
|---|
| Static generation | Default (no dynamic APIs) | Default (output: 'static') |
| On-demand rendering | Dynamic APIs trigger it | export const prerender = false |
| ISR | revalidate export | CDN Cache-Control headers |
| Tag invalidation | revalidateTag() | Experimental route caching or CDN API |
| Path invalidation | revalidatePath() | CDN purge or rebuild |
| Streaming | React Suspense | Not applicable |
| PPR | Partial prerendering | Server islands (different mechanism, similar goal) |
| Feature | Next.js | Astro |
|---|
| Page title | metadata.title or generateMetadata() | <title> in HTML template |
| Meta tags | metadata object | <meta> tags in HTML |
| Open Graph | metadata.openGraph | <meta property="og:*"> tags |
| Structured data | JSON-LD in component | JSON-LD in <script type="application/ld+json"> |
| Canonical URL | metadata.alternates.canonical | <link rel="canonical"> |
| Sitemap | Third-party or manual | @astrojs/sitemap integration |
Astro’s approach is more verbose but completely transparent. You write the actual HTML that goes in <head>.
Styling
| Feature | Next.js | Astro |
|---|
| CSS Modules | Supported | Supported |
| Scoped styles | CSS Modules | <style> tag (scoped by default) |
| Global styles | global.css import | <style is:global> or CSS import |
| Tailwind | Plugin | @astrojs/tailwind integration or Vite plugin |
| CSS-in-JS | Supported (with caveats in RSC) | Limited (prefer scoped styles or Tailwind) |
Framework Features
| Feature | Next.js | Astro |
|---|
| Image optimization | next/image (runtime) | <Image /> from astro:assets (build-time) |
| Font optimization | next/font | Fonts API in astro.config.mjs |
| Script loading | next/script | <script> (Vite-bundled) |
| Link prefetching | <Link> (automatic) | Configurable prefetch strategies |
| Middleware | Edge runtime (limited APIs) | Full Node.js runtime |
| i18n | Built-in routing | Built-in i18n routing |
| View transitions | Not built-in | Built-in with <ViewTransitions /> |
| Content management | Manual | Content collections with schema validation |
Deployment
| Platform | Next.js | Astro |
|---|
| Vercel | Optimized (first-party) | Supported via adapter |
| Cloudflare | Community adapter | Official adapter |
| Node.js/Docker | Standalone build | Official Node adapter |
| Netlify | Supported | Official adapter |
| Static hosting | output: 'export' | Default mode |
| Deno | Community | Community adapter |
When Next.js is still better
- Highly interactive apps where most pages need client-side React
- Apps heavily invested in React Server Components streaming
- Teams that want a single React mental model everywhere
- Apps using Vercel-specific features (Edge Config, KV, etc.)
When Astro is better
- Content-heavy sites (news, blogs, docs, marketing, portfolios)
- Sites where SEO and metadata correctness are critical
- Sites with isolated interactivity (islands, not app shells)
- Teams that want simpler, more transparent rendering behavior
- Multi-framework teams or teams moving away from React-only