AI SEO Tracker

Product

  • AI Leaderboard
  • Pricing
  • How it works
  • ChatGPT rank tracker
  • LLM rank tracker
  • AI visibility tracker
  • AI SEO Report
  • Tools

Agents

  • Agent docs
  • Setup skill
  • llms.txt

Tools

  • AI Visibility Checker
  • Page Inspector
  • Keyword Research
  • Share buttons

Resources

  • Blog
  • Learn AI SEO
  • LLMOnly

More

  • About
  • Help & contact
  • MagicSpace

© 2026 AI SEO TrackerBuilt by Ilias IsmPrivacyTermsSecurityImprintGDPR

AI SEO Tracker
Leaderboard
Hands on a laptop inspecting a product page, with floating AI model pills
AI SEO Page Inspector

See your page the way AI does

Person reviewing customer questions on a laptop, with floating high-intent prompt pills
AI Keyword Research

Find high-intent prompts from real questions

Student watching an AI SEO lesson, with floating course progress and lesson pills
AI SEO Course

Get AI to recommend you

More tools

AI Visibility CheckerAI Share Button GeneratorAll Tools
Pricing
Log in
Blog

LLMOnly Component: Show Content to AI Bots in Next.js

Copy the LLMOnly component for Next.js 16. It matches AI bot user agents from official docs, includes curl tests, and shows when it counts as cloaking.

Ilias IsmExpert verifiedUpdated 3 Oct 2026 · 16 min read
Summarizechatgpt logoChatGPTperplexity logoPerplexityclaude logoClaudegrok logoGrok

TL;DR

  • What it is: A Next.js server component. It renders its children only for AI bots.
  • Which bots: Take the tokens from each operator's docs. My 2025 list missed Claude-User, Perplexity-User and Amzn-SearchBot.
  • Is it cloaking: It can be. Show bots the same facts as the page. Never new claims.
  • Do this first: Put the text in the HTML for everyone. LLMOnly is the last resort.
  • How to test: curl with each bot's user agent. Then check your logs.

Does AI name your brand? Check your site.

LLMOnly is a small Next.js server component.

It reads the User-Agent header. If the request comes from a known AI bot, it renders its children. If not, it renders nothing.

How LLMOnly works: a request arrives, the component checks if the User-Agent is an AI bot, if yes it renders the extra text, if no it renders nothing
That's the whole idea.

Why does it exist?

Because most AI crawlers don't run JavaScript. Vercel and MERJ published their study in December 2024: none of the crawlers they tested from OpenAI, Anthropic, Meta, ByteDance and Perplexity ran it. (Vercel.)

So a pricing slider or a tab can leave your own page empty for them.

This is the original post for the component. The llm-only npm package from LLMrefs links back to it as its inspiration.

Below: the code, a checked bot list, real bot traffic from our site, a curl test, and where this turns into cloaking.

  • The component
  • Bot list
  • Real traffic
  • Pricing example
  • What we run today
  • Is it cloaking
  • Fix it first
  • Testing
  • Limits

The Complete LLMOnly Component

Copy this file. It runs on Next.js 15 and 16, where headers() is async.

import { headers } from 'next/headers'

// Each token comes from the operator's own crawler docs. Lowercase, matched as a substring.
const AI_BOT_TOKENS = [
  // A person asked an assistant about your page
  'chatgpt-user',
  'claude-user',
  'perplexity-user',
  'duckassistbot',
  'amzn-user',
  'meta-externalfetcher',
  'google-agent',
  'google-gemininotebook',
  // Search crawlers that feed assistant answers
  'oai-searchbot',
  'claude-searchbot',
  'perplexitybot',
  'amzn-searchbot',
  // Training crawlers: delete these if you only care about live answers
  'gptbot',
  'claudebot',
  'meta-externalagent',
  'amazonbot',
  'ccbot',
]

export function isAIBot(userAgent: string | null) {
  const ua = userAgent?.toLowerCase() ?? ''
  return AI_BOT_TOKENS.some(token => ua.includes(token))
}

export async function LLMOnly({ children }: { children: React.ReactNode }) {
  const userAgent = (await headers()).get('user-agent')
  return isAIBot(userAgent) ? <div className="llm-only-content">{children}</div> : null
}

