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 caching —
getStaticProps,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
- Create Astro project:
npm create astro@latest - Add content collections for your posts
- Port layouts — Astro layouts are similar to Next.js layouts
- Migrate components —
.tsx→.astro(or keep React withclient:load) - Deploy —
astro buildoutputs static files todist/
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:loadfor 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.