Why I Switched to Astro
After years of building with Next.js and Gatsby, I moved to Astro. What changed, what did not, and why I am not looking back.
For a long time, my go-to stack for content-heavy sites was Next.js. It’s powerful, has a massive ecosystem, and I knew it well. But every time I shipped a simple blog or marketing site, I’d end up fighting the framework — configuring hydration, managing unnecessary JavaScript bundles, and questioning why a static article needed a full React runtime.
Then I tried Astro.
The Zero-JS-by-Default Promise
The thing that hooked me immediately was Astro’s core philosophy: ship zero JavaScript by default. Every page is rendered to static HTML at build time. If a component needs interactivity, you opt into it explicitly with client:load, client:idle, or client:visible.
This isn’t just a performance trick — it’s a completely different mental model. You stop thinking “how do I prevent this from being too slow?” and start thinking “does this actually need to run on the client?”
For most content sites, the answer is: almost never.
Content Collections Changed Everything
Before Astro 2.0, managing markdown content felt like a workaround. You’d pull in gray-matter, write your own type checking, and hope nothing broke when a frontmatter field was missing.
Content Collections gave us something I didn’t know I was missing: type-safe content. You define a schema with Zod, and Astro validates every single post at build time. Wrong date format? Build fails. Missing required field? Build fails. It’s the kind of DX that makes you wonder how we survived without it.
const blog = defineCollection({
loader: glob({ pattern: "**/*.md", base: "./src/content/blog" }),
schema: z.object({
title: z.string(),
pubDate: z.coerce.date(),
category: z.enum(["Design", "Development", "Technology", "Life"]),
}),
});
What I Was Worried About
Ecosystem lock-in. I use React components, Vue components, and sometimes plain web components across different projects. Astro’s Island Architecture means I can use any of them — in the same project if needed. That flexibility is real, not marketing.
Routing. I was used to Next.js’s file-based router. Astro’s is nearly identical, plus it has dynamic route generation with getStaticPaths() that feels cleaner than getStaticProps. No complaints here.
Community and longevity. This one I genuinely wasn’t sure about. A year later, Astro has hit v5, the community is growing fast, and the core team ships consistently. I’m less worried now than I was at the start.
The Real Wins
Here’s what actually changed day-to-day:
- Build times are fast. This blog builds in under 3 seconds.
- Lighthouse scores are boring — and boring is good. 100/100/100/100 without any manual optimization.
- The mental overhead is lower. I’m not managing server state, hydration boundaries, or API routes for a blog. I’m writing markdown and Astro components.
- The output is predictable. What you see in dev is what you get in production. No hydration mismatches. No “works on my machine” surprises.
The Trade-offs
Astro isn’t for everything. If you’re building a highly interactive application — dashboards, real-time feeds, complex client-side state — you’ll want something else. Astro is purpose-built for content-first sites, and it’s unapologetic about that.
I’ve also hit a few rough edges with the content layer API in v5 — mainly around how entry.id replaced entry.slug. The migration docs were helpful, but it required a mental shift. Worth it, but worth knowing about.
Would I Recommend It?
If you’re building a blog, portfolio, documentation site, or marketing page: yes, immediately.
If you need a full application framework: look elsewhere, but keep Astro in mind for the frontend shell of a hybrid architecture.
For me, the switch was the right call. Writing a new post now feels like writing a document, not deploying an application. That’s exactly how it should feel.