How it works:

  • It reads the User-Agent header on the server. Lowercases it. Checks each token as a substring.
  • On a match, it renders the children in a div.
  • No match? It returns null. The text is not in the HTML a person gets.
  • isAIBot is exported. A route handler can reuse the list with request.headers.get('user-agent').

One correction. An earlier version of this post called the component fast and cached.

It is neither.

Reading headers() makes the route dynamic. Next.js renders it on every request. (Next.js docs.)

And with Cache Components, a headers() call outside a Suspense boundary stops the route from being prerendered. So wrap the component, like in the pricing example below.

Which AI User Agents to Match

Match the token each operator documents. Don't guess.

Three kinds of request reach your server:

Three kinds of AI request. A person asked: ChatGPT-User, Claude-User, Perplexity-User. Search crawlers: OAI-SearchBot, Claude-SearchBot, PerplexityBot. Training crawlers: GPTBot, ClaudeBot, CCBot. The first two decide what an answer says today
The first two matter most.
  1. A person asks an assistant about your page.
  2. A search crawler builds the index an assistant answers from.
  3. A training crawler collects text.

The first two decide what an answer says today.

Token (operator)What it doesIn the 2025 list
GPTBot (OpenAI)Training crawlYes
OAI-SearchBot (OpenAI)Search index for ChatGPT searchYes
ChatGPT-User (OpenAI)A person asks ChatGPT about a pageYes
ClaudeBot (Anthropic)Training crawlYes
Claude-SearchBot (Anthropic)Search result qualityNo
Claude-User (Anthropic)A person asks ClaudeNo
PerplexityBot (Perplexity)Search indexYes
Perplexity-User (Perplexity)A person asks PerplexityNo
DuckAssistBot (DuckDuckGo)Real-time fetch for AI answersYes
Amzn-SearchBot (Amazon)Search and Alexa, no model trainingNo
Amzn-User (Amazon)A user action, such as an Alexa questionNo
Amazonbot (Amazon)Improves products, may train modelsYes
Meta-ExternalAgent (Meta)Training or indexingYes
Meta-ExternalFetcher (Meta)A fetch at a user's requestNo
Google-Agent (Google)An agent on Google infrastructure acts for a userNo
Google-GeminiNotebook (Google)A page that a user added as a sourceBy accident, through gemini
CCBot (Common Crawl)Open web dataset crawlYes
Google-Extended (Google)A robots.txt token onlyListed, but no real request carries it
Applebot-Extended (Apple)A robots.txt token onlyListed, but no real request carries it

Sources: OpenAI, Anthropic, Perplexity, DuckDuckGo, Amazon, Meta, Google crawlers, Google user-triggered fetchers, Apple and Common Crawl. Bingbot, Googlebot and Applebot are classic search crawlers and are left out on purpose.

My 2025 list had five faults.

  • It missed the requests where a person waits for the answer. Claude-User, Perplexity-User, Amzn-User, Meta-ExternalFetcher and Google-Agent.
  • It had amazonbot but not amzn-searchbot. Amazon's example string is Amzn-SearchBot/0.1. That doesn't contain amazonbot.
  • It listed robots.txt tokens as user agents. google-extended and applebot-extended. Google says Google-Extended has no separate HTTP user agent string. Apple says Applebot-Extended does not crawl pages.
  • It listed tokens Anthropic no longer documents. anthropic-ai and claude-web aren't on Anthropic's current crawler page. That page lists three bots: ClaudeBot, Claude-User and Claude-SearchBot.
  • It matched too much. Bingbot. And short tokens like bard, xai and gemini, which match any user agent with those letters.

Tokens for DeepSeek, Grok, Mistral and Cohere are out. Every token here comes from an operator's page.

What 16 Days of Bot Traffic Showed

Real data. From our own site.

Our DataFast bot report holds 11,693 IP-verified bot requests to aiseotracker.com. 2 to 17 September 2026.

16 days of bot traffic: search indexing 57.7 percent, training 36.1 percent, AI answer fetches 6.1 percent, out of 11,693 verified bot requests to aiseotracker.com, 2 to 17 September 2026
Only 6% are a person asking an assistant about us.

By purpose:

  • Search indexing: 6,752 (57.7 percent).
  • Training: 4,226 (36.1 percent).
  • AI answer fetches: 715 (6.1 percent).

An answer fetch means an assistant fetched our page because a person asked.

Then I matched the agent names against my 2025 tokens. (DataFast reports names, not raw user agent strings.)

