Next.js · Performance · Web
Why I rebuilt my portfolio in Next.js — and what "superfast" actually means
I had a portfolio that was one file. One index.html, a <style> block, twelve lines of JavaScript for the scroll animation. It scored well. So why move it to Next.js?
Because "fast" isn't one number. A page can win a Lighthouse score and still feel slow — the text reflows when the font loads, the first paint waits on a request to a server three hops away, every visit re-downloads things that never change. "Superfast" is really three separate promises, and the old file only kept one of them.
1. The font was lying to me
The single worst line in the old file was this one:
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif..." rel="stylesheet">
That's a render-blocking request to Google's CDN before the browser can show my headline. Then the font arrives and every heading jumps as it swaps from the fallback — the layout shift you feel as a flicker on half the web.
Next.js's next/font fixes both at build time. It downloads the font files, self-hosts them on my own domain, and reserves the exact space the text will take so nothing moves:
import { Instrument_Serif } from "next/font/google";
const serif = Instrument_Serif({
subsets: ["latin"],
weight: "400",
display: "swap",
variable: "--font-serif",
});
Zero external requests. Zero layout shift. The name says "google" but nothing ships to Google — it's all local after the build.
2. The HTML is already written before you ask for it
This site has no database and no per-visitor logic, so there is no reason to build the page when you request it. Next.js pre-renders it at build time — the server hands you finished HTML the moment you knock. In the build output it shows up as one word:
Route (app)
┌ ○ / (Static) prerendered as static content
That ○ Static is the whole game for a site like this. The most expensive work already happened once, on my machine, before you ever visited.
3. Ship almost no JavaScript
The old file had twelve lines of JS for the scroll reveal, and that was honest — a static page shouldn't need a megabyte of framework to fade some text in. The rebuild keeps that discipline: the entire page is static HTML, and the only client-side code is the same tiny IntersectionObserver, isolated in one small component so nothing else gets dragged onto the client.
"use client";
// ...renders nothing; just fades `.rv` elements in as they scroll into view
Everything else — the layout, the copy, the work index — is server-rendered and never touches the browser's JavaScript engine.
So what does "superfast" actually mean?
Not a score. Three habits:
- Nothing blocks the first paint — fonts self-hosted, no CDN round-trips.
- The work is already done — static pre-rendering, so a visit is a file read, not a computation.
- Almost no JavaScript — ship HTML; add interactivity only where a human will actually feel it.
The framework didn't make this site fast. Those three choices did. Next.js just made them the default — and gave me somewhere to write this down.
This is the first post in Experiences — short, honest write-ups of small things I built and what they taught me. More soon.