Four generations of my personal site, from Gatsby to Next.js
Four live URLs, one product: a Gatsby site with GraphQL and particles, a first Next.js build on v2, an editorial Next 16 site on v3, and the redesign now on the apex.
- Gatsby
- Next.js
- MDX
- Portfolio
- SEO
Four generations of my personal site, from Gatsby to Next.js
I did not migrate once — I shipped four public generations of the same product. Each URL is a time capsule: Gatsby particles, an intermediate Next deploy on the v2 subdomain, the editorial stack (now on v3), and the current design on the apex domain.
Updated October 2026. I first wrote this when the editorial site (Gen 3) was on the apex. That design now lives at v3.danielastudillo.io, and danielastudillo.io runs a fourth generation: Next.js 16, an Apple-inspired redesign, a highlights page and a hardened contact form. The Gen 1–3 sections below are written as of June 2026. The older versions are still online, but search engines are told to skip them.
#TL;DR
I did not migrate once — I shipped four public generations of the same product (my portfolio + writing surface):
| Gen | URL | Stack (high level) | Repo / role |
|---|---|---|---|
| 1 | v1.danielastudillo.io | Gatsby 5, GraphQL data layer, React 18, Bootstrap, tsparticles | personal-site — time capsule |
| 2 | v2.danielastudillo.io | Next.js on Vercel (x-nextjs-prerender, App Router era) | Intermediate Next rewrite — still live |
| 3 | v3.danielastudillo.io | Next.js 16 canary, MDX blog, Tailwind v4, output: "standalone" | personal-site-v2 (private repo, archive/v3 branch) — editorial site, the apex until October 2026 |
| 4 | danielastudillo.io | Next.js 16, MDX blog, Tailwind v4, Apple-inspired redesign, highlights page | personal-site-v2 (private repo) — active |
Gens 2, 3 and 4 share a repo name (personal-site-v2, on different branches) but have different deploy targets and feature depth. “v2” and “v3” are now subdomains serving archived builds; the apex is the fourth generation.
#Generation 1 — Gatsby and GraphQL
When: portfolio-focused years, last refreshed May 2025 on the Gatsby starter lineage.
What it optimized for:
| Strength | Mechanism |
|---|---|
| Image pipeline | gatsby-plugin-image, sharp transforms |
| Typography | @fontsource/playfair-display serif hero |
| Motion | @tsparticles/react landing |
| Hosting | Static CDN on Vercel — zero backend |
Pain that motivated Gen 2:
- Every new MDX field wanted a GraphQL query or plugin touchpoint.
gatsby developrebuild feel when iterating on essays.- Blog as “occasional posts” not a primary surface.
Gen 1 is not embarrassing — it is a measurable baseline for LCP, bundle, and build time.
#Generation 2 — first Next.js (v2 subdomain)
When: roughly May 2025 → early 2026 on v2.danielastudillo.io (still live; Vercel x-nextjs-prerender headers).
Timeline in personal-site-v2 (now a private repo):
| When | Milestone |
|---|---|
| Jul 2024 | Project scaffold |
| May 2025 | Next.js 15 + React 19 + Tailwind v4, dark mode, Font Awesome, projects/about pages |
| Jul 2025 | Contact form, animations, project imagery |
| Jul 2025 | First blog — blog route + MDX support (minimal compared to today) |
What Gen 2 looked like vs Gen 1:
| Dimension | Gen 1 (Gatsby) | Gen 2 (v2 subdomain) |
|---|---|---|
| Data layer | GraphQL nodes | TypeScript data/*.ts |
| Visual identity | Particles + Bootstrap | Different Tailwind v4 system, Helvetica-forward toggles |
| Writing | Rare posts | Minimal blog (MDX landed July 2025, not the 20-post corpus) |
| Hero story | Plugin-era portfolio | Dark mode + font switcher + Framer-style polish |
I kept v1 on its subdomain so “before / after” is a real diff. Gen 2 proved Next could own production DNS before I invested in the editorial site on the apex domain.
#Generation 3 — editorial Next
When: June 2026 — the apex domain danielastudillo.io picked up the full editorial revamp plus ongoing MDX expansion (20 posts, reading guide, case-study links). It stayed there until October, when I moved it to v3.danielastudillo.io to make room for Gen 4.
DNS: Nothing “broke” in the migration so far — it is too early to claim SEO or CWV wins. The operational work was adding CNAME records in Namecheap for v1, v2 and the apex (plus v3 once the editorial site moved there), each pointing at the Vercel DNS targets so all three hostnames stay live for comparison. Gen 2-specific UI on the subdomain is gone from main (overwritten); only the URL snapshot remains.
Stack highlights (Gen 3):
| Concern | Implementation |
|---|---|
| Content | app/blog/content/**/content.mdx — no GraphQL |
| Rendering | force-static where possible; Turbopack dev |
| SEO | App Router metadata / generateMetadata, sitemap.ts, structured data |
| Fonts | Self-hosted /fonts, FontProvider, documented anti-flicker approach |
| Deploy | output: "standalone" + postbuild asset copy for Docker/Vercel |
| Projects | data/projects.ts links to blogSlug case studies |
Visual / UX shift from Gen 1:
- v1: portfolio classic — particles, Bootstrap grid, GraphQL project cards.
- v3: editorial — topic filters, series navigation (
BlogReadingGuide), restrained motion, long-form depth for senior loops.
Gen 3 is not “Gen 2 with more CSS.” It is a content product decision: essays are first-class, projects point to narratives, and performance budgets are explicit (image formats, font cache headers, bundle splitting).
#Transition map (engineering view)
Gen 1 Gatsby 5 + GraphQL + particles
│ pain: essay velocity, GraphQL tax, build feel
▼
Gen 2 Next.js @ v2.danielastudillo.io
│ pain: (fill in) — likely design system + blog scale + CWV targets
▼
Gen 3 Next 16 @ danielastudillo.io + MDX + standalone + 20 posts
│ moved to v3 in October 2026
▼
Gen 4 Next 16 @ danielastudillo.io + Apple-inspired redesign| Migration step | Checklist item |
|---|---|
| 1 → 2 | Export markdown/MDX; replicate routes; redirect old blog slugs |
| 1 → 2 | Replace gatsby-plugin-image with next/image + explicit sizes |
| 2 → 3 | Consolidate content folder convention; wire projects ↔ blog slugs |
| 2 → 3 | Harden metadata, sitemap, structured data as code |
| All gens | Keep prior subdomain alive for comparison |
#Chrome UX Report — field data (June 2026)
PageSpeed Insights surfaces 28-day CrUX p75 metrics under “Discover what your real users are experiencing.” That is not the same as Lighthouse lab scores — it needs enough real Chrome traffic on the exact origin.
| Origin | Field data (mobile, PSI) |
|---|---|
| danielastudillo.io | No Data (report, Jun 2 2026) |
| v1.danielastudillo.io | No Data (low-traffic subdomain; separate CrUX origin) |
| v2.danielastudillo.io | No Data (same — comparison subdomain, not enough volume) |
That is expected for a personal portfolio: CrUX omits origins below the privacy threshold. Until field data appears, treat Lighthouse lab and RUM you own (Vercel Web Analytics, optional web-vitals beacon) as the honest signals. Re-check PSI after the apex migration has had a full CrUX collection window.
#Lighthouse performance (June 2026, mobile, lab)
Headless Chrome, Lighthouse 12.6 — homepage / (same day, three sequential runs). The third row is the editorial site, which was still on the apex when I ran this and now lives at v3:
| URL | Performance | LCP | FCP | TBT | CLS | Speed Index |
|---|---|---|---|---|---|---|
| v1.danielastudillo.io | 87 | 3.0 s | 2.8 s | 0 ms | 0.002 | 4.7 s |
| v2.danielastudillo.io | 91 | 3.6 s | 1.0 s | 30 ms | 0.000 | 1.2 s |
| v3.danielastudillo.io | 92 | 3.0 s | 1.5 s | 80 ms | 0.000 | 3.8 s |
Cold curl on the same day showed Gen 1 HTML at ~237 KB vs ~88 KB for both Next URLs — particles and Gatsby payload explain that gap more than the Performance score spread.
Read: Lab scores cluster high-80s/low-90s with near-zero CLS. Gen 1’s Speed Index (4.7 s) and FCP (2.8 s) lag Gen 2 — consistent with tsparticles + heavier first paint. Gen 2’s LCP (3.6 s) is the slowest LCP despite the best FCP/TBT — hero image weight on that deploy. The v3 home’s Speed Index (3.8 s) reflects a denser editorial layout, not a failed migration.
#Gen 3 performance budget (repo defaults)
Gen 3’s next.config.ts made choices that did not exist on Gatsby v1:
| Knob | Setting | Why |
|---|---|---|
| Images | WebP + AVIF, 1-year minimumCacheTTL | CDN-friendly hero and OG assets |
optimizePackageImports | Framer Motion, Font Awesome, next-themes, Zod | Smaller client graphs without manual barrel surgery |
Webpack splitChunks | Dedicated chunks for React, Framer, FA | Cache isolation on repeat visits |
output: "standalone" | Docker/Vercel postbuild copy | Same artifact locally and in CI |
Webpack context lock | process.cwd() only | Prevents monorepo parent package.json resolution bugs |
Gen 2 on the subdomain inherited an earlier slice of this file; Gen 3 is where blog scale (20 MDX posts, reading guide, project ↔ essay links) met explicit bundle and font-cache policy.
| Metric | Why it still matters |
|---|---|
gatsby build vs next build wall time | Cost of publishing |
Total JS on / | Particles vs editorial home |
| LCP on hero | Image + font strategy |
| Time to add one MDX post | Files touched, commands run |
#Closing thought
Three URLs beat three README bullets: Gatsby proved static images and GraphQL pipelines; the v2 subdomain proved Next could own DNS; the editorial site proved long-form MDX and case-study linking are the product, and Gen 4 keeps that foundation under a new design. Name generations by URL + stack, not by repo folder names alone.
#Reader field guide
When to keep prior deploys alive: You are comparing stack generations; recruiters ask for before/after; SEO and CWV need separate origins measured honestly.
Operational checklist
- Map each generation to URL + stack, not folder name alone (v2 subdomain ≠ current apex deploy)
- Replicate routes and slug redirects when moving MDX between frameworks
- Replace
gatsby-plugin-imagewithnext/image+ explicitsizesduring Gatsby → Next moves - Export
metadata/generateMetadata,sitemap.ts, and structured data as code in Gen 3 - Lab Lighthouse on
/after each major visual change — CrUX may stay “No Data” on low-traffic personal domains - Track wall-clock publish cost (
gatsby buildvsnext build) and JS weight on hero, not vibes - Wire
data/projects.ts→blogSlugso projects point at case-study essays
Lab scores to compare (June 2026 mobile homepage): v1 87, v2 91, v3 92 — see Lighthouse table above for LCP/FCP/CLS detail.
#Related reading
#On this site
| Post | Why |
|---|---|
| Building a CRDT collaborative editor | Shipped product work linked from the current generation |
| Astro + Bun landing template experiment | Static SKU vs this MDX editorial stack |
| Triaging Shai-Hulud on my portfolio v2 | Supply-chain response on personal-site-v2 — audit vs worm IoCs |
#References (curated)
Three live URLs are the migration proof; Next and Gatsby docs are for readers planning their own cutover.
| Reference | Notes |
|---|---|
| Gen 1 — Gatsby | GraphQL image pipelines and particles hero (Lighthouse 87 in lab). |
| Gen 2 — Next subdomain | First App Router deploy on Vercel (91 in lab). |
| Gen 3 — v3 archive | MDX blog + standalone output (92 in lab, May 2026); the apex until October 2026. |
| Gen 4 — apex | Current site: Apple-inspired redesign, highlights page, hardened contact form. |
| Next.js App Router | File-based routes, RSC defaults, and metadata APIs we use now. |
| Next.js — Migrating | App Router migration guide when you outgrow build-time GraphQL for everything. |
| Core Web Vitals | Lab scores are directional—field data on a personal origin is often sparse. |