The biggest bot was not on the list: Amzn-SearchBot 2,537 requests, Bingbot 2,093, meta-externalagent 1,984, GoogleOther 1,022, PerplexityBot 949, ClaudeBot 905
Amazon's search bot. Number one. And my list didn't know it.
AgentRequests2025 list
Amzn-SearchBot2,537Missed
Bingbot2,093Matched, but it is a search crawler
meta-externalagent1,984Matched
GoogleOther1,022Missed
PerplexityBot949Matched
ClaudeBot905Matched
Googlebot835Missed, correctly
ChatGPT-User585Matched
OAI-SearchBot338Matched
GPTBot304Matched
DuckAssistBot71Matched
Claude-User47Missed
Four others23Missed

(The four others: Applebot 11, Google-Read-Aloud 9, Google-NotebookLM 2, meta-externalfetcher 1.)

How did the old list do?

  • It matched 7,229 of 11,693 requests. 61.8 percent.
  • Of the 715 answer fetches, it matched 656. 91.7 percent.
  • It missed 59. Mostly Claude-User (47).

Two things stand out.

Amzn-SearchBot, the largest bot, was invisible to the old list.

And 2,093 of the matched requests came from Bingbot. A search crawler. That's the closest case to the example in Google's cloaking policy.

Real-World Example: Fixing Invisible Pricing

A pricing slider is the clearest case.

The price depends on JavaScript. So it's not in the HTML that a crawler without JavaScript reads.

Three screenshots from August 2025.

Loops.so pricing page showing a dynamic pricing slider that AI cannot read, with a red box highlighting the interactive pricing calculator
Loops.so's pricing slider, boxed in red. Only the static Free plan text sits next to it. Screenshot, August 2025.

Claude saw that a pricing calculator exists. It couldn't read the prices:

Claude AI response showing it can only see basic information from Loops.so pricing page, mentioning 'A pricing calculator feature' but unable to provide actual pricing details
Claude can detect there's a pricing calculator on Loops.so but can't read the actual pricing information. It only sees the static elements. Screenshot, August 2025.

ChatGPT couldn't read the slider either. So it filled the gap from a competitor's blog:

ChatGPT response about Loops.so pricing showing it cites a competitor's blog for the paid plan, with loops.so also listed as a source, demonstrating how dynamic pricing sliders hurt AI visibility
When ChatGPT can't read Loops.so's dynamic pricing, it cites a competitor's review for the paid plan. The sources list loops.so too. Screenshot, August 2025.

Think about that. Your competitor now tells ChatGPT what you cost.

Is the gap still there? For a browser, yes. On 2 October 2026, curl returns about 515 KB of HTML for loops.so/pricing. The text has the free plan and the slider range (100 to 1.5m+).

No dollar amount.

But send an OpenAI bot name, and Loops now returns a Markdown page with 16 priced tiers. So it fixed the gap for bots. Not for browsers. (The full test.)

The best fix: a plain plan table in the page.

If the design can't carry one, LLMOnly can hold the same plans for bots. (The plans below are samples.)

import { Suspense } from 'react'
import { LLMOnly } from '@/components/ui/llm-only'

export default function PricingPage() {
  return (
    <main>
      <PricingSlider />
      {/* Suspense keeps the route prerenderable under Cache Components. The text must match the slider. */}
      <Suspense fallback={null}>
        <LLMOnly>
          <h2>Pricing plans</h2>
          <ul>
            <li>Starter: $29 per month, up to 5 users, 10 GB storage</li>
            <li>Professional: $99 per month, up to 25 users, 100 GB storage</li>
            <li>Enterprise: $299 per month, unlimited users, 1 TB storage</li>
          </ul>
        </LLMOnly>
      </Suspense>
    </main>
  )
}

A visible table or an accordion beats this.

People, bots and Google all read it. And Google says accordions and tabs don't violate its hidden text policy.

$49 report

Does AI name you?

See if AI mentions your brand. Then get the pages to fix.

Check one question freeHow it works
chatgpt logo

What's the best AI SEO tracker for a startup?

Most answers name Semrush. You aren't cited.

Ask ChatGPT

What We Run on Our Site Today

Honest answer: we don't use it anymore.

We ran LLMOnly on our homepage from 23 August 2025, on and off. We dropped it for good in the homepage rewrite of 23 August 2026.

An earlier version of this post said it improved AI citations.

We have no before-and-after data. So we claim no lift.

