After years of building with Next.js, I finally made the switch to Astro for my personal site. Here’s why, and what I learned.

The Problem with Next.js for Content Sites

Next.js is incredible for applications. But for a content-focused site, it brings baggage:

  • Client-side JS by default — Every page ships React, even static articles
  • Complex cachinggetStaticProps, revalidate, ISR, edge functions… it’s a lot
  • Bundle size — Even a simple blog post downloads React + your components
  • Build times — Grow linearly with content

Astro flips this: zero JS by default. You opt in to interactivity per component with client:load, client:visible, etc.

Why Astro Won

1. Content Collections

Type-safe frontmatter with Zod schemas:

// src/content/config.ts
import { defineCollection, z } from 'astro:content';

const blog = defineCollection({
  type: 'content',
  schema: z.object({
    title: z.string(),
    description: z.string(),
    pubDate: z.coerce.date(),
    tags: z.array(z.string()).default([]),
    draft: z.boolean().default(false),
  }),
});

No more gray-matter parsing, no more type mismatches. It just works.

2. Tailwind v4 + CSS-first

/* src/styles/global.css */
@import "tailwindcss";

@theme {
  --color-bg: #05080d;
  --color-accent: #00d4aa;
  --font-sans: "Space Grotesk", sans-serif;
}

No tailwind.config.js. No JIT compiler quirks. Native CSS variables. The DX is incredible.

3. Image Optimization Built-in

---
import { Image } from 'astro:assets';
import hero from './hero.jpg';
---

<Image src={hero} alt="Hero" width={1200} height={600} format="avif" />

Generates WebP/AVIF, multiple sizes, srcset — zero config.

The Migration Path

  1. Create Astro project: npm create astro@latest
  2. Add content collections for your posts
  3. Port layouts — Astro layouts are similar to Next.js layouts
  4. Migrate components.tsx.astro (or keep React with client:load)
  5. Deployastro build outputs static files to dist/

Performance Results

Metric Next.js Astro
JS Bundle (homepage) ~140 KB 0 KB
Lighthouse Score 92 100
Build Time (50 posts) 45s 8s
Time to Interactive 1.8s 0.4s

The Trade-offs

What you lose:

  • React ecosystem (use client:load for interactive islands)
  • Incremental Static Regeneration (use scheduled rebuilds or webhooks)
  • Middleware/Edge functions (use Cloudflare Workers or Netlify Functions)

What you gain:

  • Insane performance out of the box
  • Simpler mental model
  • Better authoring experience (MDX with frontmatter validation)
  • Framework-agnostic (use React, Svelte, Vue, Solid in the same project)

Verdict

For a content site, Astro is the right tool. Next.js is for apps. Don’t use a sledgehammer to hang a picture frame.


This post was written in MDX, processed by Astro’s content layer, and styled with Tailwind v4. The hero image was optimized at build time. Zero client-side JavaScript was harmed in the making of this page.