Building this portfolio site
The stack behind this site — Next.js, MDX, Tailwind, and a live GitHub feed — and why I chose it over a CMS.
I shipped a portfolio site over a weekend. Here’s the stack and why I made the choices I made — in case it’s useful for your own.
The stack
| Layer | Choice |
|---|---|
| Framework | Next.js 16 (App Router) |
| Styling | Tailwind CSS v4 |
| Components | shadcn-style (Radix + lucide-react + inline brand SVGs) |
| Blog | MDX in repo, rendered via next-mdx-remote/rsc |
| Code highlighting | rehype-pretty-code (Shiki, build-time) |
| GitHub data | Public REST API + 1-hour ISR |
| Hosting | Vercel |
Why MDX in repo (not a CMS)
Three reasons:
- Version-controlled. Drafts live on branches, history in
git log. Same workflow as everything else I ship. - No CMS overhead. Nothing to host, nothing to pay for, no schema migration on a Sunday night.
- React inside Markdown. When prose isn’t enough, I drop a component in. Useful for the kind of posts I want to write — embedded charts of Fabric capacity metrics, interactive DAX explainers, that sort of thing.
The trade-off is that publishing requires git push. For a personal site that’s a feature, not a bug.
GitHub repos, live
The /projects page calls https://api.github.com/users/<me>/repos from a Server
Component with Next.js’s revalidate set to one hour. Fresh data without
making the hot path slow:
const res = await fetch(
`https://api.github.com/users/${username}/repos?sort=updated&per_page=100`,
{ next: { revalidate: 3600 } }
);I filter out forks and archived repos, sort by stars, and render the top six. That’s the whole integration — no SDK, no auth (until I hit rate limits).
A small foot-gun: lucide-react brand icons
Lucide-react removed Github, Linkedin, and Twitter from its export surface.
If you’re working from an older tutorial, you’ll get a 500 the first time you
load the page. I inlined the SVG paths in a brand-icons.tsx file instead —
two minutes of work, and they’ll never break again.
That’s the site. Boring choices, fast pages, easy to maintain. Exactly what I want a portfolio to be.