Your Next.js Site Is Invisible to AI — Here's How I Fixed Mine

GEO Audit - AI Visibility for Next.js
author picture

Oscar Gallo

Published on March 25, 2026

I spent a weekend going down the GEO rabbit hole. Then I built a Claude Code skill to audit any Next.js site in minutes. My score went from 7.1 to 8.4 out of 10 — and the fixes were embarrassingly simple.


A few days ago, I wanted to know how well my sites rank in AI engines. Not Google. AI engines — ChatGPT, Claude, Perplexity, Gemini.

I have three sites: oscarcodeslife.com, byposting.com, and byrenovate.com. All built on Next.js. All "SEO optimized" — or so I thought.

I started with oscarcodeslife.com. And here's the thing — I assumed that good SEO meant good AI visibility. That if Google could find me, ChatGPT could cite me.

I was wrong.


SEO Is Not GEO

Let me be clear about something. SEO and GEO are not the same thing. They overlap, but they diverge in ways that will cost you if you ignore them.

SEO (Search Engine Optimization) is about ranking in traditional search results. Google crawls your page, indexes it, ranks it based on hundreds of signals — backlinks, content quality, page speed, mobile friendliness.

GEO (Generative Engine Optimization) is about being cited by AI systems. When someone asks ChatGPT "what's the best fractional CTO service?" or Perplexity "how do I launch an MVP in 2 weeks?" — will your site be in the answer?

The difference is fundamental. Google's crawler executes JavaScript. It sees your client-rendered React components. It waits for your useState to resolve.

AI crawlers don't.

GPTBot, ClaudeBot, PerplexityBot — they read the raw server HTML. If your content is behind a 'use client' directive with no server-side rendering? It doesn't exist to them. It's a blank page.

Think about that for a second.

You could have the best content on the internet, and every AI engine on the planet would see an empty <div id="root"></div>.


The Weekend Rabbit Hole

I went deep. And I mean deep. I read everything I could find about how AI crawlers work, what signals they look for, what makes a site "citable" versus invisible.

After a whole weekend of going through the rabbit hole, I found something interesting: the best practices for GEO aren't mysterious. They're specific. They're checkable. And most of them are things you can fix in your codebase in a single afternoon.

So I did what any builder would do. I turned those best practices into a Claude Code skill that audits your Next.js site automatically.

It's called nextjs-geo-audit. And it checks everything.


What the Skill Actually Audits

The audit runs 9 steps across 5 weighted categories. Here's each one — what it checks, why it matters, and what good looks like.

1. Robots Configuration

Your robots.ts file tells crawlers what they can and can't access. Most Next.js sites have a basic one. But most don't explicitly allow AI crawlers.

Why it matters: While a wildcard userAgent: '*' technically allows everyone, explicitly listing AI crawlers reinforces intent. It also lets you fine-tune access per bot — maybe you want to allow GPTBot but rate-limit Bytespider.

What good looks like:

import { type MetadataRoute } from "next";

export default function robots(): MetadataRoute.Robots {
  return {
    rules: [
      {
        userAgent: "*",
        allow: "/",
        disallow: ["/api/", "/admin/"],
      },
      {
        userAgent: [
          "GPTBot",
          "ChatGPT-User",
          "OAI-SearchBot",
          "ClaudeBot",
          "anthropic-ai",
          "PerplexityBot",
          "Google-Extended",
          "Applebot-Extended",
        ],
        allow: "/",
        disallow: ["/api/", "/admin/"],
      },
    ],
    sitemap: "https://yoursite.com/sitemap.xml",
  };
}

My site had a basic wildcard rule. No explicit AI crawler entries. That's a signal gap.

2. Sitemap

Your sitemap tells crawlers which pages exist. Sounds simple. But I had 19 public pages and only 10 were in my sitemap. Nine pages — including every single blog post — were invisible to discovery.

The audit cross-references every page.tsx file in your app directory against every URL in your sitemap. If a page file exists but isn't in the sitemap, it gets flagged.

What you need:

  • Dynamic sitemap.ts (not a static XML file)
  • Every public page listed
  • lastModified, changeFrequency, and priority set on each entry
  • Blog posts included (these are often forgotten)

3. llms.txt — The New Standard

This is the one most people haven't heard of yet.

llms.txt is a machine-readable Markdown file that tells AI models what your site is about. Think of it as a README for AI — a structured summary with your key pages, descriptions, and contact info.

When ChatGPT or Claude browses your site, they check for /llms.txt to quickly understand what you do and where your important content lives.

My site didn't have one. At all.

What it looks like:

// src/app/llms.txt/route.ts
export async function GET() {
  const content = `# Your Site Name

> One-paragraph factual description of your site/product.

## Key Pages

- [Homepage](https://yoursite.com/): Brief description
- [Pricing](https://yoursite.com/pricing): Brief description
- [Blog](https://yoursite.com/blog): Brief description

## Contact

Email, social links, etc.
`;

  return new Response(content, {
    headers: {
      "Content-Type": "text/plain; charset=utf-8",
      "Cache-Control": "public, max-age=86400, s-maxage=86400",
    },
  });
}

Simple. Takes 10 minutes. Massive impact on AI discoverability.

4. JSON-LD Structured Data

This is how you tell search engines and AI what your content means, not just what it says. JSON-LD is structured data embedded in your HTML that follows the schema.org vocabulary.

The audit checks for the right schema on each page type:

Page typeRequired Schema
Root layoutOrganization
HomepageWebSite or SoftwareApplication
FAQ pageFAQPage
Blog postsArticle or BlogPosting
Pricing pageProduct or Offer

