A while ago I ran curl against my own portfolio and got back half a kilobyte of HTML. The only visible text in it was the page title: 17 characters. My experience, my projects, the links to my articles, all of it was rendered in the browser by JavaScript.
For visitors, that was fine. For a LinkedIn link preview, an ATS crawler or any bot that doesn't run JavaScript, my portfolio was an empty page called "Mehdi's Portfolio". Every route shared that one title, with no description and no Open Graph tags.
The site was a React 19 SPA built with Vite, routed with react-router and deployed on Vercel, with a rewrite sending every path to index.html . I moved it to the Next.js App Router so every page ships as real HTML. This post is about the parts that turned out less simple than I expected.
One rule: visitors shouldn't notice
I gave myself a single constraint: same content, same design, pixel for pixel.…