(To measure an effect, scan the same prompts before and after a change. How to measure AI brand visibility explains how. The AI SEO Tracker report scans five engines for it.)

What we do now:

  • The homepage sends the same HTML to every user agent. On 2 October 2026, curl with a Chrome string, GPTBot, a ChatGPT-User string and a ClaudeBot string each returned the same 249,178 bytes. HTTP 200.
  • For a lean copy, we use a second URL. Not a user agent check. /markdown/blog/llmonly-component returns this post's Markdown source as text/markdown with X-Robots-Tag: noindex. For any client.
  • We don't use content negotiation. A request with Accept: text/markdown to the blog URL returns HTML.

More on that method: Markdown for agents.

Is This Cloaking

It can be. It depends on what the bot text says.

Google's spam policy defining cloaking as showing different content to users and search engines to manipulate rankings and mislead users
Google spam policies, section Cloaking. Screenshot from 2 October 2026.

Google's definition: showing users and search engines different content, with the intent to manipulate rankings and mislead users.

One listed example: adding text only when the user agent is a search engine and not a person. (Google spam policies, updated 28 August 2026.)

The policy covers Google Search. It doesn't name ChatGPT, Claude or Perplexity.

And the crawler pages of OpenAI, Anthropic, Perplexity, Amazon, Apple, DuckDuckGo and Meta don't address it either.

So where's the line?

Where the cloaking line is. Fine: same facts, different form, like your plans as a plain list and your limits in plain units. Cloaking: different claims, like an offer people cannot see or a comparison that is not on the site
Same facts: fine. New claims: cloaking.

Google's page on dynamic rendering draws it. (Google.)

  • It calls dynamic rendering a workaround. Not a long-term solution.
  • Googlebot generally doesn't treat it as cloaking when the content is similar.
  • A page about cats for users and a page about dogs for crawlers? Cloaking.

The same facts in a different form sit on one side. Different claims sit on the other.

The same switch can also be used to mislead.

SPLX researchers described "AI-targeted cloaking". A user agent check serves one page to browsers and another to ChatGPT or Perplexity crawlers. The fetched page becomes the source of the answer. (The Hacker News, 29 October 2025.)

Nobody has published a rule for AI crawlers. So treat Google's line as the minimum:

  • Show the same facts as the visible page. Same plans, limits and numbers.
  • Add no claim, offer or comparison that a person can't find on the site.
  • Use plain headings, lists and tables. So the text parses cleanly.
  • Leave Bingbot and Googlebot out of the list.

Fix It for Everyone First

Try these in order.

Try the fixes in this order: first the same page for everyone, put the text in the HTML. Second the same text at a second URL, a Markdown copy. Third different output by User-Agent, LLMOnly, the last resort
LLMOnly is step three. Not step one.
  1. The same page for everyone.
  2. The same text at a second URL.
  3. Only then: different output by user agent.

What does a crawler without JavaScript get? Vercel lists it (1 October 2026):

  • Static generation, ISR, server rendering: complete HTML.
  • Partial Prerendering: a static shell plus streamed content.
  • Client-side rendering: a shell with script tags. No data.
ProblemRight fixIs LLMOnly the answer?
A slider, tab or accordion hides the price or the feature listA plain table in the HTML for everyone. A collapsed block is fineNot needed
The page is an empty shell until JavaScript runsServer-render or prerender the pageNo, fix the rendering
You want a lean copy for agentsThe same text as Markdown at its own public URLNo
Extras that match the page, such as plan limits in plain unitsLLMOnly with the same facts, inside SuspenseYes, within the limits below
Claims or offers that people cannot seeDo not do itNo, that is cloaking

A note on htmlLimitedBots.

This Next.js option sets which user agents get blocking metadata instead of streaming metadata.

A value you set replaces the default list. That default holds Google crawlers, Bingbot, Twitterbot and Slackbot. (Next.js docs.)

Vercel adds: under Partial Prerendering, GPTBot, ClaudeBot, PerplexityBot and OAI-SearchBot are not on that list. So they receive the streaming shell. Its article gives a regex to extend the list. (Vercel.)

What that means: a crawler that stops reading before the stream ends could miss LLMOnly text inside Suspense.

No operator documents this. So test with curl. And check your logs.

Once the text is readable, what else moves citations? See how to get cited in AI answers.

Testing the Component

Run curl with each bot's user agent. Count a phrase that only the LLMOnly block contains:

