Skip to content

JSON-LD Recipes

aeo.js auto-generates WebSite, Organization, and WebPage schemas when schema.enabled: true. For richer page-type-specific schemas, drop these into your page templates.

JSON.stringify(...) does not escape </script>. A schema value containing </script> (or U+2028 / U+2029) breaks out of the script block and executes as JavaScript. Always run JSON-LD payloads through this helper before injection:

lib/serialize-json-ld.ts
export function serializeJsonForHtml(value: unknown): string {
return JSON.stringify(value)
.replace(/</g, '\\u003C')
.replace(/>/g, '\\u003E')
.replace(/&/g, '\\u0026')
.replace(/\u2028/g, '\\u2028')
.replace(/\u2029/g, '\\u2029');
}
const faqSchema = {
'@context': 'https://schema.org',
'@type': 'FAQPage',
mainEntity: [
{
'@type': 'Question',
name: 'What is Answer Engine Optimization?',
acceptedAnswer: {
'@type': 'Answer',
text: 'AEO is the practice of making your content discoverable and citable by AI-powered answer engines like ChatGPT, Claude, and Perplexity.',
},
},
],
};

aeo.js auto-detects FAQ patterns (heading ending with ? + answer paragraph) and emits this schema for you when schema.enabled: true.

const howToSchema = {
'@context': 'https://schema.org',
'@type': 'HowTo',
name: 'How to deploy a Next.js site to Vercel',
totalTime: 'PT5M',
step: [
{ '@type': 'HowToStep', position: 1, name: 'Install CLI', text: 'Run `npm install -g vercel`.' },
{ '@type': 'HowToStep', position: 2, name: 'Login', text: 'Run `vercel login`.' },
{ '@type': 'HowToStep', position: 3, name: 'Deploy', text: 'Run `vercel --prod`.' },
],
};

aeo.js auto-detects Step 1: / Step 2: heading patterns.

const articleSchema = {
'@context': 'https://schema.org',
'@type': 'BlogPosting',
headline: 'Optimizing your site for AI search engines in 2026',
image: 'https://mysite.com/og/article-cover.png',
datePublished: '2026-05-14T10:00:00Z',
dateModified: '2026-05-14T10:00:00Z',
author: { '@type': 'Person', name: 'Jane Author' },
publisher: {
'@type': 'Organization',
name: 'My Site',
logo: { '@type': 'ImageObject', url: 'https://mysite.com/logo.png' },
},
};
const productSchema = {
'@context': 'https://schema.org',
'@type': 'Product',
name: 'Acme Espresso Machine',
description: 'A semi-automatic espresso machine with built-in grinder.',
image: ['https://mysite.com/products/espresso/cover.jpg'],
sku: 'ACM-ESP-001',
brand: { '@type': 'Brand', name: 'Acme' },
offers: {
'@type': 'Offer',
url: 'https://mysite.com/products/espresso',
priceCurrency: 'USD',
price: '899.00',
availability: 'https://schema.org/InStock',
},
aggregateRating: {
'@type': 'AggregateRating',
ratingValue: '4.7',
reviewCount: '142',
},
};
const breadcrumbSchema = {
'@context': 'https://schema.org',
'@type': 'BreadcrumbList',
itemListElement: [
{ '@type': 'ListItem', position: 1, name: 'Home', item: 'https://mysite.com/' },
{ '@type': 'ListItem', position: 2, name: 'Blog', item: 'https://mysite.com/blog' },
{ '@type': 'ListItem', position: 3, name: 'Article Title' },
],
};
import { serializeJsonForHtml } from '@/lib/serialize-json-ld';
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: serializeJsonForHtml(faqSchema) }}
/>

After deploying, paste your URL into one of these: