Repository navigation
SEO AEO Review
Status: approved by the maintainer on 2026-10-14. All top-ranked recommendations, plus llms.txt as an experimental opt-in in v0.5, are scheduled; see ROADMAP, AEO stance and ADR 0012. The review text below is kept as written. The verification addendum at the end records which claims marked (recall) were confirmed against live sources and which were dropped or softened. Where the two disagree, the addendum wins.
Review only. I made no changes to the repo. Repo state reviewed: README, ROADMAP (v0.1 to post-1.0, scope freeze 2026-10-14), docs/*, ADR 0001–0011, docs/research.md, docs/review.md.
npm weekly downloads come from api.npmjs.org/downloads/point/last-week/<pkg> for 2026-10-06 → 2026-10-12. Every URL below returned HTTP 200 when I checked it today, unless marked otherwise. Where a quote comes from my own knowledge and not a page fetched today, it is marked (recall, re-verify).
next-herald is ahead of everything in the Next.js ecosystem on three things: native-object output, a sitemap index with news support, and a zero-dependency CI checker. It is on par with Nuxt SEO and behind it overall. Nuxt SEO is the real benchmark. It ships sitemap, robots, schema.org graph, OG images, a link checker, SEO utils and nuxt-llms, and it has a large install base (@nuxtjs/sitemap 306K/wk, @nuxtjs/robots 260K/wk). The plan is almost silent on AEO. Its only AEO item is an aiCrawlers: 'allow' | 'disallow' toggle, and that toggle merges together crawlers that do different jobs: training, search-indexing and user-fetch bots. That is the biggest gap, and the cheapest fix.
Legend: ✅ yes · ◐ partial · — no · (p) planned in next-herald
| Feature | next-herald (planned) | Native Next 16 | next-seo 7.3.0 | next-sitemap 4.2.3 | schema-dts 2.1.0 | Nuxt SEO 6.0.1 | Astro (@astrojs/sitemap 3.7.4, astro-seo 1.2.0) | Yoast / Rank Math | Lighthouse 13.5 / unlighthouse 0.19 |
|---|---|---|---|---|---|---|---|---|---|
| Downloads/wk | — | — | 472,622 | 638,748 | 3,138,833 | @nuxtjs/seo 124,014; sitemap 306,316; robots 259,772; schema-org 175,745; og-image 182,419; nuxt-llms 43,857 | sitemap 3,814,836; astro-seo 103,492; astro-robots-txt 25,002 | WP plugins (not npm) | lighthouse 5,404,521; unlighthouse 40,625 |
| Typed meta builders | ✅(p) native Metadata
|
✅ | ◐ (defers to native) | — | — | ✅ useSeoMeta + site config |
◐ astro-seo component | ✅ UI | — |
| Deep metadata merge | ✅(p) | — | — | — | — | ✅ | — | n/a | — |
| Site-wide config / identity | ✅(p) defineHerald
|
◐ metadataBase | ◐ | ◐ | — | ✅ nuxt-site-config | ◐ site
|
✅ | — |
| JSON-LD typed + XSS-safe | ✅(p) | guide only | ✅ components | — | types only | ✅ @graph w/ @id linking, auto Org/WebSite/WebPage |
— (manual) | ✅ auto graph | — |
Auto-linked @graph (Org→WebSite→WebPage→Article→Person) |
◐(p v0.4 graph()) |
— | — | — | — | ✅ | — | ✅ | — |
| Sitemap from routes | ✅(p) typed params | manual | — | ✅ postbuild | — | ✅ auto + sources | ✅ auto | ✅ | — |
| Sitemap index | ✅(p v0.3) | — | — | ✅ | — | ✅ multi-sitemaps | ✅ | ✅ | — |
| News / image / video sitemap | ✅(p) | img/video | — | — | — | ✅ | ◐ | ✅ (News = premium) | — |
| Accurate lastmod source | — (rejects build time, no source) | manual | — | build time | — | ✅ (content updatedAt, git-aware options) |
◐ lastmod option |
✅ post modified | — |
| hreflang | ✅(p) | manual | ◐ | ✅ | — | ✅ via i18n | ✅ i18n option | ✅ (WPML) | ✅ audit |
| robots.txt builder | ✅(p) | ✅ | — | ✅ | — | ✅ + non-prod lockdown, X-Robots-Tag | ◐ third party | ✅ | ✅ audit |
| AI crawler controls | ◐(p) single toggle | manual | — | — | — | ✅ robots groups incl. AI bots, content signals (recall, re-verify) | — | ✅ Yoast/Rank Math AI crawler toggles (recall) | — |
| llms.txt | — | — | — | — | — | ✅ nuxt-llms | 3rd party | ✅ Yoast & Rank Math generate it | — |
| Markdown versions of pages | — | — | — | — | — | ◐ (Nuxt Content raw md (recall)) | 3rd party | — | — |
| OG image templates | ✅(p) | ✅ ImageResponse | — | — | — | ✅ nuxt-og-image | 3rd party | ◐ | — |
| CI checker (HTTP crawl) | ✅(p) herald check, SARIF |
— | — | — | — | ◐ link-checker + devtools | — | — | ✅ (needs Chrome) |
| Link checker (broken internal links) | ◐ (only canonical/sitemap targets) | — | — | — | — | ✅ | — | ◐ premium | ◐ |
| Content/readability analysis | — (non-goal) | — | — | — | — | — | — | ✅ | — |
| IndexNow | — | — | — | — | — | — (recall) | 3rd party | ✅ Rank Math & Yoast Premium (recall) | — |
| Redirect manager | — | next.config | — | — | — | — | — | ✅ | — |
| Zero runtime deps | ✅ | n/a | ✅ | — (4) | 1 | — | — | n/a | — |
| Cache Components / static-safe | ✅ | ✅ | ✅ | ✅ (static files) | n/a | n/a | n/a | n/a | n/a |
Sources: nuxtseo sitemap, robots, schema-org, seo-utils, link-checker, nuxt-llms, @astrojs/sitemap, Yoast features, Yoast llms.txt, Rank Math llms.txt, Lighthouse SEO audits, unlighthouse, Next htmlLimitedBots. (rankmath.com/features/ returned 404.)
Ahead: zero deps together with native objects; App Router sitemap index on Next 16, where next-sitemap is broken on Vercel (#1048); XSS-safe JSON-LD; the planned headless-free CI checker with SARIF. No Next/Astro competitor has that checker, and Lighthouse needs Chrome.
On par: hreflang, OG templates, news/image/video sitemaps, preview lockdown (Nuxt and Yoast have all of these).
Behind: a linked @graph entity model, AI-crawler granularity, llms.txt (CMS and Nuxt already ship it), a lastmod source, an internal broken-link check, IndexNow, and any AEO-oriented checks.
The scope freeze stays: none of these go into v0.1 unless they are data or naming fixes inside existing v0.1 issues (items 1 and 2 qualify, because they change the shape of an existing API before it ships).
| # | Addition | SEO/AEO | Impact | Effort | Fit (zero deps, Cache Components) | Milestone | Evidence |
|---|---|---|---|---|---|---|---|
| 1 | Split the AI-crawler toggle by purpose. Replace `aiCrawlers: 'allow' | 'disallow'withai: { training: 'allow' |
'disallow', search: 'allow' | 'disallow', userFetch: 'allow' }` backed by a dated data table: training = GPTBot, ClaudeBot, Google-Extended, Applebot-Extended, CCBot, Bytespider, meta-externalagent; search = OAI-SearchBot, Claude-SearchBot, PerplexityBot; user = ChatGPT-User, Claude-User, Perplexity-User. Default: training per the user's choice, search allowed. | AEO | Very high: blocking "AI" wholesale silently removes the site from ChatGPT search citations | XS (data + types) |
| 2 |
Document honestly what Google-Extended does not cover. It controls Gemini training/grounding, not AI Overviews/AI Mode, which use Googlebot. You cannot opt out of AI Overviews without opting out of Search (beyond nosnippet/max-snippet). Expose max-snippet/nosnippet/data-nosnippet guidance. |
AEO | High (stops a common mistake) | XS (docs) | ✅ | v0.1 docs | Google common crawlers, AI features & your website |
| 3 | Entity graph by default: `defineHerald({ organization | person, sameAs: [...] })→ autoOrganization/WebSite/WebPagewith stable@ids (${siteUrl}/#org) and articles referencing author/publisherby@id. Move graph()andperson` up from v0.4. |
SEO+AEO | High (entity consistency, E-E-A-T signals, Nuxt/Yoast parity) | M | Pure builders ✅ | v0.4 → pull to v0.2-adjacent after freeze |
| 4 |
"Bots see content" checks: herald check --ua already fetches as bot. Add content-js-only (rendered <main>/<article> text below N chars in server HTML), content-bot-parity (text diff of bot UA vs browser UA, catching htmlLimitedBots regressions) and metadata-streamed (title/meta outside <head> for non-limited bots). |
AEO+SEO | High: AI crawlers don't run JS | S (scanner exists; add body-text extraction) | ✅ HTTP only, no browser | v0.2 | Vercel "Rise of the AI crawler" ("none of the major AI crawlers currently render JavaScript": OAI-SearchBot, ChatGPT-User, GPTBot, ClaudeBot, Meta-ExternalAgent, Bytespider, PerplexityBot; Gemini/AppleBot render). Note: the study dates from Dec 2024, so re-test. |
| 5 |
Accurate lastmod source helpers: lastModified from content updatedAt, plus a CLI herald routes --git-lastmod (via git log -1 --format=%cI, child_process, no dep). Rule lastmod-future, lastmod-uniform (all entries equal means build-time stamping) and lastmod-mismatch (sitemap lastmod vs JSON-LD dateModified / article:modified_time). |
SEO+AEO (freshness) | High | S | Build-time data ✅; never request-time | v0.3 | Google build sitemap ("Google uses lastmod if consistently and verifiably accurate"), Article dateModified |
| 6 |
IndexNow helper: herald indexnow --changed (CLI, global fetch), key file via app/[key].txt/route.ts force-static, posting changed sitemap URLs (diff against the previous build's sitemap). Feeds Bing (which grounds ChatGPT search and Copilot), Yandex, Seznam, Naver. |
AEO+SEO | Med-high | S | ✅ CLI only; no runtime | v0.5 | IndexNow docs |
| 7 |
Markdown alternates (opt-in recipe + helper): app/[...slug]/md/route.ts or .md route with force-static, and <link rel="alternate" type="text/markdown"> via alternates.types. Accept-header negotiation only as a middleware/proxy recipe, because reading Accept in a page breaks Cache Components (ADR 0006). The helper takes markdown the user already has (MDX/CMS); herald does not convert HTML. |
AEO | Medium, with evidence still forming (Cloudflare and Vercel ship it; no engine has confirmed ranking/citation use) | S-M | ✅ if static route; negotiation must live in proxy.ts
|
v0.5 (experimental) | Cloudflare Markdown for Agents, Vercel content negotiation |
| 8 |
llms.txt generator (opt-in, labelled low-evidence): defineLlmsTxt(herald, { sections }) route returning text from the same route list/sitemap source; optional llms-full.txt from user-provided markdown. Cheap because the route source already exists. Its main real use is coding agents and IDE doc tools, not search citation. |
AEO (low) | Low-med | XS-S | ✅ static route | v0.5, flagged experimental | llmstxt.org, Yoast, Rank Math, nuxt-llms |
| 9 |
Content signals line in robots (Content-Signal: search=yes, ai-input=yes, ai-train=no) as an opt-in passthrough. It is non-standard, so it is emitted as a comment-compatible line and documented as an expression of preference, not enforcement. Mention pay-per-crawl as infrastructure (Cloudflare), out of scope. |
AEO | Low-med (legal/preference signal) | XS | ✅ | v0.3 | contentsignals.org, Cloudflare content signals, Pay per crawl |
| 10 |
Citation-friendly structure checks (info severity, HTML-only, no scoring): exactly one <h1>, heading order, a visible byline/date when Article JSON-LD claims author/datePublished, JSON-LD vs visible-content consistency (headline equals h1/title, Product price present in text). |
AEO+SEO | Medium | S-M | ✅ | v0.4 | Article guidelines; Google structured-data policy that markup must reflect visible content |
| 11 |
Internal broken-link rule (link-broken, link-redirect): the crawler already follows links. |
SEO | Medium (Nuxt parity) | XS | ✅ | v0.2 | nuxtseo link-checker, Lighthouse SEO |
| 12 |
Lighthouse SEO audit parity: viewport-missing, http-status (4xx on indexable pages), link-text-generic, img-alt-missing, crawlable-anchors (no href/javascript:). |
SEO | Medium | XS-S | ✅ | v0.2/v0.4 | Lighthouse SEO audits |
| 13 |
AI referral measurement recipe (docs only): match document.referrer/server logs for chatgpt.com, perplexity.ai, copilot.microsoft.com, gemini.google.com, claude.ai; note ChatGPT appends utm_source=chatgpt.com; Bing Webmaster Tools has an AI Performance report (recall, re-verify: I could not find a working URL); Google AI Overview/AI Mode clicks are folded into Search Console "Web". Optionally herald logs to count AI bot hits from an access log (Node readline). |
AEO | Medium (users want proof) | XS docs / S CLI | ✅ no runtime code | v0.5 | OpenAI bots, Google AI features |
| 14 | Demote FAQPage in batch 1: keep the builder but document that FAQ rich results show only for well-known government/health sites (2023) and that Google later dropped several more types (2025). Don't market it as a rich-result win. Don't add HowTo. | SEO | Medium (honesty, scope) | XS | ✅ | v0.1 docs | HowTo/FAQ changes, Simplifying search results (2025), FAQPage doc |
| Idea | Verdict | Reason |
|---|---|---|
| llms.txt as a ranking/citation lever | Hype (but cheap to ship opt-in) | It is a proposal (llmstxt.org) with no standards body behind it. No major answer engine (OpenAI, Google, Perplexity, Anthropic search) has publicly documented that it reads llms.txt for search/citation. Google's John Mueller has said that no AI system uses it and compared it to the keywords meta tag, and Google's AI-features guidance says no special AI files or markup are needed (recall, re-verify the exact wording on ai-features). Adoption: Yoast, Rank Math, Nuxt and many docs sites generate it. The real consumers are coding agents and doc tools. Ship it as experimental and never claim visibility gains. |
| "GEO scoring" / AI-visibility scores | Reject | Not measurable from HTML. Same reason the repo already rejects keyword density. |
speakable schema |
Reject | Beta, US/English news publishers only, aimed at Google Assistant; no AI-engine evidence (speakable). |
| HowTo builder | Reject | HowTo rich results removed (2023). |
| Google Indexing API for ordinary pages | Reject | Officially limited to JobPosting and BroadcastEvent-in-VideoObject, with a default quota of 200 publish requests/day (quickstart, quota). It also needs a service-account credential, which a zero-dep CLI should not manage. At most, a docs mention for job boards. |
| Blocking Google-Extended to "stay out of AI Overviews" | Reject as advice | It does not do that (see item 2). |
| Accept-header negotiation inside library code | Reject | It reads request headers, which breaks ADR 0006 and the static shell. Use a proxy recipe only. |
| AI-generated FAQ blocks / "answer-first" content rewriting | Reject | Content generation is out of scope; it also risks scaled-content spam policies. |
| Pay-per-crawl integration | Reject (out of scope) | CDN-level feature (Cloudflare), not something a framework library can enforce. |
| Treating robots.txt as enforcement against Bytespider etc. | Reject the claim | Compliance is voluntary. Keep the existing "not a guarantee" note and point to WAF/bot management. |
| Headless rendering to "see what AI sees" | Reject | It would be wrong anyway: most AI crawlers do not render JS, so the raw server HTML is what they see. That is a point in favour of the existing ADR 0008. |
| ID | Severity | Since | Reports |
|---|---|---|---|
robots-blocks-ai-search |
warn | v0.2 | robots.txt disallows OAI-SearchBot / Claude-SearchBot / PerplexityBot (or * without an allow) while ai.search is allow or unset: the site will not be cited |
robots-ai-inconsistent |
info | v0.2 | Training bots allowed but search bots blocked (usually the inverse of intent) |
robots-ai-list-stale |
info | v0.2 | Bundled AI-UA table older than 180 days (prompts an upgrade) |
content-js-only |
warn | v0.2 | Indexable page whose server HTML has under N words of body text (default 50) |
content-bot-parity |
warn | v0.2 | Bot-UA and browser-UA HTML differ materially in title/meta/body text |
metadata-in-body |
warn | v0.2 |
<title>/canonical/description streamed outside <head> for a crawler UA |
link-broken |
error | v0.2 | Same-origin link returns 4xx/5xx |
link-redirect |
info | v0.2 | Internal link hits a redirect |
http-status-indexable |
error | v0.2 | Page in a sitemap or linked internally returns a non-200 status |
viewport-missing |
warn | v0.2 | No meta name=viewport
|
anchor-not-crawlable |
warn | v0.2 |
<a> without href or with javascript:
|
h1-missing / h1-multiple
|
info | v0.4 | Heading structure |
heading-order |
info | v0.4 | Skipped heading levels |
img-alt-missing |
info | v0.4 |
<img> without alt in main content |
lastmod-future |
error | v0.3 | Sitemap lastmod in the future |
lastmod-uniform |
warn | v0.3 | ≥ 90% of sitemap entries have the same lastmod (build-time stamping) |
lastmod-mismatch |
warn | v0.4 | Sitemap lastmod ≠ JSON-LD dateModified / article:modified_time (beyond 1 day) |
jsonld-date-missing |
info | v0.4 | Article without dateModified
|
jsonld-author |
warn | v0.4 | Article author missing, or a string instead of Person/Organization with name (+ url) |
jsonld-visible-mismatch |
warn | v0.4 | JSON-LD headline/name absent from visible h1/title; Product price absent from text |
jsonld-entity-id |
info | v0.4 | Organization/Person without a stable @id, or different @ids across pages |
sameas-inconsistent |
warn | v0.4 | Organization sameAs differs between pages, or a sameAs URL is non-200 |
jsonld-deprecated-type |
info | v0.4 | HowTo present; FAQPage on a non-gov/health site (no rich result expected) |
md-alternate-broken |
error | v0.5 |
rel=alternate type=text/markdown target non-200 or wrong content-type |
llms-txt-invalid |
warn | v0.5 |
/llms.txt present but not in the spec shape (H1, links) or links non-200 (only checked if present; absence is never reported) |
indexnow-key-missing |
warn | v0.5 | IndexNow configured but key file not served |
- Add one sentence to the positioning line: "…and makes your site legible to AI search: purpose-split AI crawler rules, an entity graph, and checks that what bots see without JavaScript is what users see." This stays evidence-based, with no "rank in ChatGPT" claim.
- Rename the README comparison to include Nuxt SEO and @astrojs/sitemap. Leaving out the best-in-class benchmark (Nuxt SEO, roughly 124K/wk for the meta-module and 306K/wk for its sitemap module) makes the table look selective. State plainly: "next-herald aims at Nuxt SEO parity for Next.js."
- Lead with the checker as a headless-free Lighthouse-SEO alternative for CI (lighthouse 5.4M/wk, unlighthouse 41K/wk, both needing Chrome).
- Add an "AEO stance" section to research.md: what works (crawlable server HTML, search-bot access, accurate dates, entity markup, Bing/IndexNow for ChatGPT/Copilot), what is unproven (llms.txt, markdown alternates), and what is hype (speakable, GEO scores).
- Fix the Google AI framing: AI Overviews/AI Mode = Googlebot + normal Search eligibility; Google-Extended = Gemini training/grounding only.
- Keep "no ranking claims". Extend it explicitly to "no AI-citation claims".
- The Vercel AI-crawler JS finding comes from a Dec 2024 study. Gemini (via Googlebot) and Applebot render JS. Re-test before quoting it in docs.
- I could not verify a working URL for Bing Webmaster Tools' AI Performance report (two candidate URLs returned 404). rankmath.com/features/ also returned 404.
- Items marked (recall) need a fresh fetch before they go into the repo.
Every claim the review marked (recall) or unverified was checked against a live source before it went into the docs.
| Claim | Result | Source |
|---|---|---|
| Google: no special AI files or markup needed | Confirmed. Exact wording: "You don't need to create new machine readable files, AI text files, or markup to appear in these features. There's also no special schema.org structured data that you need to add." | Google: AI features and your website |
| Google-Extended does not cover AI Overviews/AI Mode |
Confirmed. "robots.txt directives for Googlebot is the control"; limit what is shown with nosnippet, data-nosnippet, max-snippet, noindex; Google-Extended limits "AI training and grounding in some of Google's other systems". |
same page; Google common crawlers |
| Mueller: no AI system uses llms.txt; compared to keywords meta tag | Confirmed, with softer wording. His words: "AFAIK none of the AI services have said they're using LLMs.TXT … it's comparable to the keywords meta tag". Docs quote this, not "no AI system uses it". Reported by Search Engine Journal (secondary source; the original was a Reddit reply). | SEJ, SEJ follow-up |
| Nuxt SEO supports content signals |
Confirmed. Nuxt Robots supports Content-Signal and IETF Content-Usage lines in robots.txt groups. |
Nuxt Robots: AI directives |
| Yoast AI-crawler toggles | Confirmed (Premium). Yoast SEO Premium "Bot Blocker" toggles for GPTBot, CCBot and Google-Extended. | Yoast Bot Blocker |
| Rank Math AI-crawler toggle | Dropped. No first-party Rank Math page found; not claimed in the docs. | — |
| Yoast / Rank Math IndexNow | Corrected. Rank Math: IndexNow in the free plugin (Instant Indexing module). Yoast: IndexNow in Premium. | Rank Math IndexNow, Yoast IndexNow |
| Nuxt has no IndexNow | Not verified; dropped from the docs comparison. | — |
Claude-User agent name |
Confirmed, alongside ClaudeBot and Claude-SearchBot. | Anthropic crawlers |
| Bing Webmaster Tools AI Performance report | Confirmed. Public preview since 10 Feb 2026; citations across Copilot, Bing AI summaries and select partners; not clicks or rankings; not in the API yet. | Bing blog, Bing help |
rankmath.com/features/ |
Still 404; not cited. | — |
| "Bing grounds ChatGPT search" (item 6) | Not verified from a first-party source; dropped. The docs say IndexNow feeds Bing and other IndexNow engines, and that Bing's AI Performance report covers Copilot. | — |
| Vercel: major AI crawlers don't render JS | Confirmed as published ("none of the major AI crawlers currently render JavaScript"), dated Dec 2024; docs carry the date and a re-test note. | Vercel |
| OpenAI: OAI-SearchBot opt-out removes ChatGPT search answers | Confirmed ("will not be shown in ChatGPT search answers, though can still appear as navigational links"). | OpenAI crawlers |
utm_source=chatgpt.com on ChatGPT links (item 13) |
Not verified on a first-party page (OpenAI help article did not load); docs call it "widely reported" with no link. | — |
Not scheduled: item 9 (Content-Signal passthrough) was not part of the approved list. It is recorded under Considered in AEO stance.
Guides
- Getting Started
- Configuration
- Metadata
- JSON LD
- Sitemaps
- Robots
- hreflang and i18n
- OG Images
- CLI and Check Rules
- Cache Components and PPR
- Static Export
- Testing
- AEO Stance
- SEO Preview Panel
- Redirects
- Pagination and Facets
- Image SEO
- Check Diff Mode
- Migration from next seo
- Migration from next sitemap
Reference