OntoProvider
The client-side React provider — injects <link rel="alternate"> tags so AI clients can discover your Markdown variant, and auto-generates JSON-LD structured data based on per-route page types.
What it does
OntoProvider mounts inside your App Router root layout and emits two things on every page:
| What it emits | Where it goes | Why it matters |
|---|---|---|
| Document <head> | Standard discovery surface for AI clients that honor content-negotiation alternates (Anthropic and OpenAI both look here). |
| Document <head> | JSON-LD structured data scoped to the active route's pageType (scoring → Methodology, about → Organization, etc). |
Both injections happen on every navigation because the provider readsusePathname() — it's a true client component, so it tracks route changes without re-mounting the document.
Setup
Add the provider to your root layout, wrapping the entire app. The provider lives at @ontosdk/next/provider.
import { OntoProvider } from '@ontosdk/next/provider';
import ontoConfig from '../onto.config';
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<OntoProvider baseUrl="https://example.com" config={ontoConfig}>
<html lang="en">
<body>{children}</body>
</html>
</OntoProvider>
);
}<html>, not its children. The provider needs to be above the <head> so React reconciles its <link> / <script> emissions into the document head. Placing it inside <body> will still render but the metadata won't appear in the head where crawlers look.Props
| Prop | Type | Required | Description |
|---|---|---|---|
baseUrl | string | Yes | Absolute origin of your site (e.g. https://example.com). The provider uses this to construct the full alternate URL: baseUrl + pathname + ?format=md. Trailing slashes are stripped. |
config | OntoConfig | No | The same config object exported from onto.config.ts. When provided, the provider matches the current pathname against config.routes and emits JSON-LD for the matched route's pageType. Omit if you only want the alternate link. |
children | ReactNode | Yes | Your app tree, including <html> and <body>. |
Page types & schemas
When config is provided and the current pathname matches a config.routes[] entry, the provider emits a JSON-LD schema scoped to the route's pageType:
| pageType | JSON-LD schema emitted | When to use |
|---|---|---|
'scoring' | Methodology + Article (AIO scoring weights, criteria, scale) | Pages that publish a scoring formula, ranking method, or quantitative framework you want AI to cite accurately. |
'about' | Organization + AboutPage (pulls from config.organization) | Your /about page or any page describing your company. Requires config.organization to be filled in. |
'default' | None (only the alternate link emits) | Everything else — landing pages, blog posts, docs. |
Routes not listed in config.routes implicitly get 'default'. Add specific entries only for the routes whose pageType matters for AI grounding.
Relationship to onto.config
OntoProvider and onto.config.ts are designed to ship together. The config is the static source of truth (route map, organization details, sections for llms.txt); the provider is the runtime that reads it on each navigation and emits the right tags.
You can use the provider without a config — pass only baseUrl and you'll still get the alternate-link discovery surface. You can use the config without the provider too — the build CLI consumes it to generate llms.txt. But the JSON-LD injection specifically requires both.
See /sdk/configuration for the config-file reference.
Verify it works
After deploying:
| Step | Command | Expected |
|---|---|---|
| 1 | curl -s https://yourdomain.com | grep alternate | One line with rel="alternate" type="text/markdown" pointing at ?format=md |
| 2 | curl -s https://yourdomain.com | grep -A2 application/ld+json | A <script type="application/ld+json"> block if the route is in config.routes with a non-default pageType |
| 3 | Browser DevTools → Elements → search for application/ld+json | Schema renders with the expected @type (Methodology, Organization, etc) |
insights.json_ld_present from false to true, worth +20 points on the score.Gotchas
| Gotcha | Fix |
|---|---|
| Provider rendered but link/script don't appear in <head> | You probably wrapped <body> children instead of <html>. Move it up one level. |
| JSON-LD never appears | Confirm config is passed AND the current pathname matches a config.routes[].path entry exactly (case-sensitive, no trailing slash). Routes are not pattern-matched today. |
| Stale schemas after route change | The provider re-runs on every pathname change via usePathname(). If schemas don't refresh, you're probably caching at the CDN — add Cache-Control: no-store on the HTML response or use a per-route cache key. |
| TypeScript: "cannot find module '@ontosdk/next/provider'" | Update to @ontosdk/next ≥1.6.0. The /provider subpath export was added in 1.6.0. |