Building This Site: A Next.js 15 Portfolio from Scratch
Every developer eventually builds their own site. Here's what I actually care about in mine — and the decisions that shaped the implementation.
Why Next.js 15
The App Router finally feels production-ready. Server Components make it trivial to keep data fetching on the server, which for a content-heavy site like this means:
- Zero client-side API calls for static content
- Syntax highlighting via Shiki runs at build time
- MDX renders entirely on the server
MDX as a Content Layer
I chose MDX over a headless CMS for one reason: the content lives in Git. Every blog post, case study, and art project description is a .mdx file. Version control, no API key dependencies, works offline.
The rendering pipeline uses next-mdx-remote/rsc — the v5 subpath built specifically for App Router Server Components:
import { compileMDX } from 'next-mdx-remote/rsc';
import remarkGfm from 'remark-gfm';
import rehypeShiki from '@shikijs/rehype';
const { content } = await compileMDX({
source: rawMdx,
options: {
mdxOptions: {
remarkPlugins: [remarkGfm],
rehypePlugins: [[rehypeShiki, { theme: 'github-dark' }]],
},
},
});
Deliberate Simplicity
No ISR. No CMS. No client-side data fetching for static content. Every page is either fully static (SSG) or a serverless function (/api/*). The simplicity makes deployment, debugging, and future modifications all easier.