Technical SEO · · 2 min read · Updated
Generative Engine Optimization (GEO) & CWV Gates in Next.js
How to audit Next.js apps for AI search citations (JSON-LD entity graphs, llms.txt, bot rules) and enforce Core Web Vitals client bundle budgets in CI.
Zayne Lovecraft
Software Engineer III

Ranking in traditional search and getting cited by AI answer engines (ChatGPT Search, Claude, Perplexity) come down to the same discipline: clean semantic HTML, machine-readable entity graphs, and fast page loads. Yet most teams only discover a broken schema block or a bloated client bundle after traffic drops.
To catch both classes of regressions early, I open-sourced two zero-dependency CLIs: geo-audit (case study) for Technical SEO + Generative Engine Optimization (GEO), and next-vitals-guard for Next.js client bundle budgets.
1. Auditing AI search readiness with geo-audit
Traditional SEO linters check whether you have a <title> tag and a <meta name="description">. That's table stakes, but AI crawlers and RAG pipelines care just as much about how unambiguously your page declares its entities and whether your site exposes a machine-readable map.
Running geo-audit against a live URL scores 8 signals out of 100:
$ npx github:zaynelovecraft/geo-audit https://www.zaynelovecraft.com
GEO & Technical SEO Audit — https://www.zaynelovecraft.com/
✓ [10/10] Title tag (15-65 chars) — "Zayne Lovecraft | Full-Stack Engineer, AI & Technical SEO" (61c)
✓ [10/10] Meta description (50-165 chars) — 145 chars
✓ [10/10] Canonical link tag — https://www.zaynelovecraft.com
✓ [15/15] Single semantic <h1> — Found 1 <h1> tag(s)
✓ [10/10] Semantic HTML5 landmarks (<main>, <article>) — main=true, article/section=true
✓ [20/20] JSON-LD structured data — Types: Person, WebSite
✓ [15/15] robots.txt allows AI citation crawlers — GPTBot / ClaudeBot / PerplexityBot allowed
✓ [10/10] /llms.txt machine-readable index — Present
Adding a dynamic /llms.txt route in Next.js App Router
When I first ran geo-audit against this portfolio, it scored 90/100 because /llms.txt was missing. Adding a dynamic route handler in src/app/llms.txt/route.ts fixed the gap in 30 lines of TypeScript and keeps the file automatically in sync with new blog posts and case studies:
// src/app/llms.txt/route.ts
import { getAllPosts } from "@/lib/posts";
import { OPEN_SOURCE_REPOS, PROJECTS, SITE_CONFIG } from "@/lib/site-data";
export function GET() {
const posts = getAllPosts();
const body = [
`# ${SITE_CONFIG.name}`,
`> ${SITE_CONFIG.summary}`,
"",
"## Case Studies",
...PROJECTS.map((p) => `- [${p.name}](${SITE_CONFIG.url}/work/${p.slug}): ${p.summary}`),
"",
"## Open Source",
...OPEN_SOURCE_REPOS.map((r) => `- [${r.name}](${r.url}): ${r.description}`),
"",
"## Articles",
...posts.map((p) => `- [${p.title}](${SITE_CONFIG.url}/articles/${p.slug}): ${p.description}`),
].join("\n");
return new Response(body, {
headers: { "Content-Type": "text/plain; charset=utf-8" },
});
}
2. Catching "use client" bundle bloat with next-vitals-guard
As I covered in Understanding Client Components and Client Boundaries in Next.js and Engineering 100 Lighthouse Scores, a single misplaced "use client" directive can pull a heavy library into your shared layout chunk and tank Interaction to Next Paint (INP) overnight.
next-vitals-guard walks .next/static/chunks right after next build, computes the exact zlib.gzipSync transfer size of every .js file, and exits non-zero if any single chunk or total client payload crosses your threshold:
$ npx github:zaynelovecraft/next-vitals-guard --max-kb=220 --max-chunk-kb=90
Next.js Client Bundle Budget (.next/static/chunks)
CHUNK RAW GZIP
--------------------------------------------------------------
1q4itoff--b7w.js 223.5 kB 69.9 kB
1bf99sczvmond.js 160.5 kB 43.6 kB
0cz1d0mv5g_q7.js 110 kB 38.7 kB
--------------------------------------------------------------
Total Client JS (gzip): 184.4 kB (budget: 220 kB)
✓ All client chunks within Core Web Vitals budget.
Because it relies only on Node's built-in fs, path, and zlib modules, it runs in under 200ms in GitHub Actions without headless Chrome overhead.
Takeaway
You don't have to guess whether a deploy hurt your search visibility or your bundle budget. Run geo-audit against your staging URL and next-vitals-guard after next build so every release stays fast and citable.
- Technical SEO
- GEO
- Core Web Vitals
- Next.js
- JSON-LD
Keep reading

AI · · 7 min read
Top 10 AI Tools to Build With Today (and How to Use Them)
The 10 AI tools I actually build with, from Cursor and Claude to the AI SDK, Vercel Workflow, and MCP, and how they fit into real automation pipelines.

AI · · 3 min read
Filesystem-First AI Agent OS for Cursor and Claude Code
How I structured a central filesystem workspace with a self-updating memory.md correction loop, 3-layer research logs, and reusable Cursor skills.