My site had good coverage — ProfessionalService on the homepage, Person on about me, BlogPosting on each blog post. But it was missing the Organization schema in the root layout. That's the one that feeds Google's Knowledge Panel and tells AI engines who runs the site.

5. Metadata

This one you probably know. But the audit goes deeper than "do you have a title?"

It checks:

  • metadataBase — without this, your Open Graph images break
  • Title template"OCL - %s" so every page gets a branded title
  • Unique descriptions on every page
  • Canonical URLsalternates.canonical prevents duplicate content issues
  • Open Graph and Twitter cards — for social sharing and AI citation
  • No accidental noindex on public pages (P0 blocker if found)

I had 3 pages — /branding, /calendar, /links — with zero metadata. No title, no description, no OG, no Twitter cards. Nothing. They were ghosts.

6. SSR Content Visibility

This is the big one. The one that separates GEO from SEO.

AI crawlers do not execute JavaScript.

Let that sit for a moment.

If your page file starts with 'use client' and your content is rendered client-side, AI crawlers see nothing. An empty HTML shell.

The audit scans every page.tsx in your app and checks:

  • Is it a Server Component or Client Component?
  • If client, is the content still in the initial server HTML?
  • Are there useState-driven patterns hiding content?
  • Are FAQ answers behind state toggles instead of <details> elements?

My site scored 9/10 on SSR in the first audit — most pages were Server Components. But the second, more thorough audit caught that 3 pages (/talks, /youtube, /profile) were 'use client' with content invisible to crawlers. The profile page had my entire resume — experience, projects, tech stack — completely invisible to AI.

The fix for /profile? Delete one line. Remove 'use client'. The page used zero browser APIs.

7. Link Integrity

Simple but important. The audit:

  • Cross-references every internal href with existing page files
  • Flags href="#" placeholder links
  • Flags broken internal links

Crawlers follow links. Broken links waste their crawl budget and signal a poorly maintained site.

8. Next.js Configuration

The final check:

  • Middleware — is it blocking bot user agents?
  • Custom 404 — does not-found.tsx exist?
  • Custom error page — does error.tsx exist?
  • Static generation — are public pages using SSG/ISR or are they unnecessarily force-dynamic?

My Real Results

I ran the audit on oscarcodeslife.com. Twice. Before and after fixing everything.

Before: 7.1 / 10

CategoryScore
Technical GEO (robots, sitemap, llms.txt)6/10
Structured data (JSON-LD)7/10
Metadata6/10
SSR content visibility9/10
Link integrity & Next.js config6/10

What was wrong:

  • 9 pages missing from the sitemap — including every blog post
  • No llms.txt — AI models had no machine-readable summary
  • No Organization JSON-LD — Google and AI couldn't attribute the site
  • 3 pages with absolutely zero metadata
  • No canonical URLs on any page
  • No explicit AI crawler rules in robots.ts

After: 8.4 / 10

CategoryScore
Technical GEO (robots, sitemap, llms.txt)9/10
Structured data (JSON-LD)9/10
Metadata9/10
SSR content visibility7/10
Link integrity & Next.js config9/10

What I fixed:

  • All 19 pages now in the sitemap with proper metadata
  • llms.txt route handler created with full site summary
  • Organization JSON-LD added to root layout
  • Every page now has title, description, OG, Twitter cards, canonical URLs
  • Explicit AI crawler rules for GPTBot, ClaudeBot, PerplexityBot, and more

What's left:

  • 3 client component pages (/talks, /youtube, /profile) still need refactoring from client to server components
  • No llms-full.txt extended version yet
  • No custom 404 or error pages

The SSR score actually went down from 9 to 7 between audits — not because it got worse, but because the second audit was more thorough. It correctly identified that 'use client' pages with static data are still invisible to AI crawlers, even though Next.js technically SSR-renders them for the initial load.


The Scoring System

The skill uses weighted categories because not everything matters equally:

CategoryWeight
Technical GEO (robots, sitemap, llms.txt)20%
Structured data (JSON-LD)20%
Metadata15%
SSR content visibility30%
Link integrity & Next.js config15%

SSR visibility gets the heaviest weight — 30%. Because if your content isn't in the server HTML, nothing else matters. You can have perfect metadata, perfect JSON-LD, a beautiful llms.txt — and it's all worthless if the actual content is client-rendered and invisible.

Every finding gets a priority classification:

  • P0 — Blocker: noindex on public pages, AI crawlers blocked, page content invisible
  • P1 — Critical: No robots.ts, no sitemap, no JSON-LD, content hidden from SSR
  • P2 — Important: No llms.txt, missing descriptions, no canonical URLs
  • P3 — Nice to have: Missing Twitter cards, no custom 404

And every fix includes a working code snippet you can paste directly into your codebase. No guessing. No Googling. Just apply and ship.


Install It

The skill is open source. Install it and run it on your own Next.js project:

git clone https://github.com/Oscar-Codes-Life/nextjs-geo-audit.git ~/.claude/skills/nextjs-geo-audit

Then ask run the command/skill in Claude Code:

/nextjs-geo-audit

It'll scan your codebase and generate a full GEO_AUDIT_REPORT.md with scores, findings, and code fixes.


The Takeaway

If you're building on Next.js and you haven't thought about GEO, you're building a site that's invisible to the fastest-growing discovery channel on the internet.

AI search isn't coming. It's here. People are asking ChatGPT and Perplexity questions instead of Googling. And if your site isn't optimized for those engines, you don't exist in that world.

The good news? The fixes are concrete, specific, and mostly simple. A few hours of work took my site from 7.1 to 8.4. The hard part wasn't the fixing — it was knowing what to look for.

That's why I built the skill. So you don't have to spend a weekend in the same rabbit hole I did.

Are you paying attention?