When Astro Is the Right Choice (And When It Isn't)

An Astro 6 decision guide (June 2026): the project shapes where Astro wins, where Next.js or Hugo win instead, and a 1-day spike to confirm before you commit.

Astro is the best default for content-heavy sites in 2026, but it is not a universal answer. Pick the wrong project shape and you fight the framework on every feature. This guide is built on Astro 6 (stable March 10, 2026) and shows exactly where Astro wins, where Next.js or Hugo win instead, and a one-day spike that settles the question before you commit.

TL;DR

  • Pick Astro when content is the product — blog, docs, marketing, a small catalog. You get near-zero shipped JavaScript by default and excellent Core Web Vitals with little tuning.
  • Pick Next.js when interactivity is the product — dashboards, multi-step app flows, deep client-side routing and shared state.
  • Pick Hugo or plain HTML when even Astro is overkill — a thousand-page docs site that rebuilds in seconds, or a five-page brochure.
  • Astro 6 needs Node 22.12+, uses Vite 7 and Zod 4, and moved content config to src/content.config.ts. If you are on Astro 4 tutorials, several APIs below have changed.

What changed in 2026 (so old guides mislead you)

Two things reset the landscape this year. First, Cloudflare acquired The Astro Technology Company on January 16, 2026, and the whole core team joined Cloudflare. The framework stays open source (MIT), but the Cloudflare adapter is now first-class: Astro 6’s dev server runs on workerd, the same runtime that serves production on Cloudflare Workers, so KV, D1, R2, and Durable Objects bindings work in local dev without a simulation layer.

Second, Astro 6 shipped stable on March 10, 2026 with breaking changes that invalidate a lot of older copy-paste:

  • output: 'hybrid' is gone. There are now only two modes: static (the default; pages are prerendered) and server. Mixed sites use static plus per-page export const prerender = false.
  • Content config moved from src/content/config.ts to src/content.config.ts, and the v5 grace period is over.
  • Collections must declare a loader (glob or file) — the legacy auto-collections API was removed.
  • The schema helper moved: import z from astro/zod, not astro:content.

If a tutorial still shows output: 'hybrid' or z from astro:content, it predates Astro 6.

The fit test: is your site Astro-shaped?

Astro is a strong default when most of these are true:

  • Your site is mostly static content — blog, documentation, marketing pages, a small store.
  • You want top Core Web Vitals (LCP, INP, CLS) without hand-tuning a bundler.
  • You don’t need to share complex client-side state across many routes.
  • You are comfortable authoring pages in .astro or .mdx.
  • Your team is small and doesn’t need an opinionated full-stack app framework.

The single deciding question: list every page type your site needs (marketing, blog, docs, dashboard, checkout) and mark each “static” or “interactive.” If 70% or more are static, Astro is the right default. If most pages are interactive app surfaces, you will spend more time routing around Astro than using it.

Astro vs the alternatives (June 2026)

Astro 6Next.js 15HugoPlain HTML
Default JS shipped~0 KB on content pagesHydrates the route by default0 KB0 KB
Best forContent + a few islandsApp-style, heavy interactivityHuge static sitesTiny brochure sites
Markdown/MDXNative, typed via content layerVia MDX pluginsNative (no MDX)Manual
UI componentsReact, Vue, Svelte, Solid, PreactReact onlyGo templatesNone
Build speed (large sites)Fast; content layer cut build memory 25-50% in v5ModerateFastestInstant
SSR / dynamicoutput: 'server' + adapterBuilt-in, first-classNone (static only)None
HostingStatic anywhere; SSR via adapterBest on Vercel-class hostsStatic anywhereAnywhere

Note: “near-zero JS” applies to content-only pages. Each interactive island you add (client:load, client:visible) ships exactly its own component and runtime — that is the cost you pay only where you opt in.

The one-day spike: prove it before you commit

Framework swaps mid-build are expensive. Spend a day validating Astro against your real content before deciding.

1. Scaffold the official starter. Starlight is the docs template; use --template basics for a blank site.

npm create astro@latest -- --template starlight my-spike
cd my-spike
npm install
npm run dev      # http://localhost:4321

2. Wire up a content collection — this is Astro’s strongest feature and the one thing you must try before deciding. Note the Astro 6 syntax: file is src/content.config.ts, you pass a glob loader, and z comes from astro/zod.

// src/content.config.ts  (Astro 6)
import { defineCollection } from 'astro:content';
import { glob } from 'astro/loaders';
import { z } from 'astro/zod';

const articles = defineCollection({
  loader: glob({ pattern: '**/*.{md,mdx}', base: './src/content/articles' }),
  schema: z.object({
    title: z.string(),
    publishedAt: z.coerce.date(),
    tags: z.array(z.string()).default([]),
    draft: z.boolean().default(false),
  }),
});

export const collections = { articles };

Drop a sample file in src/content/articles/hello.mdx, then render it with getCollection('articles'). If this typed-front-matter workflow feels natural, you have your answer.

3. Add only the one UI framework you actually need. Don’t mix three.

npx astro add react       # or vue, svelte, solid, preact
---
// src/pages/index.astro
import Counter from '../components/Counter.tsx';
---
<html>
  <body>
    <h1>Static heading</h1>
    <Counter client:load />   <!-- only this island hydrates -->
  </body>
</html>

4. Build and inspect the payload. A content-only page with no islands should ship essentially no JavaScript.

npm run build
ls -lh dist/_astro/*.js 2>/dev/null | head
# a content page with no islands ships ~0 KB of JS

5. Confirm your hosting target. Static output runs on any host. If you need SSR, set output: 'server' and add an adapter — remember hybrid no longer exists.

// astro.config.mjs — SSR example (Astro 6)
import { defineConfig } from 'astro/config';
import cloudflare from '@astrojs/cloudflare';

export default defineConfig({
  output: 'server',
  adapter: cloudflare(),
});

For a mostly static site that needs a few dynamic routes, keep the default static output and opt those pages out individually:

---
// src/pages/preview/[id].astro
export const prerender = false;   // this route renders on the server
---

Common pitfalls

  • Choosing Astro for an app-style project, then hitting friction on every interactive feature. Run the 70%-static test first.
  • Mixing multiple UI integrations (React + Vue + Svelte). Each adds its own runtime; pick one.
  • Storing data ad-hoc instead of using content collections — you throw away typed front matter and build-time validation.
  • Copy-pasting an Astro 4/5 config: output: 'hybrid', src/content/config.ts, or z from astro:content will break on Astro 6.
  • Assuming Astro replaces a CMS. For non-technical editors you still need a CMS (Astro 6’s stable live content collections can pull from Contentful, Sanity, or WordPress at request time).
  • Deploying static content to a server-heavy platform when a static CDN would be cheaper and faster.

Who this is for

Indie builders shipping content sites, documentation, marketing pages, or small portfolios — and anyone weighing Astro against Next.js for a content-first project.

When to skip Astro

Teams building dashboards, complex SaaS apps, or products with deep client-side state and routing. There, Next.js 15’s app-first model and React-everywhere defaults fit the work better.

FAQ

  • Is Astro production-ready in June 2026?: Yes. Astro 6 has been stable since March 10, 2026, runs on Vite 7 and Node 22.12+, and powers many production content sites. The Cloudflare acquisition (Jan 2026) kept it open source under MIT.
  • Should I start a new project on Astro 5 or 6?: Start on Astro 6. Most breaking changes are config-level (src/content.config.ts, the glob loader, z from astro/zod), so building fresh on 6 is easier than migrating later.
  • Can I use React inside Astro?: Yes, via the React integration. Islands hydrate React only where you opt in with directives like client:load, keeping the rest of the page JS-free. Vue, Svelte, Solid, and Preact work the same way.
  • How does Astro compare to Hugo or Eleventy?: Astro is more flexible if you want JS components anywhere and native MDX; Hugo wins on raw build speed for very large static sites; Eleventy is the most minimal. Choose by ecosystem and authoring fit.
  • Do I still need an adapter for a static site?: No. The default static output prerenders everything and deploys to any CDN with no adapter. You only add an adapter when you switch to output: 'server' or opt specific routes out of prerendering.
  • What replaced the old hybrid output mode?: It was removed. Use static (the default) for the whole site and add export const prerender = false to the individual routes that must render on the server.

External references: Astro 6.0 release notes and the official content collections guide.

Tags: #Indie dev #Astro #MDX #Comparison #Getting started