โ† All notes

May 25, 2026ยท3 min read

How I built this website (mostly by vibe-coding it)

A static Next.js site, a public library RSS feed, a Spotify scraper, and a daily cron. No backend, no database, ~$10/year all-in.

  • #meta
  • #engineering
  • #web

I'm a systems engineer. Frontend is not my world. I spend my days on cloud hardware, firmware, BMCs, and the kind of distributed plumbing that nobody sees until it breaks at 3am. So when I decided to put up a personal site, I had a constraint: I didn't want it to become a second job.

This post is a walk-through of what's actually under the hood โ€” partly so future-me has a map, partly because I think the architecture is genuinely nice for anyone in the same position.

The stack, in one paragraph

The whole site is a pile of JSON files, a handful of React components, and a build step that turns them into static HTML. Vercel hosts the output on a global CDN. Cloudflare points my domain at Vercel. Tailwind makes it look like I have taste. That's it.

No backend. No database. No server process running 24/7. If a meteor took out every Vercel data center tomorrow, I could redeploy the same files to any static host in fifteen minutes.

The content layer

Every page on the site reads from a JSON file in content/:

  • profile.json โ€” name, tagline, bio, social links
  • now.json โ€” the "what I'm doing now" timeline
  • achievements.json, projects.json, sports.json โ€” what they sound like
  • generated/books.json, generated/podcasts.json โ€” auto-synced (more on that in a second)

A tiny lib/content.ts wraps fs.readFile and gives each shape a TypeScript type. Every page just calls something like getProfile(). It doesn't know or care where the data came from. Which means I can edit JSON, push, deploy, and the site updates โ€” no React, no JSX, no thinking about components.

The "live" data

The books and podcasts pages are the only ones I never edit by hand.

scripts/sync-books.ts scrapes my public Santa Clara County Library shelves via BiblioCommons RSS feeds. scripts/sync-podcasts.ts calls Spotify's API for the shows I follow. Both write to JSON files that get committed and read like any other content.

These scripts run as prebuild โ€” so every npm run build starts with a fresh data pull. If a sync fails (SCCLD sometimes returns a bot challenge), it logs a warning and leaves the existing JSON in place. The build never crashes because the library was grumpy.

Covers are filled in by a second-pass enrich script that tries Open Library first, Google Books second, and falls back to a placeholder if both miss.

How it ships

git push  โ†’  vercel --prod
              โ†“
   Vercel runs `npm install`
              โ†“
   prebuild โ†’ sync:books + sync:podcasts (fresh JSON)
              โ†“
   next build โ†’ 13 static HTML pages
              โ†“
   Uploaded to Vercel's global edge network
              โ†“
   Aliased to harshbhuwania.com

A vercel.json cron fires once a day at 09:00 UTC, hits a deploy hook, and runs the whole pipeline again. So the books and podcasts stay current even when I don't push for a week.

The three external pieces

This part confused me for embarrassingly long, so writing it down:

Service Job
Cloudflare Registered the domain, hosts the DNS. Tells the world "harshbhuwania.com lives at 76.76.21.21".
Vercel Builds the site, hosts the static files on their CDN, manages HTTPS, runs the cron.
Tailwind A CSS framework that lets me write styling as utility classes on the HTML itself. No separate CSS files.

Three services, three completely separate jobs. If I ever want to leave Vercel, I change one DNS record at Cloudflare. If I ever want to leave Cloudflare, I transfer the domain.

What I'd tell past-me

  1. Don't pick a CMS. I almost did. JSON files in a Git repo are a CMS, and they're the best one I've ever used.
  2. Static is enough. A personal site has maybe 100 visitors a month. It does not need a database.
  3. Treat scrapers as eventually-consistent. Always fall back to the last-known data; never let a third party break your build.
  4. Vibe-code the layout, but type the data. TypeScript types in lib/content.ts catch every malformed JSON edit before it hits prod. The rest of the code โ€” the React, the Tailwind classes โ€” I genuinely barely understand. That's fine.

Total cost: ~$10/year for the domain. Total time to first deploy: about one weekend. Total maintenance since: zero.