Why I Switched to Astro (And Haven't Looked Back)
I’ve built things with Next.js. I’ve built things with Nuxt. I’ve built things with Create React App, Gatsby, Remix, and a handful of other frameworks that promised to make my life easier. They mostly did. But they all shared one assumption I started questioning: that every page needs a JavaScript runtime.
The weight of modern frameworks
A typical React site ships somewhere between 70–150KB of JavaScript just to hydrate a page that could have been static HTML. For content-heavy sites — portfolios, blogs, marketing pages — that’s a significant amount of work the browser does before a user can interact with anything.
I’d grown so used to this that I stopped noticing it. Lighthouse scores in the 60s felt normal. Then I built something with Astro.
Zero JS by default
Astro’s core idea is simple: send HTML. If a component doesn’t need interactivity, it doesn’t need JavaScript on the client. You write components, they render at build time, and what ships to the browser is clean, fast HTML.
This sounds obvious. It wasn’t, apparently, for most of the ecosystem.
---
// This runs at build time only — zero client JS
const data = await fetch('https://api.example.com/posts');
const posts = await data.json();
---
<ul>
{posts.map(post => <li>{post.title}</li>)}
</ul>
That fetch happens once, at build. The user gets pre-rendered HTML instantly.
Islands architecture
When you DO need JavaScript — a carousel, a form, a search input — Astro uses “islands.” You opt specific components into client-side hydration:
<!-- Only this component ships JS -->
<SearchBar client:load />
<!-- Everything else: pure HTML -->
<Header />
<BlogList />
<Footer />
You can even control WHEN it hydrates: client:load, client:idle, client:visible. The last one is particularly useful — don’t hydrate until the component is in the viewport.
Content collections
For a portfolio or blog, Astro’s content collections are exactly what you want. Type-safe frontmatter, markdown/MDX support, and a query API that feels like a local database:
const posts = await getCollection('blog');
const sorted = posts.sort((a, b) =>
b.data.date.valueOf() - a.data.date.valueOf()
);
No external CMS needed for simple sites. No database. Just files.
The tradeoffs
Astro isn’t for everything. If you’re building a highly interactive web app — a Notion clone, a data dashboard, a real-time chat — you want a full client-side framework. Astro’s sweet spot is content-driven sites that need to be fast and are mostly read.
For my portfolio, it was the perfect fit. The build output is a folder of HTML files. Deployment is a git push. Lighthouse scores are green across the board.
Sometimes the right tool is the boring one.