What I Learned Building This Site with Astro
I picked Astro for my personal site because it promised zero JS by default. Six weeks later, here's what held up and what surprised me.
When I set out to rebuild my personal site I had one hard requirement: no JavaScript shipped to the browser unless I specifically ask for it. Most of what a portfolio site does — display text, show images, link to things — doesn’t need JavaScript at all. So why send it?
Astro’s pitch is exactly that. Islands architecture, zero JS by default, ship HTML. I’d been watching it from a distance for a while and finally had a good excuse to build something real with it.
Content Collections are the right abstraction
The feature that surprised me most was Content Collections. The idea is simple: put your markdown files in src/content/, define a schema with Zod, and Astro gives you a type-safe API to query them.
const projects = defineCollection({
loader: glob({ pattern: '**/*.md', base: './src/content/projects' }),
schema: z.object({
title: z.string(),
date: z.coerce.date(),
featured: z.boolean().default(false),
}),
});
Adding a new project to my portfolio is now just dropping a markdown file into a folder. No database, no CMS, no build step beyond what Astro already does. For a site I maintain alone, this is exactly the right level of complexity.
Tailwind v4 is different
I paired Astro with Tailwind CSS v4, which is a significant departure from v3. There’s no tailwind.config.js anymore — configuration lives in a @theme block inside your CSS file:
@theme {
--color-primary: #3BF686;
--font-sora: 'Sora', system-ui, sans-serif;
}
The mental model is cleaner once you get used to it. CSS custom properties all the way down, no plugin system, no PostCSS config to wrestle with. The migration from v3 isn’t trivial if you have an existing project, but starting fresh it’s genuinely nicer.
Where I hit friction
Static paths with the new loader API. Astro v6 changed the Content Collections API. The old proj.render() method is gone — it’s now render(proj) imported from astro:content. I ran into this as a build error on my project detail pages, but the fix was a two-line change once I understood what changed.
Google Fonts and the render-blocking request. Fonts loaded from an external CDN block rendering until they arrive. I used rel="preconnect" hints which helps, but the ideal solution is to self-host the font files. That’s on my list for the next pass.
No hot module replacement for markdown. Editing a markdown file in dev mode doesn’t always trigger a fast refresh. Small annoyance, not a dealbreaker.
What I’d do the same
Everything about the architecture. Static output, content collections, Tailwind for styling, no framework for interactivity I don’t need. The site builds in under two seconds and ships no JavaScript. Lighthouse scores are in the high 90s without any optimization work.
If you’re building a portfolio, blog, or any content-heavy site that doesn’t need real-time data, Astro is genuinely the right tool. The learning curve is shallow if you know HTML, and you’re not fighting the framework when you want to do something simple.