@signalor/nextjs
v0.2.0
Published
Signalor SDK for Next.js — auto-injects schema markup, serves llms.txt + sitemap, and reports deploys to Signalor for GEO scoring.
Maintainers
Readme
@signalor/nextjs
Signalor SDK for Next.js. Auto-injects schema markup, serves /llms.txt and /sitemap.xml, and reports each deploy to Signalor so your GEO score stays current — usually four lines of code per app.
Install
pnpm add @signalor/nextjsThen set your API key (mint one at signalor.ai → Settings → Developers):
# .env
SIGNALOR_API_KEY=sk_live_...That's it. Now wire up whichever pieces you want.
Middleware — AI-crawler detection (edge runtime)
// On Next.js 16+: src/proxy.ts (the new name for middleware)
// On Next.js 14/15: src/middleware.ts
import { signalorMiddleware } from "@signalor/nextjs/middleware";
export default signalorMiddleware();
export const config = {
matcher: ["/((?!_next/static|_next/image|favicon.ico).*)"],
};Stamps x-signalor-ai-crawler: <bot-name> on responses to known AI crawlers (GPTBot, ClaudeBot, PerplexityBot, Google-Extended, Bytespider, etc.). Pass options to record analytics or swap behaviour:
export default signalorMiddleware({
onDetected: (info) => analytics.track("ai_crawler", info),
headerName: "x-ai-bot", // or `false` to disable
});Next.js 16 note: the
middlewarefile convention was renamed toproxy. The CLI auto-detects your version and writes the right filename. If you're hand-wiring on 16+, usesrc/proxy.ts.The
import + export default signalorMiddleware()form is intentional:signalorMiddlewareis a factory that returns the actual handler. Re-export shortcuts likeexport { signalorMiddleware as default }confuse Next.js 16's static analyzer — use the explicit form above.
Schema markup — <SignalorSchema />
// app/layout.tsx
import { SignalorSchema } from "@signalor/nextjs";
export default function RootLayout({ children }) {
return (
<html>
<head>
<SignalorSchema />
</head>
<body>{children}</body>
</html>
);
}Server component. Fetches your org's Organization + WebSite defaults from Signalor (cached 24h) and emits a single <script type="application/ld+json">. Pass additional to merge per-page schema:
<SignalorSchema additional={[{ "@type": "Article", headline: post.title, datePublished: post.date }]} />/llms.txt route
// app/llms.txt/route.ts
export { GET } from "@signalor/nextjs/llms-txt";Renders an llms.txt populated from your Signalor org profile. Cached for 1 hour with Cache-Control headers. Fails open with a minimal stub if Signalor is unreachable, so the route never 5xx's.
Sitemap
// app/sitemap.ts
export { default } from "@signalor/nextjs/sitemap";Reads routes from SIGNALOR_SITEMAP_ROUTES env (comma-separated paths) and prefixes them with your org's primary URL. To compose with your own route discovery:
import { signalorSitemap } from "@signalor/nextjs/sitemap";
export default async function sitemap() {
return signalorSitemap({ routes: await loadRoutesFromCms() });
}Snapshot route — score sites behind Cloudflare / Turnstile
// app/api/signalor/snapshot/route.ts
export { GET } from "@signalor/nextjs/snapshot";If your site sits behind a Cloudflare managed challenge / Turnstile (or any bot
protection), Signalor's crawler is served a "Just a moment…" interstitial instead
of your content, and your GEO score collapses. This route lets the analyzer read
your site's own server-rendered HTML — fetched from your deployment origin
(the *.vercel.app host, which is not behind your Cloudflare) — so scoring works
regardless of your edge protection.
Every request is HMAC-signed with your API key and time-bounded, so the route
never exposes your content publicly (unsigned/stale requests get a 401). The
analyzer pulls / plus the routes in SIGNALOR_SITEMAP_ROUTES.
On Vercel it works with zero extra config (uses VERCEL_URL). On other hosts, set
SIGNALOR_ORIGIN to an origin the route can self-fetch that isn't behind your CDN.
Deploy notifier
// package.json
{
"scripts": {
"postbuild": "signalor-deploy"
}
}Runs after every build. Auto-detects the deploy context — commit, environment, URL — from Vercel (VERCEL_*), Netlify (NETLIFY, CONTEXT, URL), or your own env vars (SIGNALOR_DEPLOY_URL, SIGNALOR_DEPLOY_COMMIT, SIGNALOR_DEPLOY_ENV). Reports to Signalor, which kicks off a fresh GEO analysis and surfaces it in the dashboard's Deployments tab.
The script never fails your build. Missing key → warns, exits 0. Network error → warns, exits 0.
Configuration
| Env var | Default | Used by |
| ----------------------------- | --------------------------- | ---------------------------------------- |
| SIGNALOR_API_KEY | (required) | every server-side primitive |
| SIGNALOR_API_BASE | https://api.signalor.ai | every server-side primitive |
| SIGNALOR_APP_BASE | https://signalor.ai | dashboard links |
| SIGNALOR_SITEMAP_ROUTES | / | sitemap + snapshot routes (comma-separated) |
| SIGNALOR_ORIGIN | https://$VERCEL_URL | snapshot route self-fetch origin |
| SIGNALOR_DEPLOY_URL | (detect) | signalor-deploy on self-hosted |
| SIGNALOR_DEPLOY_COMMIT | (detect) | signalor-deploy on self-hosted |
| SIGNALOR_DEPLOY_ENV | production | signalor-deploy on self-hosted |
| SIGNALOR_DEPLOY_HOST | self-hosted | signalor-deploy on self-hosted |
Runtime
- Edge-safe:
@signalor/nextjs/middlewareruns in the edge runtime. No Node-only APIs. - Server components:
<SignalorSchema />, the llms.txt route, the sitemap, and the snapshot route run in the Node runtime — they use nativefetch, which Next.js caches automatically. - Zero runtime deps: only peer-deps
nextandreact.
Roadmap
signalor-cli init— automated wire-up vianpx.- Vercel marketplace listing — one-click install + provisioned env vars.
- Per-route schema overrides via Next.js metadata API.
- Bulk metadata push from the build step (currently the SDK uses the
/metadata/bulkendpoint but doesn't auto-collect — coming in 0.2).
License
MIT
