I’m not primarily a frontend engineer. My day job is infrastructure, platform engineering, and making sure systems don’t fall over. But I build side projects, I write, and I care about performance. So when I decided to rebuild my personal site, I spent time evaluating options before committing.
I landed on Astro. Six weeks of using it in production convinced me it’s the right tool for content-heavy sites. Here’s why.
The Problem with Modern Frontend Defaults
Most frontend frameworks ship too much JavaScript. React, Vue, Next.js — they’re excellent tools designed for interactive applications with complex state. A personal website with a blog is not that. But if you reach for Next.js (the default “professional” choice), you inherit all that machinery.
A Next.js “Hello World” ships around 70kb of JavaScript before your code runs. For a blog where readers are there for the content, that’s unnecessary overhead that:
- Increases Time to Interactive
- Burns mobile data budgets
- Requires hydration before the page is usable
Astro’s default is the opposite: zero JavaScript. It renders everything to static HTML at build time. You opt in to JavaScript for the pieces that need it (interactive components), rather than opting out.
Content Collections Are a Game Changer
The feature that sealed it for me was Astro’s Content Collections with Zod schema validation.
// src/content/config.ts
import { defineCollection, z } from 'astro:content';
const blog = defineCollection({
type: 'content',
schema: z.object({
title: z.string(),
date: z.coerce.date(),
category: z.enum(['aws-cloud', 'devops-sre', 'software-engineering']),
draft: z.boolean().default(false),
}),
});
export const collections = { blog };
This schema runs at build time. If you have a markdown file with a malformed date or an invalid category, the build fails with a clear error message. You catch content errors before deploying, not after.
Compare this to manually parsing frontmatter and hoping your markdown files are consistent. I have 15+ blog posts. Without type checking, one malformed frontmatter field would silently produce a broken post. With Astro Content Collections, that’s impossible.
The Island Architecture Is Actually Useful
Astro’s “island architecture” means interactive UI components are isolated. You mark specific components to hydrate on the client:
---
import SearchBar from './SearchBar.tsx';
import StaticHeader from './StaticHeader.astro';
---
<!-- This renders to static HTML, zero JS -->
<StaticHeader title="Blog" />
<!-- This hydrates and ships its React bundle -->
<SearchBar client:load />
For my site, the interactive pieces are:
- The constellation canvas background (pure JavaScript)
- The category filter on the blog listing page
- The role cycling animation in the hero
Everything else — navigation, blog posts, certifications page — is static HTML. The JavaScript budget is tiny and intentional.
Build Performance
Astro builds are fast. My current site builds in under 3 seconds on my laptop. I’ve worked with Next.js projects at work that take 40+ seconds for a production build.
$ npm run build
🚀 astro v4.16.0 launched in 312ms
▶ Generating static pages
├─ / 130ms
├─ /blog 89ms
├─ /certifications 71ms
└─ /blog/[...3 posts] done
✓ Completed in 2.8s
For a site I deploy manually after writing a post, build time matters. I don’t want to wait.
The Developer Experience
A few smaller things that add up:
File-based routing — src/pages/blog/[slug].astro just works. No router configuration.
Scoped styles — CSS in .astro files is scoped to that component by default. No CSS modules setup, no naming conventions. It’s just :where(.scope-abc) .card { } under the hood.
TypeScript first — astro/tsconfigs/strict gives you proper type safety including typed frontmatter from your collection schema.
Shiki syntax highlighting — Built in. One config line to get beautiful GitHub Dark code blocks.
When Not to Use Astro
Astro is a static site generator at heart. If you need:
- Server-rendered pages with dynamic data per user
- Complex real-time state
- A full application with authentication flows
Then Astro’s server mode (output: 'server') can handle some of these, but you’re probably better served by Next.js, Remix, or a dedicated backend.
For content sites, portfolios, documentation, and blogs — Astro is currently the best tool available.
Deploying to AWS
Since I host on AWS S3 + CloudFront, the deploy process is three commands:
# Build
npm run build
# Sync to S3 (--delete removes stale files)
aws s3 sync dist/ s3://husbch.com --delete
# Clear CloudFront cache
aws cloudfront create-invalidation \
--distribution-id $CF_DIST_ID \
--paths "/*"
The dist/ output from Astro is pure static files — HTML, CSS, JS, images. It deploys anywhere that can serve static files. No Node.js runtime required. No Lambda@Edge. Just S3 + CloudFront doing what they’re designed to do.
I’ll take that over maintaining a serverless Next.js deployment any day.