Skip to content
Writing

Building This Site: Astro 5, Cloudflare Workers, and Why I Ditched the SPA

How I built my portfolio site with Astro 5 and Cloudflare Workers, and the architecture decisions behind every choice.

3 min read
  • astro
  • cloudflare
  • typescript
  • web-performance
  • architecture

My old portfolio was a React + Vite single-page app. It worked fine, but it had one problem I couldn’t stop thinking about: a crawler hitting it got an empty <div id="root"> and a script tag, and nothing else until the JS ran. For a personal site whose entire job is to be found and read, that always felt backwards.

So I rebuilt it. Here’s what I picked and why.

The stack

  • Astro 5: static-first, with no framework runtime shipped on content pages
  • Tailwind CSS v4: utility-first, configured in CSS
  • TypeScript: strict mode, no any, named exports only
  • Cloudflare Workers: unlimited free bandwidth, a big edge network
  • GitHub Actions: CI/CD with Lighthouse gates

Why Astro over Next.js

The deciding question for me was: what does the default output look like?

Next.js ships the React runtime on every page, even the static ones. For a site that’s mostly text and images, that’s weight I’m paying for nothing. Astro renders HTML first and lets me sprinkle in client-side code only where I actually need it, which here is theme switching, the mobile menu, and search. That’s the whole interactive surface. Everything else is just HTML.

The Content Layer sealed it. Blog posts are Markdown with Zod-validated frontmatter, so a typo in a schema is a build error, not a surprise at runtime. I run strict TypeScript everywhere else, so having the content pipeline hold the same line felt right.

Why Cloudflare over Vercel

A few reasons:

  1. Unlimited bandwidth on the free tier. Vercel’s Hobby plan caps at 100 GB and technically prohibits commercial use, and a portfolio that might land you work is arguably commercial.
  2. A bigger edge network: more points of presence, faster more places.
  3. Cloudflare acquired Astro in early 2026, so the integration is first-party now.

Deploying is about as simple as it gets: astro build spits static HTML into dist/, and wrangler deploy pushes it to Workers. No adapter needed while the site stays static.

Quality gates

Every PR has to clear astro check (types + templates), Biome (lint and format), Vitest, Playwright with axe-core for accessibility, and Lighthouse CI. If a Lighthouse score drops under the threshold, the PR fails, which is mostly there to stop me from slowly wrecking my own performance budget without noticing.

What I’d do differently

If I were building a real app (auth, dashboards, live data) I’d reach for Next.js and not think twice. Astro is built for content sites, and the point was to use it for what it’s good at.

The other thing: I’d have figured out how the site should actually look much earlier. The first version was technically solid and completely generic-looking. Sorting out the visual side later is what moved it from “works” to “feels like mine.”

Source

The code’s still private for now, but the architecture notes and write-ups live here on the site.

Back to all writing