Astro vs Next.js for Content-Driven Side Projects
- #comparison
If your side project is mostly content, a blog, docs, a portfolio, the choice between Astro and Next.js comes down to how much interactivity you actually need. Not which one is “better.” There isn’t a better here.
What Astro gets right
Astro ships zero JavaScript by default. Every component renders to static HTML at build time unless you explicitly opt one into hydration with a client:* directive. For a blog where 95% of the page is prose, your Lighthouse score is close to perfect without tuning anything, because there’s barely any JS to ship in the first place.
It’s also framework-agnostic where you need interactivity: drop a React or Svelte component in for a search box or a comment widget and leave everything else as plain HTML. A heavy client-side library never taxes the rest of the page just because one widget needed it.
Content collections, what this site runs on, give typed frontmatter and a clean query API. Define a schema once, in src/content.config.ts, and every post gets checked against it at build time:
const posts = defineCollection({
schema: z.object({
title: z.string(),
publishedDate: z.date(),
tags: z.array(z.string()),
draft: z.boolean().default(false),
}),
});
Forget a required field or typo a tag’s type, and the build fails before it ever ships, instead of quietly rendering a page with undefined where a date should be. Querying is a one-liner: getCollection('posts') gets you every post, typed, ready to sort and filter. It’s purpose-built for exactly this kind of project.
What Next.js gets right
Next.js assumes your app is an app. Server components, client components, route handlers, middleware, the full toolkit for real interactivity, auth, and data mutations. If your “content site” is going to grow into something with accounts and a dashboard, Next.js gives you a path there without a framework migration later.
The cost for a pure content site is more upfront concepts (server vs. client components, the App Router’s file conventions, when to reach for a Server Action) and, by default, more JavaScript shipped than an equivalent Astro page even when nothing on the page needs interactivity at all. React Server Components narrowed this gap a lot compared to older Next.js versions, where every page shipped the full React runtime whether it needed it or not. It hasn’t closed it entirely, and for a page that’s genuinely just text and images, you’re still paying for a runtime you’re not using.
Content in Next.js also usually means reaching for a library like Contentlayer or rolling your own MDX pipeline with next-mdx-remote, since there’s no first-party equivalent to content collections. It’s solvable, plenty of production sites do exactly this, but it’s a decision and a dependency you have to make yourself rather than getting for free out of the box.
I’ve used Next.js for years, and it’s still what I’d point a beginner at first, even outside of content sites specifically. It gets you real experience on both the frontend and the backend in one project, with one set of conventions, which matters more early on than picking the theoretically optimal tool. The learning curve shows up later, once performance starts to matter: knowing when something needs to be a server component versus a client component isn’t always obvious, and the line between them trips people up well past the beginner stage, myself included some days.
Honestly, I’d just ask this
Is the interactivity a small add-on to content, or is the content a small part of an interactive product? A blog with a comment widget is still fundamentally content, so Astro fits. A SaaS dashboard with a marketing page and a changelog attached is fundamentally an app, so Next.js fits, even though part of it reads as “just content.” Get this backwards and you either fight Astro to bolt on features it wasn’t built for, or ship a content site paying an app framework’s JS budget for nothing.
| Astro | Next.js | |
|---|---|---|
| Default JS shipped | Near zero | Low with RSC, but nonzero |
| Content collections | Built in, typed | Needs a library or custom setup |
| Interactivity model | Opt-in islands, any framework | React everywhere |
For this site specifically, content-first with a couple of small interactive pieces (the post filter, search), that’s exactly Astro’s sweet spot. I didn’t seriously consider Next.js when I started this blog, and having lived with the choice for a while now, I still wouldn’t. The one time I’d reconsider is if a comment system or user accounts ever got added, at which point enough of the site becomes “app” that the calculation above flips.