When I decided to rebuild my portfolio, I had a clear goal: ship as little JavaScript as possible while keeping the developer experience I love. I’d heard Astro was excellent for content-heavy sites, especially blogs, but I’d never used it. I’ve built with React Router, Next.js, and TanStack Start before, so this was the perfect opportunity to learn a new meta framework.
Why Astro?
Astro’s island architecture is brilliant. Most of my portfolio is static content — text, links, images. There’s no reason to hydrate the entire page. With Astro, I write components in React (because I know it well), but they ship as plain HTML by default.
---
// This React component ships ZERO JS to the client
import ProjectCard from '../components/ProjectCard.astro';
---
<ProjectCard title="My App" description="A cool thing I built" />
When I need interactivity — like a theme toggle or mobile nav — I opt in with client:load. Everything else is static.
The monorepo setup
I structured this as a Turborepo monorepo with pnpm workspaces. The portfolio app lives alongside a Hono API and shared packages for database access, common utilities, and UI components. It’s overkill for a simple portfolio, but that’s the point — I wanted a place to quickly spin up experiments and make them public without creating separate repos for every idea.
Content Collections
Astro 5’s Content Collections made the blog trivial to set up. I define a schema with Zod, drop MDX files in a folder, and query them with full type safety. No CMS, no database, no build plugins — just files and types.
const posts = await getCollection('blog', ({ data }) => !data.draft);
Worth learning?
Absolutely. Astro’s approach to partial hydration and content collections feels right for content-heavy sites. The learning curve was gentle coming from React — most concepts transferred directly. If you’re building something like a blog or portfolio and tired of shipping megabytes of framework code, Astro delivers.