URL=https://yoursite.com/pricing
PHRASE="a phrase that only the LLMOnly block contains"
for ua in "GPTBot" "ChatGPT-User" "OAI-SearchBot" "ClaudeBot" "Claude-User" "Claude-SearchBot" "PerplexityBot" "Perplexity-User" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 Chrome/126.0 Safari/537.36"; do
  printf '%s: ' "$ua"
  curl -s -A "$ua" "$URL" | grep -c "$PHRASE"
done

Expect 1 or more for every bot. And 0 for the Chrome line.

Every line is 0? Check the HTTP status. Vercel says a 403 or 429 points at a firewall or CDN rule. Not at robots.txt.

In a browser: open More network conditions in DevTools. Turn off "Use browser default" under User agent. Enter GPTBot/1.0. Reload.

Then check your logs. Curl proves your code. Your logs prove what real bots receive. Search them for the tokens above and check for status 200.

Don't use Page Inspector for this. Page Inspector renders the page before it extracts the text. And it doesn't send an AI bot user agent. So it shows neither what a crawler without JavaScript gets, nor LLMOnly content. Use curl for both.

Limits and Costs

Four things to know before you ship it.

Every request renders.

The page is dynamic. If a CDN or proxy caches the HTML, send Vary: User-Agent. Or keep the page out of the shared cache.

Vary makes caches store responses separately by the headers it lists. (MDN.)

Without it, a cache can hand the bot version to a person.

A user agent is a string the client picks.

Google warns that user agent strings can be spoofed. Common Crawl says crawlers falsely identify as CCBot.

Our own numbers, from the DataFast report for 23 August to 1 September 2026 (the first date it holds data):

  • 378 of 6,911 bot requests (5.5 percent) were user agent only. DataFast couldn't verify the source IP.
  • That included 74 of 256 ChatGPT-User requests.

So keep nothing private in bot-only text.

Browser agents get the human page.

Vercel says browser-based agents like Perplexity's Comet and OpenAI's Atlas render pages the way a person's browser does.

LLMOnly can't reach them.

A firewall comes first.

A firewall or CDN rule can block a bot before your code runs. Whatever your robots.txt says.

Implementation for Webflow & Framer

LLMOnly needs code that runs on your server for each request.

Your site builder doesn't give you that? Then put the text in the page. In an accordion, or a plans-in-text block.

Google says accordions and tabs don't violate its hidden text policy.

Two things not to do:

  • Don't hide text with CSS for bots. Google lists text positioned off-screen as hidden text abuse.
  • Don't check navigator.userAgent in a script. It never runs in a crawler that skips JavaScript.

For real switching, an edge proxy like a Cloudflare Worker can read the header. The same cloaking limits apply.

$49 report

Does AI name you?

See if AI mentions your brand. Then get the pages to fix.

Check one question freeHow it works
chatgpt logo

What's the best AI SEO tracker for a startup?

Most answers name Semrush. You aren't cited.

Ask ChatGPT

Ilias Ism

Founded LinkDR, MagicSpace SEO, AI SEO Tracker, and GenPPT.

XLinkedIn

On this page

  • The Complete LLMOnly Component
  • Which AI User Agents to Match
  • What 16 Days of Bot Traffic Showed
  • Real-World Example: Fixing Invisible Pricing
  • What We Run on Our Site Today
  • Is This Cloaking
  • Fix It for Everyone First
  • Testing the Component
  • Limits and Costs
  • Implementation for Webflow & Framer
$49 report
Does AI name you?

See if AI mentions your brand. Then get the pages to fix.

chatgpt logo

What's the best AI SEO tracker for a startup?

Most answers name Semrush. You aren't cited.

Check one question free

Keep reading

AI SEO Agent: How to Build One, With a Real Run

An AI SEO agent is a model, tools, a loop and rules. Build a read-only one with MCP and a skill, and see the plan ours wrote: zero new posts.

3 Oct 2026

How to Use Claude Code for SEO: 6 Jobs With Real Output

Use Claude Code for SEO and AI search: set it up, add house rules, connect live data, and run six jobs. Each one has the real output from our own site.

3 Oct 2026

Claude SEO Skills: How a SKILL.md Works (3 Real Examples)

What a Claude SEO skill is, how a SKILL.md file works, three real skills you can read today, and a template to write your own with an approval gate.

3 Oct 2026