Astro or Next.js? Choosing a Stack for Content-Heavy Sites

Why this portfolio uses Next.js for the interactive homepage and Astro for the blog — and a checklist for deciding on your own project.

Two stylised planets, one orange and one indigo, orbiting a shared sun

This website is built with two frameworks. The interactive homepage is a Next.js app; the blog you are reading is generated by Astro. That sounds like over-engineering until you look at what each part of the site actually does.

Two different defaults

Next.js assumes your page is an application: React owns the whole document, and interactivity is the default. Astro assumes your page is a document: HTML is the default, and interactivity is opt-in, one “island” at a time.

Neither default is better. They are optimised for different ratios of content to interaction.

When Astro shines

A blog post is 95% text. Shipping a JavaScript framework to render paragraphs is waste. Astro’s content collections give you typed frontmatter, and pages ship zero JavaScript unless you ask for it.

ts
import { defineCollection, z } from 'astro:content';
import { glob } from 'astro/loaders';

const blog = defineCollection({
  loader: glob({ pattern: '**/*.md', base: './src/content/blog' }),
  schema: z.object({
    title: z.string(),
    description: z.string(),
    pubDate: z.coerce.date(),
    tags: z.array(z.string()),
  }),
});

export const collections = { blog };

Built-in Markdown, syntax highlighting, RSS and file-based routing mean the whole blog needs almost no custom infrastructure.

When Next.js shines

The homepage is the opposite: a custom cursor, a WebGL project viewer, charts, a chat widget and a dozen other interactive pieces that share state. That is an application, and React with Next.js handles it well — including image optimisation and static export.

Using both

Both frameworks can export static files, so combining them is mostly a matter of build scripts: build the Next.js app, build the Astro blog into a /blog folder, and deploy the merged output to Netlify or Vercel. Shared design tokens live in one CSS file both projects import, so the header and footer look identical.

Decision checklist

  • Mostly text, occasional widgets? Astro.
  • Heavy shared client state? Next.js.
  • Authors editing Markdown in Git? Astro content collections.
  • Personalised or authenticated pages? Next.js with server rendering.
  • Both? Split by section, not by page — and keep the design system shared.

Choosing the right tool for each part of a site is a performance decision too; the Core Web Vitals playbook explains why.

Thanks for reading! Have a question or a project in mind? Get in touch or leave a comment.