This blog runs on Astro. I built it in a single session on March 21st, 2026, working alongside an AI coding agent in Cursor. It went from an empty subfolder to a live site on GitHub Pages in a few hours — including an RSS feed, hero images, and a deployment pipeline.

Here’s the actual build story, not the polished version.

Why Astro (and not the other ten options)

I evaluated Hugo, Next.js, Eleventy, and Astro. My agent recommended Astro. I trusted it. Here’s why that turned out to be the right call:

Markdown-first with type safety. I defined a schema in content.config.ts with five categories (AI & LLMs, Building Products, Product Management, Project Management, Tools & Workflows), required fields like title and pubDate, and optional fields like heroImage. If I write a post with a typo in the category — say “product-managment” — the build fails. Not in production. At build time. That’s the kind of guardrail that matters when you’re shipping fast.

Zero JavaScript by default. Every page ships as pure HTML and CSS. No hydration, no client-side rendering, no bundle to optimize. This blog loads instantly because there’s nothing to load. Build time for 6 pages: ~740 milliseconds.

Modern developer experience. Hot reload, TypeScript, component-based layouts. It feels like working in a real framework, not fighting a templating engine. And if I ever want to embed an interactive chart or a live demo in a post, Astro supports partial hydration — I can drop in a React or Svelte component without loading the whole framework.

The architecture decision: subfolder in a monorepo

My main workspace is a private monorepo (where_cursor_feels_home) that holds my journals, knowledge base, rules, and every project I build. I wanted the blog source code to live there too — centralized, searchable, part of the same context graph.

But GitHub Pages needs a public repository. So the setup is:

  • Source: victoria-ai-blog/ subfolder in the private monorepo
  • Build output: pushed to a separate public repo vcrosby22/victoria-ai-blog via GitHub Actions
  • Live site: vcrosby22.github.io/victoria-ai-blog

This means I write blog posts alongside my journals and rules, build locally, and the CI pipeline handles the rest. Source stays private. Only the built HTML goes public.

The gotchas (because there are always gotchas)

Astro uses import.meta.env.BASE_URL for path resolution. I configured base: "/victoria-ai-blog" in astro.config.mjs. The homepage hero linked to ${base}blog/ which resolved to /victoria-ai-blogblog/ — no slash between the base and the path. Every internal link was broken.

The fix was one character: base: "/victoria-ai-blog/" — add a trailing slash. But finding it meant deploying, clicking “Read the blog,” getting a 404, inspecting the HTML, and tracing the path concatenation. Classic “one character, thirty minutes” bug.

The .md extensions that appeared in URLs

Astro 5’s content collections include the file extension in post.id. So my URLs were coming out as /blog/building-with-ai-agents.md/ instead of /blog/building-with-ai-agents/. Not broken, but ugly and not what you’d expect.

The fix: post.id.replace(/\.mdx?$/, "") everywhere I generate a slug — in the dynamic route, the blog index, and the homepage. Three files, same one-liner.

The underscore that GitHub Pages couldn’t see

After deploying, all styling disappeared. The CSS lives in Astro’s _astro/ directory, and GitHub Pages runs Jekyll by default. Jekyll ignores any directory starting with an underscore.

The fix: drop an empty .nojekyll file in public/. It gets copied to dist/ during the build and tells GitHub Pages to serve files as-is. This is a well-known gotcha, but if you don’t know about it, you’ll spend an hour wondering why your CSS returns 404.

The RSS feed that looked “broken”

I added @astrojs/rss and created a feed endpoint at /rss.xml. When I checked it in Chrome, I saw raw XML. My first reaction: “It’s broken.” It wasn’t — that’s just what RSS looks like in a browser.

But I didn’t want visitors to see raw angle brackets, so I built an XSL stylesheet (rss-style.xsl) that transforms the XML into a styled HTML page when viewed in a browser. RSS readers still consume the raw XML. Browsers get something readable.

The content model

Every post is a Markdown file in src/content/blog/ with typed frontmatter:

---
title: "Post Title"
description: "Why someone should read this"
pubDate: 2026-03-21
category: "ai"
tags: ["agents", "cursor"]
heroImage: "/victoria-ai-blog/images/blog/hero.png"
---

Categories are an enum. Tags are a string array. The hero image is optional. The pubDate gets coerced to a Date object at build time. Adding a post means creating a file, writing Markdown, and committing. No CMS, no database, no API calls.

The deployment pipeline

The GitHub Action checks out the monorepo, installs dependencies in the victoria-ai-blog/ subfolder, runs astro build, and pushes the contents of dist/ to the gh-pages branch of the public repo using a deploy token.

It’s triggered manually or on push to main. The whole build-and-deploy cycle takes about 45 seconds. Most of that is GitHub Actions overhead — the actual Astro build is under a second.

What this cost

  • Hosting: free (GitHub Pages)
  • Domain: none yet (using vcrosby22.github.io/victoria-ai-blog)
  • Dependencies: Astro, three plugins
  • Build time: 740ms for 6 pages
  • Total time from empty folder to live site: one session, maybe four hours including debugging the trailing slash, the .nojekyll fix, and the RSS stylesheet

What’s next

Tag-based filtering on the blog index. Maybe a dark mode toggle (the CSS variables are already set up for it). More posts — I have a backlog of topics about data ingestion, AI agent calibration, and the difference between project management and product management.

But the main lesson from this build is the same one I keep learning: start where you are, ship something real, and improve it live.


If you’re evaluating static site generators in 2026, I’d recommend Astro unless you have a specific reason not to. The DX is excellent and the defaults are right. More about my AI-assisted build workflow.