Why I Switched from Next.js to Astro for My Portfolio
The portfolio graveyard
Let me be honest about my track record:
- 2020 — Gatsby → CMS integration was overkill
- 2020 — Next.js → Worked fine, got bored
- 2026 — Astro → Still here
Every developer rebuilds their portfolio too many times. I’m not going to pretend I’m above that.

But each rewrite taught me something about what a portfolio site actually needs versus what I assumed it needed.
What a portfolio actually needs
After three versions, here’s the real requirements list:
- Static content that loads fast
- Good SEO — meta tags, sitemap, RSS feed
- Markdown/MDX support
- Minimal JavaScript on the client
- Easy to deploy — preferably just a
dist/folder
That’s it. No auth, no database, no API routes, no real-time anything.
Why Gatsby didn’t last
Gatsby in 2020 was powerful but opinionated in ways that didn’t match what I needed. GraphQL for querying local files felt like using a crane to move a coffee cup. The plugin ecosystem was huge but fragile — every Gatsby upgrade broke at least two plugins.
Build times were also rough. For a site with 6 blog posts, waiting 30+ seconds felt wrong.

Why Next.js was fine but wrong
Next.js is great. I use it professionally. But for a portfolio, it’s bringing a framework designed for full-stack web applications to a problem that needs a static site generator.
With Next.js, you’re shipping React to the client by default. Your “About” page — which is literally just text and a photo — gets hydrated with a full React runtime. You can optimize this with server components and static exports, but you’re fighting the framework’s defaults instead of working with them.
Why Astro stuck
Astro is built for content sites. Not adapted for them, not capable of them — built for them. The defaults match what I actually need:
Zero JavaScript by default
An Astro page ships zero client-side JS unless you explicitly add an interactive island. My blog posts are pure HTML and CSS. No hydration, no runtime, no bundle. Just… content.
Content Collections
Define a schema, drop Markdown files in a folder, and Astro gives you typed, validated content:
const posts = await getCollection("blog");
const projects = await getCollection("projects");
No GraphQL, no custom loaders, no CMS integration. Files in, typed data out.
Build performance
The entire site — 10+ pages, blog posts with syntax highlighting, sitemap, RSS — builds in under a second. Gatsby took 30+.

Markdown + Shiki
Code blocks get syntax highlighted at build time with Shiki. No client-side highlighting library, no layout shift. The HTML ships with inline styles already applied.
Component flexibility
I write components in .astro files, but I could use React, Svelte, Vue, or Solid for interactive bits. For this site, I don’t need any of them — but the option exists without framework lock-in.
The migration
Moving from Next.js to Astro was straightforward:
- Blog posts — already Markdown, just moved to
src/content/blog/ - Components — rewrote JSX to
.astrosyntax (similar enough) - Layouts — same slot-based pattern
- Styling — Tailwind works identically
- Config —
astro.config.mjsinstead ofnext.config.js
The biggest change was mental: stop reaching for useState and useEffect. In Astro, if you need state, you probably need an island. For a portfolio, you almost never need state.
When NOT to use Astro
If your site has significant interactivity — dashboards, forms, real-time features — Astro isn’t the right choice. Use Next.js or Remix.
If your site is primarily content with occasional interactivity, Astro is the best tool I’ve used. Three portfolio rewrites later, I think this one might actually stick.
