I’ve shipped production projects with both frameworks. I know the Next.js ecosystem well — App Router, Server Components, ISR, the whole surface area. And for a long time it was my default. Then I started using Astro for content-heavy sites and I haven’t looked back for most client work.

Here’s my honest take on where each one wins, without the framework tribalism.

The Island Architecture Advantage

Astro’s core premise is simple: ship zero JavaScript by default, then hydrate only the components that actually need interactivity. They call it “islands.”

On a typical marketing site or portfolio, maybe 5–10% of the UI actually needs JavaScript. A navigation dropdown, a contact form, a carousel. The rest is static content. But with Next.js, you’re shipping a full React runtime to every visitor by default — even to pages that are pure HTML.

With Astro you write:

<!-- This ships as static HTML — zero JS -->
<HeroSection />

<!-- This hydrates only when visible -->
<ContactForm client:visible />

That client:visible directive is the whole game. The form only loads its JS bundle when it scrolls into the viewport. Everything else is pre-rendered HTML that needs no runtime.

The Real Performance Difference

I ran a comparison on a redesign project — same site design, one built with Next.js (static export mode), one with Astro. Both fully optimized, both with Tailwind, both with image optimization.

The Astro version had:

  • ~80% smaller JavaScript payload
  • LCP under 1.2s vs 1.9s for Next.js
  • Total Blocking Time of 0ms on Astro vs ~120ms on Next.js

That TBT number matters for Core Web Vitals scores. Google uses TBT as a proxy for INP (Interaction to Next Paint), and that 120ms delta is enough to push a score from green to amber. For clients who care about SEO — and most do — that’s a real business argument.

The gap narrows significantly for SPAs and dynamic apps, where you’re shipping JavaScript anyway. But for content sites? Astro wins on performance without effort.

When Astro Wins

These are the project types where I now default to Astro:

Portfolios and agency sites. Pure content, occasional interactivity. The Astro model fits perfectly. Ship a blog post as static HTML, add an animated counter as a client island, done.

Marketing and landing pages. Speed matters for conversion. The lighter the page, the faster the FCP, the lower the bounce rate. Marketing pages also change infrequently, making Astro’s static-first approach ideal.

Blogs and documentation. MDX support is first-class, content collections are a clean abstraction, and the build output is pure static HTML that can be served from a CDN with zero backend.

Corporate websites. Most corporate sites are 90% static content with a contact form at the bottom. There is no reason that form’s submit handler needs a 200KB React bundle.

When Next.js Still Wins

I’m not dismissive of Next.js. There are real use cases where it’s the better tool.

Complex authentication flows. Next.js’s server-side rendering and middleware story for auth is mature. Libraries like NextAuth are built specifically for it. Astro can do server-side rendering, but its auth tooling ecosystem isn’t as developed.

Real-time data. If your app needs live updates — dashboards, notifications, collaborative editing — you want the React ecosystem’s hooks, context, and state management baked in at the root level. Astro can do this, but you’re swimming against the grain.

Large SPAs with complex state. If the majority of your UI is interactive — think a SaaS dashboard, a data-heavy admin panel — the “islands” model doesn’t save you much. You’re hydrating most of the page anyway. At that point, Next.js’s developer experience is genuinely better.

Teams already in the React ecosystem. If you have five engineers who know React deeply and no one has touched Astro, the productivity tax of switching frameworks is real. I wouldn’t force Astro on a team that doesn’t have appetite for it.

A Note on Component Syntax

One thing that throws people is that Astro has its own component syntax. It’s not React, not Vue, not Svelte. But it’s remarkably intuitive once you see the pattern:

---
// Server-side script — runs at build time
const posts = await getCollection('blog');
const featured = posts.filter(p => p.data.featured);
---

<!-- Template — JSX-like, but ships as HTML -->
<section>
  {featured.map(post => (
    <article>
      <h2>{post.data.title}</h2>
      <p>{post.data.description}</p>
    </article>
  ))}
</section>

<style>
  /* Scoped CSS — no class name collisions */
  article { border-bottom: 1px solid hsl(0 0% 15%); }
</style>

The frontmatter fence (---) separates server logic from template. Styles are scoped by default. You can still use React, Vue, or Svelte components inside an Astro template — it’ll hydrate them as islands. The interoperability story is genuinely good.

The Right Tool for the Right Job

What I’ve settled on: Astro is my default for anything content-forward. Next.js is my tool for application-heavy work. There’s significant overlap in the middle, and for those cases I ask: “Will this page need significant JavaScript to function?” If the answer is no, Astro wins on performance, simplicity, and build output quality.

The frameworks aren’t competitors in the way the discourse makes them seem. They solve different center-of-mass problems. The mistake is using either one dogmatically.