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 linksnow.jsonโ the "what I'm doing now" timelineachievements.json,projects.json,sports.jsonโ what they sound likegenerated/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
- 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.
- Static is enough. A personal site has maybe 100 visitors a month. It does not need a database.
- Treat scrapers as eventually-consistent. Always fall back to the last-known data; never let a third party break your build.
- Vibe-code the layout, but type the data. TypeScript types in
lib/content.tscatch 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.