next-herald
Typed metadata, safe JSON-LD, sitemaps with hreflang, OG templates and an SEO CI check for the Next.js 16 App Router. Built on native Next APIs, zero dependencies beyond next and react.
[!WARNING] next-herald is pre-alpha and not published to npm. Everything below describes the planned API. No library code exists yet; the design is open for feedback in issues and the ADRs.
Why
The App Router moved SEO into Next itself (generateMetadata, sitemap.ts, robots.ts, opengraph-image). The popular packages come from the Pages Router era:
- next-sitemap (~639K downloads/week) writes files to
public/in a postbuild step. Its last release was 4.2.3 in September 2023, and on Next 16 + Vercel those files are not deployed (#1048). - next-seo (~473K/week) is maintained (7.3.0, July 2026). In the App Router it now recommends native
generateMetadatafor meta tags and focuses on JSON-LD components. - Native Next.js covers the basics but has no sitemap index, no news sitemaps, shallow metadata merging, a JSON-LD guide that warns
JSON.stringifyis not XSS-safe, and no way to check a build. Metadata bugs keep appearing (e.g. vercel/next.js#98684, #97472, #98126).
Details and sources: Research.
Positioning
next-herald is a Next.js 16 App Router–only SEO kit that returns native
Metadata,MetadataRoute.Sitemap/RobotsandImageResponseobjects, fills the gaps Next leaves (sitemap index, news sitemaps, deep merging, hreflang, XSS-safe JSON-LD), and shipsherald check, a dependency-free CI crawler for technical SEO mistakes: a headless-free alternative to Lighthouse SEO audits in CI. It also keeps 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. It aims at Nuxt SEO parity for Next.js. It is pre-alpha. It makes no ranking or AI-citation claims and no performance claims; bundle sizes will be published with reproducible scripts.
AEO stance (evidence-based)
"Answer engine optimization" is mostly marketing. next-herald only builds what engines document (AEO stance, ADR 0012):
- Does: server-HTML visibility checks (most AI crawlers don't run JS); AI crawlers split into training / search / user-fetch, with search allowed by default, because OpenAI says sites that block OAI-SearchBot "will not be shown in ChatGPT search answers"; entity graph with stable
@ids; accuratelastmod; IndexNow. - Corrects a common mistake: Google-Extended does not opt you out of AI Overviews or AI Mode; those use Googlebot. Google: "You don't need to create new machine readable files, AI text files, or markup to appear in these features" (source).
- Experimental opt-ins (v0.5): llms.txt / llms-full.txt and markdown alternates. No proven visibility benefit; off by default.
- Won't do: GEO scores,
speakable, HowTo, FAQ rich-result promises, or any claim that it gets you ranked or cited.
Design principles
- Native first. Return objects Next renders; never render our own
<head>tags. (ADR 0001) - Zero dependencies. Only the
nextandreactpeers; the CLI uses Node built-ins. Enforced in CI. (ADR 0002) - Static by default. Never read headers or cookies, so pages stay in the Cache Components static shell. (ADR 0006)
- Safe by default. JSON-LD is escaped; previews are
noindexunless built for production. (ADR 0004) - Check what crawlers see.
herald checkreads served HTML, not Next internals. (ADR 0008)
Non-goals (v1): Pages Router, Next.js ≤ 15, rendering <head> tags, headless-browser crawling, content/keyword scoring, a hosted service.
Features (planned)
- Typed metadata builders —
createMetadata(site defaults, title template,metadataBase),pageMetadata(canonical, OG/Twitter filled from title/description),mergeMetadata(deep merge, since Next merges per top-level key). (Metadata) - Typed JSON-LD — builders for common schema.org types with Google-required fields enforced by TypeScript, a
<JsonLd>Server Component with XSS-safe serialization, and an entity graph by default (Organization/Person with stable@id,sameAs,authorby@id); accepts schema-dts values. No rich-result promises: FAQ rich results are limited to government/health sites, HowTo is gone. (JSON-LD) - Sitemaps from your
app/routes —herald routesscansapp/,defineSitemaprequires a typedparamsenumerator for every dynamic route; image, video and language alternates. (Sitemaps) - Large sites — split at 50,000 URLs / 50 MB with a generated sitemap index; Google News sitemaps. (ADR 0007)
- Robots —
robots.tswith automatic sitemap lines, preview lock-down and AI-crawler rules by purpose (training / search / user-fetch; search allowed by default). (Robots) - hreflang — reciprocal alternates and
x-defaultfrom oneI18nSource; optional next-tongue adapter, recipes for next-intl and Paraglide. (hreflang & i18n) - OG image templates — typed JSX templates on
next/ogImageResponse, no bundled fonts. (OG images) herald check— crawls a running server or a static export for missing/duplicate titles and descriptions, broken canonicals, hreflang reciprocity,noindexleaks, sitemap/robots consistency, structured data validity and OG image size, plus no-JS content visibility, bot/browser parity, broken internal links, Lighthouse-equivalent basics, accuratelastmodand AI-crawler robots mistakes;pretty,json, GitHub annotations and SARIF output. (CLI & rules)- SEO preview panel (dev only, v0.3) — overlay in
next devwith the Google result snippet, OG/X cards and a chat link preview, plus liveherald checkwarnings; stripped from production builds. (Preview panel) - Redirect manager (v0.3) — typed redirect rules for
next.configredirectsor yourproxy.ts, with checks for redirect chains, loops and temporary redirects where permanent ones are intended. (Redirects) - Pagination and faceted URLs (v0.3) — canonical and
noindexhelpers for filtered, sorted and paginated pages, plus a check for search-param duplicate content. (Pagination) - Image SEO (v0.4) — image sitemap entries from
next/imageusage and route data; checks for missing alt, missing dimensions and legacy formats. (Image SEO) herald check --diff(v0.4) — compares SEO output with a base snapshot or branch and reports only changes (newnoindex, title/canonical changes, removed sitemap URLs) as markdown or SARIF for PR comments. (Diff mode)herald indexnow— CLI that submits changed sitemap URLs to IndexNow (Bing, Yandex and others; not Google). (CLI)- Experimental (opt-in, v0.5) — static markdown alternate routes and llms.txt / llms-full.txt. No proven visibility benefit. (AEO stance)
- Cache Components and static export safe — pure builders; works with
cacheComponents: trueandoutput: 'export'. (Cache Components, Static export)
Quick start (planned API)
npm install next-herald # not yet published
npx herald init
// herald.config.ts
import { defineHerald } from 'next-herald/config';
export default defineHerald({
siteUrl: 'https://example.com',
siteName: 'Example',
title: { default: 'Example', template: '%s · Example' },
description: 'What Example does, in one sentence.',
});
// app/layout.tsx (excerpt)
import { createMetadata } from 'next-herald/metadata';
import herald from '@/herald.config';
export const metadata = createMetadata(herald);
// app/blog/[slug]/page.tsx
import { pageMetadata } from 'next-herald/metadata';
import { JsonLd, jsonLd } from 'next-herald/jsonld';
import herald from '@/herald.config';
import { getPost } from '@/lib/posts';
export async function generateMetadata({ params }: PageProps<'/blog/[slug]'>) {
const { slug } = await params;
const post = await getPost(slug);
return pageMetadata(herald, { path: `/blog/${slug}`, title: post.title, description: post.excerpt, type: 'article' });
}
export default async function Page({ params }: PageProps<'/blog/[slug]'>) {
const { slug } = await params;
const post = await getPost(slug);
return (
<article>
<JsonLd data={jsonLd.article({ headline: post.title, datePublished: post.date, author: { name: post.author } })} />
<h1>{post.title}</h1>
</article>
);
}
// app/sitemap.ts
import { defineSitemap } from 'next-herald/sitemap';
import { routes } from '@/herald-routes.gen'; // written by `herald routes` (v0.2); v0.1: a hand-written list
import herald from '@/herald.config';
import { listSlugs } from '@/lib/posts';
export default defineSitemap(herald, {
routes,
params: { '/blog/[slug]': async () => (await listSlugs()).map((slug) => ({ slug })) }, // missing key = type error
});
// app/robots.ts
import { defineRobots } from 'next-herald/robots';
import herald from '@/herald.config';
export default defineRobots(herald, { rules: [{ userAgent: '*', disallow: ['/admin'] }] });
npx herald check --build --format github # CI
Full walkthrough: Getting started.
Comparison
Feature-level comparison as of October 2026. next-herald's column is planned, not shipped. Weekly downloads from api.npmjs.org (2026-10-06 → 10-12). Nuxt SEO is the best-in-class benchmark; next-herald aims at Nuxt SEO parity for Next.js.
| next-herald (planned) | Native Next.js 16 | next-seo 7.3 | next-sitemap 4.2.3 | schema-dts 2.1 | Nuxt SEO 6.0 | Astro (@astrojs/sitemap 3.7) | |
|---|---|---|---|---|---|---|---|
| Status | pre-alpha | stable | active (Jul 2026) | last release Sep 2023 | active (Oct 2026) | active | active |
| Downloads/week | — | — | 472,622 | 638,748 | 3,138,833 | @nuxtjs/seo 124,014; sitemap 306,316 | 3,814,836 |
| Runtime dependencies | 0 (peers next, react) | — | 0 | 4 | 1 (schema-dts-lib) | > 0 | > 0 |
| Meta tags | builds native Metadata | metadata / generateMetadata | recommends native in App Router; <NextSeo> for Pages | — | — | useSeoMeta + site config | community (astro-seo) |
| Deep merge across segments | yes | no (per top-level key) | — | — | — | yes | — |
| JSON-LD | typed builders + XSS-safe <JsonLd> | guide only | components | — | types only | @graph with @id linking | manual |
Entity graph (@id, sameAs) | yes, by default (v0.2) | — | — | — | — | yes | — |
| Sitemap | native sitemap.ts from app/ routes | sitemap.ts, generateSitemaps | — | postbuild files in public/ | — | yes | yes |
| Sitemap index > 50k | yes (route handler) | split only, no index | — | yes | — | yes | yes |
| News sitemap | yes | no | — | no | — | yes | — |
Accurate lastmod source | content dates, --git-lastmod, checks | manual | — | build time | — | yes | option |
| hreflang alternates | from one I18nSource | manual | languageAlternates (Pages) | alternateRefs | — | via i18n | i18n option |
| AI crawlers by purpose | training / search / user-fetch | manual | — | — | — | robots groups + Content-Signal | — |
| llms.txt | experimental opt-in (v0.5) | — | — | — | — | nuxt-llms | third party |
| OG image templates | yes (next/og) | ImageResponse | — | — | — | nuxt-og-image | third party |
| Broken internal links | link-broken | — | — | — | — | link checker | — |
| CI checker | herald check (no browser) | — | — | — | — | partial (link checker, devtools) | — |
| Pages Router | no | — | yes | yes | n/a | n/a | n/a |
Sources in Research and the SEO/AEO review. Corrections welcome — open an issue if any cell is wrong.
Documentation
Also available as a GitHub wiki (same content as docs/).
Roadmap
| Milestone | Theme |
|---|---|
| v0.1 | Core builders, ship fast: config, metadata builders, <JsonLd> + 7 types, basic sitemap/robots, CI with dependency budget |
| v0.2 | herald check core (titles, descriptions, canonicals, noindex, sitemap/robots, no-JS visibility, links, Lighthouse-equivalent basics, AI-crawler robots) + herald routes + entity graph |
| v0.3 | Large sites & i18n: sitemap index, news, hreflang, next-tongue adapter, static export, accurate lastmod |
| v0.4 | OG templates, JSON-LD, OG, citation-friendly structure and entity rules, more schema types, SARIF |
| v0.5 | herald init, herald indexnow, verified migration guides, published size/perf report, AI referral docs; experimental markdown alternates and llms.txt |
| v0.9 | Beta: API and rule-ID freeze |
| v1.0 | Stable, semver guarantees |
| post-1.0 | next-seo / next-sitemap codemods |
Scope freeze: no new features before v0.1 ships. The approved SEO/AEO additions (2026-10-14) land in v0.2+; in v0.1 only the defineRobots AI option shape changes (#11).
Full detail with acceptance criteria: ROADMAP.md · Milestones · Project board.
Contributing
Design feedback is the most valuable contribution right now. Read CONTRIBUTING.md or comment on an ADR. Please follow the Code of Conduct; report vulnerabilities per SECURITY.md.
Support
If this project is useful to you, you can support its development with a donation: Buy me a coffee. It helps keep these tools maintained, documented and free for everyone.
License
MIT © Louis Wright (Louis Wright)