World
# aeo.js — Answer Engine Optimization for the Modern Web - Complete Documentation
This file contains all documentation concatenated into a single file for easy consumption by LLMs.
> Make your website discoverable by ChatGPT, Claude, Perplexity & AI search engines. Auto-generates llms.txt, robots.txt, sitemap, JSON-LD structured data & more.
## Table of Contents
This document includes all content from this project.
Each section is separated by a horizontal rule (---) for easy parsing.
---
# Contributors
URL: https://aeojs.org/contributors
> The people who make aeo.js possible.
## Contributors
6 contributors have shaped aeo.js. Thank you.
- [rubenmarcus 80 commits](https://github.com/rubenmarcus)
- [KimHyeongRae0 5 commits](https://github.com/KimHyeongRae0)
- [FabioRocha231 3 commits](https://github.com/FabioRocha231)
- [ribeiroevandro 2 commits](https://github.com/ribeiroevandro)
- [HusseinAdeiza 1 commit](https://github.com/HusseinAdeiza)
- [mhanelia 1 commit](https://github.com/mhanelia)
Want to join them?
[Open a PR](https://github.com/multivmlabs/aeo.js)
.
Built by [rubenmarcus](https://github.com/rubenmarcus) & [multivmlabs](https://github.com/multivmlabs)
---
# Audit & Citability
URL: https://aeojs.org/features/audit
> Audit your AEO setup and measure your site
## Audit & Citability
## Site Audit
[Section titled “Site Audit”](#site-audit)
The auditSite function checks your site for AEO best practices and returns a detailed report:
-
import { auditSite, formatAuditReport, getGrade, resolveConfig } from 'aeo.js';
const config = resolveConfig({
title: 'My Site',
url: 'https://mysite.com',
pages: [{ pathname: '/', title: 'Home', content: 'Welcome to my site.' }],
});
const result = auditSite(config);
console.log(formatAuditReport(result));
console.log('Grade:', getGrade(result.score));
The audit checks for:
Presence of robots.txt, llms.txt, llms-full.txt
- Sitemap accessibility
- Structured data (JSON-LD)
- Open Graph meta tags
- AI crawler accessibility
You can also run it from the CLI:
Terminal window
npx aeo.js check
## Citability Score
[Section titled “Citability Score”](#citability-score)
Measure how likely AI engines are to cite your content:
import { formatPageCitability, resolveConfig, scorePageCitability, scoreSiteCitability } from 'aeo.js';
// Score a single page
const pageScore = scorePageCitability({
pathname: '/',
title: 'Home',
content: '# Home\n\nMy site publishes practical guides for AI-ready content.',
});
console.log(formatPageCitability(pageScore));
// Score the whole site
const config = resolveConfig({
title: 'My Site',
url: 'https://mysite.com',
pages: [{ pathname: '/', title: 'Home', content: 'Welcome to my site.' }],
});
const siteScore = scoreSiteCitability(config);
The citability score evaluates:
- Content structure and headings
- Factual density and specificity
- Source attribution
- Unique data and statistics
- Clear definitions and explanations
## Reports
[Section titled “Reports”](#reports)
Generate a comprehensive AEO report:
import { formatReportJson, formatReportMarkdown, generateReport, resolveConfig } from 'aeo.js';
const config = resolveConfig({
title: 'My Site',
url: 'https://mysite.com',
pages: [{ pathname: '/', title: 'Home', content: 'Welcome to my site.' }],
});
const report = generateReport(config);
// Get as markdown
console.log(formatReportMarkdown(report));
// Get as JSON
console.log(formatReportJson(report));
## Platform Hints
[Section titled “Platform Hints”](#platform-hints)
Get platform-specific optimization suggestions:
import { auditSite, generatePlatformHints, resolveConfig, scoreSiteCitability } from 'aeo.js';
const config = resolveConfig({
title: 'My Site',
url: 'https://mysite.com',
pages: [{ pathname: '/', title: 'Home', content: 'Welcome to my site.' }],
});
const audit = auditSite(config);
const citability = scoreSiteCitability(config);
const hints = generatePlatformHints(audit, citability);
// Returns platform-specific optimization hints for ChatGPT, Perplexity, Google AI, and Bing Copilot
[Previous
JSON-LD Recipes](/features/json-ld/) [Next
Configuration](/reference/configuration/)
Built by [rubenmarcus](https://github.com/rubenmarcus) & [multivmlabs](https://github.com/multivmlabs)
---
# CLI
URL: https://aeojs.org/features/cli
> Run aeo.js from the command line without any framework integration.
## CLI
## Usage
[Section titled “Usage”](#usage)
Terminal window
npx aeo.js <command> [options]
** [options]">
## Commands
[Section titled “Commands”](#commands)
### generate
[Section titled “generate”](#generate)
Generate all AEO files (robots.txt, llms.txt, sitemap.xml, etc.):
Terminal window
npx aeo.js generate
npx aeo.js generate --url https://mysite.com --title "My Site" --out public
### init
[Section titled “init”](#init)
Create an aeo.config.ts configuration file in your project:
Terminal window
npx aeo.js init
This generates a starter config with all options documented.
### check
[Section titled “check”](#check)
Validate your AEO setup and get a GEO readiness score (0–100). Does not write any files — safe to run in CI:
Terminal window
npx aeo.js check # formatted output
npx aeo.js check --json # machine-readable JSON for scripting
Fail a CI build if the score drops below a threshold:
Terminal window
SCORE=$(npx aeo.js check --json | jq '.audit.score')
["$SCORE" -ge 70] || { echo "GEO score $SCORE below 70"; exit 1; }
### report
[Section titled “report”](#report)
Deeper analysis than check: per-page citability scores, platform-specific hints (ChatGPT, Claude, Perplexity, Google AI Overviews, Bing Copilot), and a prioritized fix list.
Terminal window
npx aeo.js report > aeo-report.md
npx aeo.js report --json > aeo-report.json
aeo-report.mdnpx aeo.js report --json > aeo-report.json">
## Options
[Section titled “Options”](#options)
Flag
Description
--out <dir>
Output directory (default: auto-detected)
--url <url>
Site URL
--title <title>
Site title
--no-widget
Disable widget generation
--json
JSON output (for check and report)
--help, -h
Show help
--version, -v
Show version
Both --flag value and --flag=value forms are supported.
## Configuration file
[Section titled “Configuration file”](#configuration-file)
Caution
The standalone CLI does not** currently load aeo.config.{ts,js} — it configures itself from CLI flags + defaults only. npx aeo.js init still scaffolds the file as the canonical place for your settings, but today it’s consumed by framework integrations rather than the CLI itself.
Create one with npx aeo.js init:
import { defineConfig } from 'aeo.js';
export default defineConfig({
title: 'My Site',
url: 'https://mysite.com',
description: 'A site optimized for AI discovery',
outDir: 'public',
});
Import it into your framework config so the integration picks it up:
vite.config.ts
import aeoConfig from './aeo.config';
import { aeoVitePlugin } from 'aeo.js/vite';
export default { plugins: [aeoVitePlugin(aeoConfig)] };
For raw CLI usage on a static site, pass values directly:
Terminal window
npx aeo.js generate --url https://mysite.com --title "My Site" --out public
See the full [Configuration reference](/reference/configuration/) for all options.
A complete CLI reference with every flag, exit codes, JSON output shapes, framework auto-detection table, and CI scripting patterns is in [docs/cli.md on GitHub](https://github.com/multivmlabs/aeo.js/blob/main/docs/cli.md).
[Previous
Widget](/features/widget/) [Next
Schema & Open Graph](/features/schema-og/)
Built by [rubenmarcus](https://github.com/rubenmarcus) & [multivmlabs](https://github.com/multivmlabs)
---
# Generated Files
URL: https://aeojs.org/features/generated-files
> All the files aeo.js generates and what they
## Generated Files
After building, aeo.js generates these files in your output directory:
## robots.txt
[Section titled “robots.txt”](#robotstxt)
AI-crawler-aware robots directives. Includes rules for all known AI crawlers (GPTBot, ClaudeBot, PerplexityBot, etc.) with sensible defaults.
User-agent: *
Allow: /
User-agent: GPTBot
Allow: /
User-agent: ClaudeBot
Allow: /
## llms.txt
[Section titled “llms.txt”](#llmstxt)
A concise, LLM-readable summary of your site. This is the first file AI crawlers look for — it tells them what your site is about and where to find content.
# My Site
A site optimized for AI discovery
## Pages
- [Home](https://mysite.com/)
- [About](https://mysite.com/about)
- [Blog](https://mysite.com/blog)
## llms-full.txt
[Section titled “llms-full.txt”](#llms-fulltxt)
Full concatenated content of all pages in a single file. Useful for LLMs that want to ingest your entire site at once.
## sitemap.xml
[Section titled “sitemap.xml”](#sitemapxml)
Standard XML sitemap for search engines and AI crawlers.
## docs.json
[Section titled “docs.json”](#docsjson)
A structured documentation manifest:
{
"name": "My Site",
"description": "A site optimized for AI discovery",
"baseUrl": "https://mysite.com",
"totalDocs": 5,
"docs": [{
"title": "Home",
"path": "/",
"markdownUrl": "https://mysite.com/index.md",
"htmlUrl": "https://mysite.com/",
"content": "..."
}]
}
## ai-index.json
[Section titled “ai-index.json”](#ai-indexjson)
An AI-optimized content index designed for RAG (Retrieval-Augmented Generation) pipelines:
[{
"id": "index",
"url": "https://mysite.com/",
"title": "Home",
"content": "Full page content in markdown...",
"description": "Page description",
"keywords": ["keyword1", "keyword2"]
}]
## Raw Markdown
[Section titled “Raw Markdown”](#raw-markdown)
One .md file per page, extracted from your rendered HTML:
public/
index.md # Markdown for /
about.md # Markdown for /about
blog.md # Markdown for /blog
## Toggling generators
[Section titled “Toggling generators”](#toggling-generators)
You can enable or disable individual generators:
{
generators: {
robotsTxt: true,
llmsTxt: true,
llmsFullTxt: true,
rawMarkdown: true,
manifest: true,
sitemap: true,
aiIndex: true,
schema: true,
}
}
[Previous
Vanilla JS / Static HTML](/frameworks/vanilla/) [Next
Widget](/features/widget/)
Built by [rubenmarcus](https://github.com/rubenmarcus) & [multivmlabs](https://github.com/multivmlabs)
---
# JSON-LD Recipes
URL: https://aeojs.org/features/json-ld
> Copy-paste structured-data recipes for FAQ, HowTo, Product, Article, Recipe, Event, VideoObject, and BreadcrumbList — each paired with an XSS-safe serializer.
## 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.
## The safe serializer
[Section titled “The safe serializer”](#the-safe-serializer)
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');
}
**/g, '\\u003E') .replace(/&/g, '\\u0026') .replace(/\u2028/g, '\\u2028') .replace(/\u2029/g, '\\u2029');}">
Note
aeo.js’s own auto-generated JSON-LD already uses this serializer internally. The recipes below are for custom** JSON-LD you add on top.
## FAQ Page
[Section titled “FAQ Page”](#faq-page)
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.
## HowTo
[Section titled “HowTo”](#howto)
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.
## Article / BlogPosting
[Section titled “Article / BlogPosting”](#article--blogposting)
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' },
},
};
**
Caution
Always use ISO-8601 strings for datePublished / dateModified. A raw Date object passed through a template literal renders as "Thu May 14 2026 ..." and breaks validators. Wrap with new Date(d).toISOString().
## Product
[Section titled “Product”](#product)
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',
},
};
## BreadcrumbList
[Section titled “BreadcrumbList”](#breadcrumblist)
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' },],
};
## Injecting safely — per framework
[Section titled “Injecting safely — per framework”](#injecting-safely--per-framework)
[Next.js](#tab-panel-0)
- [Astro](#tab-panel-1)
- [Nuxt](#tab-panel-2)
- [Svelte](#tab-panel-3)
import { serializeJsonForHtml } from '@/lib/serialize-json-ld';
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: serializeJsonForHtml(faqSchema) }}
/>
<script lang="ts">
import { serializeJsonForHtml } from './lib/serialize-json-ld';
const schema = serializeJsonForHtml(faqSchema);
</script>
<svelte:head>
<script type="application/ld+json">{@html schema}</script>
</svelte:head>
">
## Validation
[Section titled “Validation”](#validation)
After deploying, paste your URL into one of these:
- [Schema Markup Validator](https://validator.schema.org/) — official, quickest
- [Google Rich Results Test](https://search.google.com/test/rich-results) — Google rich-result eligibility
- [Bing Webmaster URL Inspection](https://www.bing.com/webmasters/url-inspection) — Bing/Copilot-specific feedback
Note
Full catalog (including Recipe**, **Event**, **VideoObject**, and more per-framework injection variants like React Helmet and vanilla HTML) is in [docs/json-ld.md on GitHub](https://github.com/multivmlabs/aeo.js/blob/main/docs/json-ld.md).
[Previous
Schema & Open Graph](/features/schema-og/) [Next
Audit & Citability](/features/audit/)
Built by [rubenmarcus](https://github.com/rubenmarcus) & [multivmlabs](https://github.com/multivmlabs)
---
# Schema & Open Graph
URL: https://aeojs.org/features/schema-og
> Auto-generated JSON-LD structured data and Open Graph meta tags.
## Schema & Open Graph
aeo.js can generate JSON-LD structured data and Open Graph meta tags for your pages.
## JSON-LD Schema
[Section titled “JSON-LD Schema”](#json-ld-schema)
Enable schema generation in your config:
-
{
schema: {
enabled: true,
organization: {
name: 'My Company',
url: 'https://mysite.com',
logo: 'https://mysite.com/logo.png',
sameAs: ['https://twitter.com/mycompany',
'https://github.com/mycompany',],
},
defaultType: 'WebPage', // or 'Article'
},
}
**
This generates <script type="application/ld+json"> blocks with:
Organization** schema for your site
- **WebPage** or **Article** schema for each page
- Proper @context, @type, name, description, and url fields
## Open Graph tags
[Section titled “Open Graph tags”](#open-graph-tags)
Enable OG tag generation:
{
og: {
enabled: true,
image: 'https://mysite.com/og.png',
twitterHandle: '@mycompany',
type: 'website', // or 'article'
},
}
This generates meta tags like:
<meta property="og:title" content="Page Title" />
<meta property="og:description" content="Page description" />
<meta property="og:image" content="https://mysite.com/og.png" />
<meta property="og:url" content="https://mysite.com/page" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content="@mycompany" />
">
## Programmatic API
[Section titled “Programmatic API”](#programmatic-api)
The lower-level helpers expect a resolved config and a page entry.
import {
generateJsonLdScript,
generateOGTags,
generateOGTagsHtml,
generateSchemaObjects,
resolveConfig,
} from 'aeo.js';
const page = {
pathname: '/page',
title: 'Page Title',
description: 'Page description',
content: 'Page content',
};
const resolvedConfig = resolveConfig({
title: 'My Company',
description: 'My company website',
url: 'https://mysite.com',
pages: [page],
});
// Get schema objects
const schemas = generateSchemaObjects(resolvedConfig);
// Get a <script> tag string
const script = generateJsonLdScript([...schemas.site,
...(schemas.pages[page.pathname] ?? []),]);
// Get OG meta tag objects
const tags = generateOGTags(page, resolvedConfig);
// Get OG meta tags as HTML string
const html = generateOGTagsHtml(page, resolvedConfig);
tag stringconst script = generateJsonLdScript([ ...schemas.site, ...(schemas.pages[page.pathname] ?? []),]);// Get OG meta tag objectsconst tags = generateOGTags(page, resolvedConfig);// Get OG meta tags as HTML stringconst html = generateOGTagsHtml(page, resolvedConfig);">
Tip
Schema and OG generators work independently — you can enable one without the other.
[Previous
CLI](/features/cli/) [Next
JSON-LD Recipes](/features/json-ld/)
Built by [rubenmarcus](https://github.com/rubenmarcus) & [multivmlabs](https://github.com/multivmlabs)
---
# Human/AI Widget
URL: https://aeojs.org/features/widget
> The drop-in toggle that shows how AI sees your pages.
## Human/AI Widget
The Human/AI widget is a floating toggle that lets visitors switch between the normal page and its AI-readable markdown version.
## How it works
[Section titled “How it works”](#how-it-works)
When a visitor clicks **AI**, the widget:
- Fetches the .md file for the current page
- Falls back to extracting markdown from the live DOM if no .md exists
- Displays the markdown in a slide-out panel
- Offers copy-to-clipboard and download actions
## Automatic injection
[Section titled “Automatic injection”](#automatic-injection)
Framework plugins (Astro, Vite, Nuxt, Angular) inject the widget automatically. No extra code needed.
## Manual setup (Next.js / React)
[Section titled “Manual setup (Next.js / React)”](#manual-setup-nextjs--react)
For Next.js or other manual setups, create a client component:
'use client';
import { useEffect } from 'react';
export function AeoWidgetLoader() {
useEffect(() => {
import('aeo.js/widget').then(({ AeoWidget }) => {
new AeoWidget({
config: {
title: 'My Site',
url: 'https://mysite.com',
widget: { enabled: true, position: 'bottom-right' },
},
});
});
}, []);
return null;
}
{ import('aeo.js/widget').then(({ AeoWidget }) => { new AeoWidget({ config: { title: 'My Site', url: 'https://mysite.com', widget: { enabled: true, position: 'bottom-right' }, }, }); }); }, []); return null;}">
## Framework components
[Section titled “Framework components”](#framework-components)
aeo.js ships React, Vue, and Svelte wrapper components:
### React
[Section titled “React”](#react)
import { AeoReactWidget } from 'aeo.js/react';
<AeoReactWidget config={{ title: 'My Site', url: 'https://mysite.com' }} />
">
### Vue
[Section titled “Vue”](#vue)
<script setup>
import { AeoVueWidget } from 'aeo.js/vue';
</script>
<template>
<AeoVueWidget :config="{ title: 'My Site', url: 'https://mysite.com' }" />
</template>
">
## Configuration
[Section titled “Configuration”](#configuration)
widget: {
enabled: true,
position: 'bottom-right', // 'bottom-left' | 'top-right' | 'top-left'
size: 'default', // 'small' | 'icon-only'
humanLabel: 'Human',
aiLabel: 'AI',
showBadge: true,
theme: {
background: 'rgba(18, 18, 24, 0.9)',
text: '#C0C0C5',
accent: '#E8E8EA',
badge: '#4ADE80',
},
}
## Size variants
[Section titled “Size variants”](#size-variants)
Default
Small
Icon
size: 'default'
size: 'small'
size: 'icon-only'
Full labels with icons
Compact — ~30% smaller
Just icons, no labels
Tip
Set widget.enabled: false to disable the widget entirely while still generating AEO files.
[Previous
Generated Files](/features/generated-files/) [Next
CLI](/features/cli/)
Built by [rubenmarcus](https://github.com/rubenmarcus) & [multivmlabs](https://github.com/multivmlabs)
---
# Angular
URL: https://aeojs.org/frameworks/angular
> Use aeo.js with Angular.
## Angular
## Setup
[Section titled “Setup”](#setup)
-
Install the package:
Terminal window
npm install aeo.js
-
Add a post-build step to your package.json:
{
"scripts": {
"postbuild": "node -e \"import('aeo.js/angular').then(m => m.postBuild({ title: 'My App', url: 'https://myapp.com' }))\""
}
}
m.postBuild({ title: 'My App', url: 'https://myapp.com' }))\"" }}">
-
Build your app:
Terminal window
npm run build
## How it works
[Section titled “How it works”](#how-it-works)
The Angular plugin:
- Reads angular.json to auto-detect the output directory (dist/<project>/browser/)
- Scans route config files (*.routes.ts) and component directories for routes
- Scans pre-rendered HTML from the build output for full page content
- Injects the widget into index.html automatically
## Programmatic usage
[Section titled “Programmatic usage”](#programmatic-usage)
You can also generate AEO files from source routes without building:
import { generate } from 'aeo.js/angular';
await generate({ title: 'My App', url: 'https://myapp.com' });
Note
Angular SSR (with @angular/ssr) is supported. The plugin will scan pre-rendered HTML for content extraction.
## Configuration
[Section titled “Configuration”](#configuration)
Pass any [AeoConfig](/reference/configuration/) options to postBuild or generate:
import { postBuild } from 'aeo.js/angular';
await postBuild({
title: 'My App',
url: 'https://myapp.com',
generators: {
robotsTxt: true,
llmsTxt: true,
schema: true,
},
});
[Previous
SvelteKit](/frameworks/sveltekit/) [Next
Webpack](/frameworks/webpack/)
Built by [rubenmarcus](https://github.com/rubenmarcus) & [multivmlabs](https://github.com/multivmlabs)
---
# Astro
URL: https://aeojs.org/frameworks/astro
> Use aeo.js with Astro.
## Astro
## Setup
[Section titled “Setup”](#setup)
-
Install the package:
Terminal window
npm install aeo.js
-
Add the integration to your Astro config:
astro.config.mjs
import { defineConfig } from 'astro/config';
import { aeoAstroIntegration } from 'aeo.js/astro';
export default defineConfig({
site: 'https://mysite.com',
integrations: [aeoAstroIntegration({
title: 'My Site',
description: 'A site optimized for AI discovery',
url: 'https://mysite.com',
}),],
});
-
Build your site:
Terminal window
npm run build
## How it works
[Section titled “How it works”](#how-it-works)
The Astro integration:
- Hooks into the Astro build pipeline via astro:build:done
- Scans all rendered HTML pages for content extraction
- Generates all AEO files in your output directory
- Automatically injects the Human/AI widget
- Persists the widget across View Transitions
## Configuration
[Section titled “Configuration”](#configuration)
Pass any [AeoConfig](/reference/configuration/) options to the integration:
aeoAstroIntegration({
title: 'My Site',
url: 'https://mysite.com',
generators: {
robotsTxt: true,
llmsTxt: true,
sitemap: false, // Astro has its own sitemap
},
widget: {
position: 'bottom-left',
theme: {
accent: '#6366f1',
},
},
});
[Previous
Quick Start](/getting-started/quick-start/) [Next
Next.js](/frameworks/nextjs/)
Built by [rubenmarcus](https://github.com/rubenmarcus) & [multivmlabs](https://github.com/multivmlabs)
---
# Next.js
URL: https://aeojs.org/frameworks/nextjs
> Use aeo.js with Next.js.
## Next.js
## Setup
[Section titled “Setup”](#setup)
-
Install the package:
Terminal window
npm install aeo.js
-
Wrap your Next.js config with withAeo:
next.config.mjs
import { withAeo } from 'aeo.js/next';
export default withAeo({
aeo: {
title: 'My Site',
description: 'A site optimized for AI discovery',
url: 'https://mysite.com',
},
});
-
Add the post-build step to your package.json:
{
"scripts": {
"postbuild": "node -e \"import('aeo.js/next').then(m => m.postBuild({ title: 'My Site', url: 'https://mysite.com' }))\""
}
}
m.postBuild({ title: 'My Site', url: 'https://mysite.com' }))\"" }}">
Tip
The postbuild step is needed because Next.js pre-renders pages during build. The post-build step scans the rendered HTML to extract full page content.
## Widget
[Section titled “Widget”](#widget)
For Next.js, add the widget manually as a client component:
// app/layout.tsx (or any client component)
'use client';
import { useEffect } from 'react';
export function AeoWidgetLoader() {
useEffect(() => {
import('aeo.js/widget').then(({ AeoWidget }) => {
new AeoWidget({
config: {
title: 'My Site',
url: 'https://mysite.com',
widget: { enabled: true, position: 'bottom-right' },
},
});
});
}, []);
return null;
}
{ import('aeo.js/widget').then(({ AeoWidget }) => { new AeoWidget({ config: { title: 'My Site', url: 'https://mysite.com', widget: { enabled: true, position: 'bottom-right' }, }, }); }); }, []); return null;}">
## Configuration
[Section titled “Configuration”](#configuration)
Pass any [AeoConfig](/reference/configuration/) options in the aeo key:
export default withAeo({
aeo: {
title: 'My Site',
url: 'https://mysite.com',
generators: {
robotsTxt: true,
llmsTxt: true,
schema: true,
},
widget: {
enabled: true,
position: 'bottom-right',
},
},
});
[Previous
Astro](/frameworks/astro/) [Next
Vite](/frameworks/vite/)
Built by [rubenmarcus](https://github.com/rubenmarcus) & [multivmlabs](https://github.com/multivmlabs)
---
# Nuxt
URL: https://aeojs.org/frameworks/nuxt
> Use aeo.js with Nuxt 3.
## Nuxt
## Setup
[Section titled “Setup”](#setup)
-
Install the package:
Terminal window
npm install aeo.js
-
Add the module to your Nuxt config:
nuxt.config.ts
export default defineNuxtConfig({
modules: ['aeo.js/nuxt'],
aeo: {
title: 'My Site',
description: 'A site optimized for AI discovery',
url: 'https://mysite.com',
},
});
## How it works
[Section titled “How it works”](#how-it-works)
The Nuxt module:
- Scans your pages/ directory for routes
- Generates AEO files during dev and production builds
- Scans pre-rendered HTML from .output/public/ for full page content
- Injects the widget as a client-side Nuxt plugin
- Adds <link> and <meta> tags for AEO discoverability
## Configuration
[Section titled “Configuration”](#configuration)
Pass any [AeoConfig](/reference/configuration/) options in the aeo key:
export default defineNuxtConfig({
modules: ['aeo.js/nuxt'],
aeo: {
title: 'My Site',
url: 'https://mysite.com',
generators: {
robotsTxt: true,
llmsTxt: true,
rawMarkdown: true,
},
widget: {
enabled: true,
position: 'bottom-left',
},
},
});
[Previous
Vite](/frameworks/vite/) [Next
Remix](/frameworks/remix/)
Built by [rubenmarcus](https://github.com/rubenmarcus) & [multivmlabs](https://github.com/multivmlabs)
---
# Remix
URL: https://aeojs.org/frameworks/remix
> Use aeo.js with Remix and React Router 7.
## Remix
## Setup
[Section titled “Setup”](#setup)
-
Install the package:
Terminal window
npm install aeo.js
-
Add a post-build step to your package.json:
{
"scripts": {
"postbuild": "node -e \"import('aeo.js/remix').then(m => m.postBuild({ title: 'My Site', url: 'https://mysite.com' }))\""
}
}
m.postBuild({ title: 'My Site', url: 'https://mysite.com' }))\"" }}">
-
Build your app:
Terminal window
npm run build
## How it works
[Section titled “How it works”](#how-it-works)
The Remix plugin:
- Detects the static assets directory: build/client for Vite-based Remix / React Router 7, public/ for the classic compiler
- Scans app/routes flat-file routes (_index.tsx, about.tsx, blog._index.tsx, folder routes with route.tsx) — pathless layouts (_marketing.) and optional segments (($lang).) are resolved, dynamic segments ($slug) are skipped
- Scans prerendered HTML (React Router’s prerender option) for titles, descriptions, and full text content
- Generates all AEO files (robots.txt, llms.txt, sitemap.xml, ai-index.json, …) into the static assets directory so they’re served from the site root
Tip
Enable [prerendering](https://reactrouter.com/how-to/pre-rendering) for your static routes — aeo.js extracts full page content from prerendered HTML. Routes that only render on the server are still listed from source scanning, but without page content.
## Widget
[Section titled “Widget”](#widget)
Remix renders HTML on the server, so add the widget script to your root route once:
app/root.tsx
import { getWidgetScript } from 'aeo.js/remix';
const widgetScript = getWidgetScript({
title: 'My Site',
url: 'https://mysite.com',
});
// In your Layout component, before </body>:
<div dangerouslySetInnerHTML={{ __html: widgetScript }} />
:
">
## Programmatic usage
[Section titled “Programmatic usage”](#programmatic-usage)
Generate AEO files from source routes without a build — files land in public/:
import { generate } from 'aeo.js/remix';
await generate({ title: 'My Site', url: 'https://mysite.com' });
## Configuration
[Section titled “Configuration”](#configuration)
Pass any [AeoConfig](/reference/configuration/) options to postBuild or generate:
import { postBuild } from 'aeo.js/remix';
await postBuild({
title: 'My Site',
url: 'https://mysite.com',
description: 'A site optimized for AI discovery',
schema: { organization: { name: 'My Company' } },
});
[Previous
Nuxt](/frameworks/nuxt/) [Next
SvelteKit](/frameworks/sveltekit/)
Built by [rubenmarcus](https://github.com/rubenmarcus) & [multivmlabs](https://github.com/multivmlabs)
---
# SvelteKit
URL: https://aeojs.org/frameworks/sveltekit
> Use aeo.js with SvelteKit.
## SvelteKit
## Setup
[Section titled “Setup”](#setup)
-
Install the package:
Terminal window
npm install aeo.js
-
Add a post-build step to your package.json:
{
"scripts": {
"postbuild": "node -e \"import('aeo.js/sveltekit').then(m => m.postBuild({ title: 'My Site', url: 'https://mysite.com' }))\""
}
}
m.postBuild({ title: 'My Site', url: 'https://mysite.com' }))\"" }}">
-
Build your app:
Terminal window
npm run build
## How it works
[Section titled “How it works”](#how-it-works)
The SvelteKit plugin:
- Detects the build output: build/ for adapter-static, or .svelte-kit/output for other adapters
- Scans prerendered HTML pages for titles, descriptions, and full text content
- Scans src/routes for +page.svelte (and mdsvex +page.md/+page.svx) routes — route groups like (marketing) are transparent, dynamic segments like [slug] are skipped
- Generates all AEO files (robots.txt, llms.txt, sitemap.xml, ai-index.json, …) into the output directory
- Injects the Human/AI widget into every prerendered page
Tip
For the best results, prerender as much as possible (export const prerender = true in your root +layout.ts). aeo.js extracts content from prerendered HTML — server-rendered-only routes are still listed from source scanning, but without page content.
## Programmatic usage
[Section titled “Programmatic usage”](#programmatic-usage)
Generate AEO files from source routes without a build — files land in static/ so any adapter ships them:
import { generate } from 'aeo.js/sveltekit';
await generate({ title: 'My Site', url: 'https://mysite.com' });
## Configuration
[Section titled “Configuration”](#configuration)
Pass any [AeoConfig](/reference/configuration/) options to postBuild or generate:
import { postBuild } from 'aeo.js/sveltekit';
await postBuild({
title: 'My Site',
url: 'https://mysite.com',
description: 'A site optimized for AI discovery',
widget: { enabled: true, position: 'bottom-right' },
// Skip widget injection into prerendered HTML:
// injectWidget: false,
});
[Previous
Remix](/frameworks/remix/) [Next
Angular](/frameworks/angular/)
Built by [rubenmarcus](https://github.com/rubenmarcus) & [multivmlabs](https://github.com/multivmlabs)
---
# Vanilla JS / Static HTML
URL: https://aeojs.org/frameworks/vanilla
> Use aeo.js with a plain HTML site, hand-rolled JS, or any static-site generator — no framework required.
## Vanilla JS / Static HTML
aeo.js works on any project that produces static HTML. Use the CLI directly — no plugin, no module, no framework.
## Quick Start
[Section titled “Quick Start”](#quick-start)
- [Zero-install (npx)](#tab-panel-4)
- [Installed](#tab-panel-5)
-
Terminal window
npx aeo.js generate --url https://mysite.com --title "My Site" --out public
**
Terminal window
npm install --save-dev aeo.js
npx aeo.js generate --url https://mysite.com --title "My Site" --out public
generate walks your output directory, extracts page content, and emits robots.txt, llms.txt, sitemap.xml, ai-index.json, and schema.json next to your HTML.
Caution
The standalone CLI configures itself from flags only** — it does not load aeo.config.{ts,js}. Pass --url / --title / --out on the command line, or call the package’s API programmatically (see “Calling the API directly” below) to access richer options like contentDir and pages.
## How aeo.js Discovers Your Pages
[Section titled “How aeo.js Discovers Your Pages”](#how-aeojs-discovers-your-pages)
Source
How to set
Use when
**Built HTML in outDir**
--out public (CLI)
You have a built static site with *.html files
**contentDir**
programmatic only — see below
You have handwritten .md / .mdx files
**pages array**
programmatic only — see below
Explicit control for runtime-only routes
The CLI exposes --out for outDir. For the richer options (contentDir, pages), call the package’s API directly:
scripts/aeo.mjs
import { generateAEOFiles, resolveConfig } from 'aeo.js';
await generateAEOFiles(resolveConfig({
title: 'My Site',
url: 'https://mysite.com',
contentDir: 'content',
outDir: 'public',
pages: [{ pathname: '/', title: 'Home' }],
}));
## Common Setups
[Section titled “Common Setups”](#common-setups)
### Hand-rolled HTML site
[Section titled “Hand-rolled HTML site”](#hand-rolled-html-site)
package.json
{
"scripts": {
"build:aeo": "aeo.js generate --url https://mysite.com --title \"My Site\" --out ."
}
}
### Eleventy / Hugo / Jekyll
[Section titled “Eleventy / Hugo / Jekyll”](#eleventy--hugo--jekyll)
package.json
{
"scripts": {
"build": "eleventy",
"postbuild": "aeo.js generate --url https://mysite.com --title \"My Site\" --out _site"
}
}
For Hugo use --out public; for Jekyll --out _site. The postbuild script runs automatically after npm run build.
### Markdown blog (no framework)
[Section titled “Markdown blog (no framework)”](#markdown-blog-no-framework)
The CLI’s --out flag covers built HTML; to also pull .md front-matter and bodies from a contentDir, use the programmatic API:
scripts/aeo.mjs
import { generateAEOFiles, resolveConfig } from 'aeo.js';
await generateAEOFiles(resolveConfig({
title: 'My Blog',
url: 'https://myblog.dev',
contentDir: 'content', // pull post bodies from here
outDir: 'public', // write generated files here
}));
package.json
{
"scripts": {
"build:aeo": "node scripts/aeo.mjs"
}
}
## Adding the Widget
[Section titled “Adding the Widget”](#adding-the-widget)
<script type="module">
import { AeoWidget } from 'https://esm.sh/aeo.js/widget';
new AeoWidget({
config: {
title: 'My Site',
url: 'https://mysite.com',
widget: { enabled: true, position: 'bottom-right', size: 'small' },
},
});
</script>
Or install and bundle locally with import { AeoWidget } from 'aeo.js/widget'.
## CI Integration
[Section titled “CI Integration”](#ci-integration)
- run: npx aeo.js check --json | tee audit.json
- run: |
SCORE=$(jq '.audit.score' audit.json)
["$SCORE" -ge 70] || { echo "GEO score $SCORE below 70"; exit 1; }
Note
Full walkthrough with deployment recipes (Vercel, Netlify, Cloudflare, GitHub Pages) is in the [Vanilla Guide on GitHub](https://github.com/multivmlabs/aeo.js/blob/main/docs/vanilla.md).
## Further Reading
[Section titled “Further Reading”](#further-reading)
[CLI Reference](/features/cli/) — every command, every flag
- [JSON-LD Recipes](/features/json-ld/) — FAQ, HowTo, Product, Article, Recipe, Event
- [Configuration Reference](/reference/configuration/)
[Previous
Webpack](/frameworks/webpack/) [Next
Generated Files](/features/generated-files/)
Built by [rubenmarcus](https://github.com/rubenmarcus) & [multivmlabs](https://github.com/multivmlabs)
---
# Vite
URL: https://aeojs.org/frameworks/vite
> Use aeo.js with Vite (React, Vue, Svelte, etc.).
## Vite
## Setup
[Section titled “Setup”](#setup)
-
Install the package:
Terminal window
npm install aeo.js
-
Add the plugin to your Vite config:
vite.config.ts
import { defineConfig } from 'vite';
import { aeoVitePlugin } from 'aeo.js/vite';
export default defineConfig({
plugins: [aeoVitePlugin({
title: 'My Site',
description: 'A site optimized for AI discovery',
url: 'https://mysite.com',
}),],
});
## How it works
[Section titled “How it works”](#how-it-works)
The Vite plugin:
- Generates AEO files on both vite dev and vite build
- Injects the Human/AI widget automatically via HTML transform
- Serves dynamic .md files during development (extracts content from your running app)
- Detects SPA shells and falls back to client-side DOM extraction
- Works with React, Vue, Svelte, or any Vite-based framework
## Configuration
[Section titled “Configuration”](#configuration)
Pass any [AeoConfig](/reference/configuration/) options to the plugin:
aeoVitePlugin({
title: 'My Site',
url: 'https://mysite.com',
widget: {
position: 'top-right',
theme: {
background: 'rgba(0, 0, 0, 0.85)',
accent: '#f59e0b',
},
},
});
[Previous
Next.js](/frameworks/nextjs/) [Next
Nuxt](/frameworks/nuxt/)
Built by [rubenmarcus](https://github.com/rubenmarcus) & [multivmlabs](https://github.com/multivmlabs)
---
# Webpack
URL: https://aeojs.org/frameworks/webpack
> Use aeo.js with Webpack.
## Webpack
## Setup
[Section titled “Setup”](#setup)
-
Install the package:
Terminal window
npm install aeo.js
-
Add the plugin to your Webpack config:
webpack.config.js
const { AeoWebpackPlugin } = require('aeo.js/webpack');
module.exports = {
plugins: [new AeoWebpackPlugin({
title: 'My Site',
description: 'A site optimized for AI discovery',
url: 'https://mysite.com',
}),],
};
## How it works
[Section titled “How it works”](#how-it-works)
The Webpack plugin:
- Runs during the emit phase of the Webpack compilation
- Generates all AEO files in your output directory
- Injects the widget into your HTML entry point
## Configuration
[Section titled “Configuration”](#configuration)
Pass any [AeoConfig](/reference/configuration/) options to the plugin:
new AeoWebpackPlugin({
title: 'My Site',
url: 'https://mysite.com',
widget: {
enabled: true,
position: 'bottom-right',
},
});
[Previous
Angular](/frameworks/angular/) [Next
Vanilla JS / Static HTML](/frameworks/vanilla/)
Built by [rubenmarcus](https://github.com/rubenmarcus) & [multivmlabs](https://github.com/multivmlabs)
---
# Installation
URL: https://aeojs.org/getting-started/installation
> Install aeo.js in your project.
## Installation
## Install the package
[Section titled “Install the package”](#install-the-package)
- [npm](#tab-panel-6)
- [pnpm](#tab-panel-7)
- [yarn](#tab-panel-8)
-
Terminal window
npm install aeo.js
**
Terminal window
pnpm add aeo.js
Terminal window
yarn add aeo.js
## Or use the CLI directly
[Section titled “Or use the CLI directly”](#or-use-the-cli-directly)
No installation needed — run it with npx:
Terminal window
npx aeo.js generate
## Requirements
[Section titled “Requirements”](#requirements)
Node.js** 18 or later
- A framework with a build step that outputs HTML (or use the CLI for any static site)
## Next steps
[Section titled “Next steps”](#next-steps)
Head to [Quick Start](/getting-started/quick-start/) to add aeo.js to your project, or jump to your framework’s guide:
- [Astro](/frameworks/astro/)
- [Next.js](/frameworks/nextjs/)
- [Vite](/frameworks/vite/)
- [Nuxt](/frameworks/nuxt/)
- [Angular](/frameworks/angular/)
- [Webpack](/frameworks/webpack/)
[Previous
Introduction](/getting-started/introduction/) [Next
Quick Start](/getting-started/quick-start/)
Built by [rubenmarcus](https://github.com/rubenmarcus) & [multivmlabs](https://github.com/multivmlabs)
---
# Introduction
URL: https://aeojs.org/getting-started/introduction
> What is aeo.js and why you need Answer Engine Optimization.
## Introduction
## What is Answer Engine Optimization?
[Section titled “What is Answer Engine Optimization?”](#what-is-answer-engine-optimization)
Answer Engine Optimization (AEO) is the practice of making your website discoverable and citable by AI-powered answer engines like **ChatGPT**, **Claude**, **Perplexity**, and **SearchGPT**.
Unlike traditional SEO which focuses on search engine result pages (SERPs), AEO ensures your content is understood and referenced by Large Language Models (LLMs) when they generate answers.
## Why aeo.js?
[Section titled “Why aeo.js?”](#why-aeojs)
AI crawlers and LLMs look for specific files and formats that most websites don’t provide:
- **llms.txt** — A concise summary of your site written for LLMs
- **llms-full.txt** — Full content concatenated for deep ingestion
- **ai-index.json** — Structured content optimized for RAG pipelines
- **docs.json** — Documentation manifest for content discovery
- **Raw Markdown** — Per-page .md files for clean content extraction
**aeo.js** generates all of these automatically from your existing pages — no manual work required.
## How it works
[Section titled “How it works”](#how-it-works)
- **Install** the package and add the plugin for your framework
- **Build** your site as usual
- **aeo.js** scans your output, extracts content, and generates all AEO files
- The optional **widget** lets visitors toggle between human and AI views of your pages
## Scope: what aeo.js does and doesn’t influence
[Section titled “Scope: what aeo.js does and doesn’t influence”](#scope-what-aeojs-does-and-doesnt-influence)
AEO is the **on-site** half of being discoverable by AI engines — there’s an off-site half that aeo.js cannot reach. Setting expectations upfront so you can plan a full strategy:
What aeo.js does well
Improves machine-readable access to **your own site**:
- generates llms.txt, llms-full.txt, ai-index.json, raw markdown copies, JSON-LD schema, and a sitemap
- structures content so AI crawlers can fetch, parse, and quote it
- audits and scores your page-level citability so you know where to improve
What aeo.js cannot influence
Off-site signals that AI engines also weigh:
- **third-party mentions and reviews** of your product across the web
- **inbound links** and how reputable the linking domains are
- **community discussions** (Reddit, Hacker News, X, niche forums) and how often you’re cited there
- **model training data** — what an LLM “already knows” about you from its pre-training cutoff
- **freshness signals** outside your control (independent news, blog roundups, podcasts)
Think of aeo.js as a strong foundation — necessary but not sufficient. For full AEO coverage, pair it with the off-site work above: PR, community engagement, and being mentioned where your customers research.
## Supported Frameworks
[Section titled “Supported Frameworks”](#supported-frameworks)
Framework
Import
Astro
aeo.js/astro
Next.js
aeo.js/next
Vite (React, Vue, Svelte)
aeo.js/vite
Nuxt
aeo.js/nuxt
Angular
aeo.js/angular
Webpack
aeo.js/webpack
Any (CLI)
npx aeo.js generate
[Next
Installation](/getting-started/installation/)
Built by [rubenmarcus](https://github.com/rubenmarcus) & [multivmlabs](https://github.com/multivmlabs)
---
# Quick Start
URL: https://aeojs.org/getting-started/quick-start
> Get aeo.js running in under 2 minutes.
## Quick Start
The fastest way to add AEO to your site depends on your framework.
## Astro
[Section titled “Astro”](#astro)
-
Install the package:
Terminal window
npm install aeo.js
-
Add the integration to your Astro config:
astro.config.mjs
import { defineConfig } from 'astro/config';
import { aeoAstroIntegration } from 'aeo.js/astro';
export default defineConfig({
site: 'https://mysite.com',
integrations: [aeoAstroIntegration({
title: 'My Site',
description: 'A site optimized for AI discovery',
url: 'https://mysite.com',
}),],
});
-
Build your site — all AEO files are generated automatically.
## Next.js
[Section titled “Next.js”](#nextjs)
-
Install the package:
Terminal window
npm install aeo.js
-
Wrap your Next.js config:
next.config.mjs
import { withAeo } from 'aeo.js/next';
export default withAeo({
aeo: {
title: 'My Site',
description: 'A site optimized for AI discovery',
url: 'https://mysite.com',
},
});
-
Add the post-build step to package.json:
{
"scripts": {
"postbuild": "node -e \"import('aeo.js/next').then(m => m.postBuild({ title: 'My Site', url: 'https://mysite.com' }))\""
}
}
m.postBuild({ title: 'My Site', url: 'https://mysite.com' }))\"" }}">
## Vite
[Section titled “Vite”](#vite)
-
Install the package:
Terminal window
npm install aeo.js
-
Add the plugin to your Vite config:
vite.config.ts
import { defineConfig } from 'vite';
import { aeoVitePlugin } from 'aeo.js/vite';
export default defineConfig({
plugins: [aeoVitePlugin({
title: 'My Site',
description: 'A site optimized for AI discovery',
url: 'https://mysite.com',
}),],
});
## CLI (any site)
[Section titled “CLI (any site)”](#cli-any-site)
No framework integration needed:
Terminal window
npx aeo.js generate --url https://mysite.com --title "My Site"
See [CLI docs](/features/cli/) for all commands and options.
[Previous
Installation](/getting-started/installation/) [Next
Astro](/frameworks/astro/)
Built by [rubenmarcus](https://github.com/rubenmarcus) & [multivmlabs](https://github.com/multivmlabs)
---
# aeo.js — Answer Engine Optimization for the Modern Web
URL: https://aeojs.org
> Answer Engine Optimization for the modern web. Make your site discoverable by AI crawlers and LLMs.
## aeo.js
[Get Started](/getting-started/introduction/)
**Install Audit Generate Config
$ ▌
added 1 package in 2s
Ready — run npx aeo.js init to get started
$ ▌
GEO Readiness Score: 92/100 (Excellent)
══════════════════════════════════════
AI Access: 20/20
Content Structure: 20/20
Schema Presence: 20/20
Meta Quality: 16/20
Citability: 16/20
$ ▌
[aeo.js] Generating AEO files...
robots.txt — AI crawler directives
llms.txt — LLM summary
llms-full.txt — full content for LLMs
sitemap.xml — sitemap
schema.json — JSON-LD structured data
ai-index.json — AI content index
Generated 6 files in 42ms
// aeo.config.ts
import { defineConfig } from 'aeo.js'
export default defineConfig({
title: 'My Site',
url: 'https://mysite.com',
schema: { organization: { name: 'My Co' } },
widget: { size: 'small' },
});
- [Quick Start](#quick-start)
- [What is AEO?](#what-is-aeo)
- [Why AEO](#why-aeo-matters)
- [Widget](#widget)
- [Configuration](#configuration)
- [Generated Files](#generated-files)
- [Checker](#checker)
## Quick Start
[Section titled “Quick Start”](#quick-start)
-
Terminal window
npm install aeo.js
[Astro](#tab-panel-9)
- [Next.js](#tab-panel-10)
- [Vite](#tab-panel-11)
- [Nuxt](#tab-panel-12)
- [Angular](#tab-panel-13)
- [Webpack](#tab-panel-14)
- [CLI](#tab-panel-15)
astro.config.mjs
import { defineConfig } from 'astro/config';
import { aeoAstroIntegration } from 'aeo.js/astro';
export default defineConfig({
site: 'https://mysite.com',
integrations: [aeoAstroIntegration({
title: 'My Site',
description: 'A site optimized for AI discovery',
url: 'https://mysite.com',
}),],
});
next.config.mjs
import { withAeo } from 'aeo.js/next';
export default withAeo({
aeo: {
title: 'My Site',
description: 'A site optimized for AI discovery',
url: 'https://mysite.com',
},
});
Add the post-build step to package.json:
{
"scripts": {
"postbuild": "node -e \"import('aeo.js/next').then(m => m.postBuild({ title: 'My Site', url: 'https://mysite.com' }))\""
}
}
m.postBuild({ title: 'My Site', url: 'https://mysite.com' }))\"" }}">
vite.config.ts
import { defineConfig } from 'vite';
import { aeoVitePlugin } from 'aeo.js/vite';
export default defineConfig({
plugins: [aeoVitePlugin({
title: 'My Site',
description: 'A site optimized for AI discovery',
url: 'https://mysite.com',
}),],
});
nuxt.config.ts
export default defineNuxtConfig({
modules: ['aeo.js/nuxt'],
aeo: {
title: 'My Site',
description: 'A site optimized for AI discovery',
url: 'https://mysite.com',
},
});
Add a post-build step to package.json:
{
"scripts": {
"postbuild": "node -e \"import('aeo.js/angular').then(m => m.postBuild({ title: 'My App', url: 'https://myapp.com' }))\""
}
}
m.postBuild({ title: 'My App', url: 'https://myapp.com' }))\"" }}">
Or generate programmatically:
import { generate } from 'aeo.js/angular';
await generate({ title: 'My App', url: 'https://myapp.com' });
webpack.config.js
const { AeoWebpackPlugin } = require('aeo.js/webpack');
module.exports = {
plugins: [new AeoWebpackPlugin({
title: 'My Site',
description: 'A site optimized for AI discovery',
url: 'https://mysite.com',
}),],
};
No framework needed — run standalone:
Terminal window
# Generate all AEO files
npx aeo.js generate --url https://mysite.com --title "My Site"
# Scaffold a config file
npx aeo.js init
# Check your setup
npx aeo.js check
## What is AEO?
[Section titled “What is AEO?”](#what-is-aeo)
Answer Engine Optimization (AEO) is the practice of making your website discoverable and citable by AI-powered answer engines like ChatGPT, Claude, Perplexity, and SearchGPT.
aeo.js** auto-generates the files these engines look for and provides a drop-in widget that shows visitors how your site appears to AI. First-class support for Astro, Next.js, Vite, Nuxt, Angular, and Webpack — or run standalone via CLI.
## Why AEO matters
[Section titled “Why AEO matters”](#why-aeo-matters)
58%
of searches
end without a click — AI gives the answer directly
40%
of Gen Z
prefer AI assistants over traditional search engines
97%
of sites
have no llms.txt or structured data for AI crawlers
1 min
to set up
aeo.js generates robots.txt, llms.txt, schema & more
If your site isn’t optimized for AI engines, you’re invisible to a growing share of users who never open a search results page. AEO is the new SEO.
## Widget
[Section titled “Widget”](#widget)
The Human/AI widget lets visitors toggle between the normal page and its AI-readable markdown version. Framework plugins inject it automatically — for Next.js or manual setups:
'use client';
import { useEffect } from 'react';
export function AeoWidgetLoader() {
useEffect(() => {
import('aeo.js/widget').then(({ AeoWidget }) => {
new AeoWidget({
config: {
title: 'My Site',
url: 'https://mysite.com',
widget: { enabled: true, position: 'bottom-right' },
},
});
});
}, []);
return null;
}
{ import('aeo.js/widget').then(({ AeoWidget }) => { new AeoWidget({ config: { title: 'My Site', url: 'https://mysite.com', widget: { enabled: true, position: 'bottom-right' }, }, }); }); }, []); return null;}">
React and Vue wrapper components are also available:
- [React](#tab-panel-16)
- [Vue](#tab-panel-17)
import { AeoReactWidget } from 'aeo.js/react';
<AeoReactWidget config={{ title: 'My Site', url: 'https://mysite.com' }} />
">
<script setup>
import { AeoVueWidget } from 'aeo.js/vue';
</script>
<template>
<AeoVueWidget :config="{ title: 'My Site', url: 'https://mysite.com' }" />
</template>
">
## Configuration
[Section titled “Configuration”](#configuration)
All frameworks use the same config shape — [see full reference](/reference/configuration/):
import { defineConfig } from 'aeo.js';
export default defineConfig({
title: 'My Site',
url: 'https://mysite.com',
description: 'A site optimized for AI discovery',
generators: {
robotsTxt: true,
llmsTxt: true,
llmsFullTxt: true,
rawMarkdown: true,
sitemap: true,
aiIndex: true,
schema: true,
},
schema: {
enabled: true,
organization: { name: 'My Company', url: 'https://mysite.com' },
defaultType: 'WebPage',
},
og: {
enabled: true,
image: 'https://mysite.com/og.png',
twitterHandle: '@mycompany',
},
widget: {
enabled: true,
position: 'bottom-right',
theme: { accent: '#4ADE80', badge: '#4ADE80' },
},
});
## Generated Files
[Section titled “Generated Files”](#generated-files)
After building, your output directory contains:
public/
├── robots.txt # AI-crawler directives
├── llms.txt # Short LLM-readable summary
├── llms-full.txt # Full content for LLMs
├── sitemap.xml # Standard sitemap
├── docs.json # Documentation manifest
├── ai-index.json # AI content index
├── index.md # Markdown for /
└── about.md # Markdown for /about
## Checker
[Section titled “Checker”](#checker)
Scan
Free — no signup required. Powered by [AEO Checker](https://check.aeojs.org)
## Built by
[](https://github.com/rubenmarcus)[](https://github.com/KimHyeongRae0)[](https://github.com/FabioRocha231)[](https://github.com/ribeiroevandro)[](https://github.com/HusseinAdeiza)[](https://github.com/mhanelia)
[View all 6 contributors →](/contributors/)
Built by [rubenmarcus](https://github.com/rubenmarcus) & [multivmlabs](https://github.com/multivmlabs)
---
# API Reference
URL: https://aeojs.org/reference/api
> Programmatic API for aeo.js.
## API Reference
## Core
[Section titled “Core”](#core)
### defineConfig(config)
[Section titled “defineConfig(config)”](#defineconfigconfig)
Type-safe configuration helper:
import { defineConfig } from 'aeo.js';
export default defineConfig({
title: 'My Site',
url: 'https://mysite.com',
});
**
### generateAll(config)
[Section titled “generateAll(config)”](#generateallconfig)
Generate all AEO files programmatically:
import { generateAll } from 'aeo.js';
await generateAll({
title: 'My Site',
url: 'https://mysite.com',
outDir: 'dist',
});
### resolveConfig(config)
[Section titled “resolveConfig(config)”](#resolveconfigconfig)
Resolve a partial config into a fully resolved config with defaults:
import { resolveConfig } from 'aeo.js';
const resolved = resolveConfig({ title: 'My Site' });
// All fields now have defaults
The examples below use resolvedConfig for a config object returned by resolveConfig.
### validateConfig(config)
[Section titled “validateConfig(config)”](#validateconfigconfig)
Validate a config object and return any errors:
import { validateConfig } from 'aeo.js';
const errors = validateConfig(config);
### detectFramework()
[Section titled “detectFramework()”](#detectframework)
Auto-detect the framework used in the current project:
import { detectFramework } from 'aeo.js';
const info = detectFramework();
// { framework: 'next', contentDir: 'pages', outDir: '.next' }
## HTML Extraction
[Section titled “HTML Extraction”](#html-extraction)
### htmlToMarkdown(html, pagePath, config)
[Section titled “htmlToMarkdown(html, pagePath, config)”](#htmltomarkdownhtml-pagepath-config)
Convert HTML to clean markdown:
import { htmlToMarkdown } from 'aeo.js';
const md = htmlToMarkdown(
'<html><head><title>Hello</title></head><body><main><p>World</p></main></body></html>',
'/',
{ url: 'https://mysite.com' }
);
// Returns a markdown document with YAML frontmatter
Hello
World
', '/', { url: 'https://mysite.com' });// Returns a markdown document with YAML frontmatter">
### extractTextFromHtml(html)
[Section titled “extractTextFromHtml(html)”](#extracttextfromhtmlhtml)
Extract plain text from HTML:
import { extractTextFromHtml } from 'aeo.js';
const text = extractTextFromHtml('<p>Hello <strong>world</strong></p>');
world**
');">
### extractTitle(html) / extractDescription(html)
[Section titled “extractTitle(html) / extractDescription(html)”](#extracttitlehtml--extractdescriptionhtml)
Extract the title or meta description from an HTML document.
### extractJsonLd(html)
[Section titled “extractJsonLd(html)”](#extractjsonldhtml)
Extract JSON-LD structured data from an HTML document.
## Schema
[Section titled “Schema”](#schema)
### generateSchemaObjects(config)
[Section titled “generateSchemaObjects(config)”](#generateschemaobjectsconfig)
Generate JSON-LD schema objects:
import { generateSchemaObjects } from 'aeo.js';
const schemas = generateSchemaObjects(resolvedConfig);
### generateJsonLdScript(schemas)
[Section titled “generateJsonLdScript(schemas)”](#generatejsonldscriptschemas)
Generate a <script type="application/ld+json"> tag:
import { generateJsonLdScript, generateSiteSchemas } from 'aeo.js';
const schemas = generateSiteSchemas(resolvedConfig);
const script = generateJsonLdScript(schemas);
### generateSiteSchemas(config) / generatePageSchemas(page, config)
[Section titled “generateSiteSchemas(config) / generatePageSchemas(page, config)”](#generatesiteschemasconfig--generatepageschemaspage-config)
Generate schemas for the site or individual pages.
## Open Graph
[Section titled “Open Graph”](#open-graph)
### generateOGTags(page, config)
[Section titled “generateOGTags(page, config)”](#generateogtagspage-config)
Returns an array of MetaTag objects:
import { generateOGTags } from 'aeo.js';
const tags = generateOGTags(
{ pathname: '/', title: 'Home' },
resolvedConfig
);
// [{ property: 'og:title', content: 'Home' }, ...]
### generateOGTagsHtml(page, config)
[Section titled “generateOGTagsHtml(page, config)”](#generateogtagshtmlpage-config)
Returns OG tags as an HTML string.
## Audit
[Section titled “Audit”](#audit)
### auditSite(config)
[Section titled “auditSite(config)”](#auditsiteconfig)
Audit a site for AEO best practices:
import { auditSite, formatAuditReport, getGrade, resolveConfig } from 'aeo.js';
const config = resolveConfig({
title: 'My Site',
url: 'https://mysite.com',
pages: [{ pathname: '/', title: 'Home', content: 'Welcome to my site.' }],
});
const result = auditSite(config);
console.log(formatAuditReport(result));
console.log(getGrade(result.score)); // 'Excellent', 'Good', 'Fair', etc.
## Citability
[Section titled “Citability”](#citability)
### scorePageCitability(page)
[Section titled “scorePageCitability(page)”](#scorepagecitabilitypage)
Score how likely a page is to be cited by AI:
import { scorePageCitability, formatPageCitability } from 'aeo.js';
const score = scorePageCitability({
pathname: '/',
title: 'Home',
content: '# Home\n\nMy site publishes practical guides for AI-ready content.',
});
console.log(formatPageCitability(score));
### scoreSiteCitability(config)
[Section titled “scoreSiteCitability(config)”](#scoresitecitabilityconfig)
Score citability across your entire site.
## Reports
[Section titled “Reports”](#reports)
### generateReport(config)
[Section titled “generateReport(config)”](#generatereportconfig)
Generate a comprehensive AEO report:
import { generateReport, formatReportMarkdown } from 'aeo.js';
const report = generateReport(resolvedConfig);
console.log(formatReportMarkdown(report));
## Platform Hints
[Section titled “Platform Hints”](#platform-hints)
### generatePlatformHints(audit, citability)
[Section titled “generatePlatformHints(audit, citability)”](#generateplatformhintsaudit-citability)
Generate platform-specific optimization hints from audit and citability results:
import { auditSite, generatePlatformHints, scoreSiteCitability } from 'aeo.js';
const audit = auditSite(resolvedConfig);
const citability = scoreSiteCitability(resolvedConfig);
const hints = generatePlatformHints(audit, citability);
## Types
[Section titled “Types”](#types)
All types are exported from the main entry:
import type {
AeoConfig,
ResolvedAeoConfig,
PageEntry,
DocEntry,
AeoManifest,
MarkdownFile,
ManifestEntry,
AIIndexEntry,
FrameworkType,
FrameworkInfo,
AuditResult,
AuditCategory,
AuditIssue,
MetaTag,
PageCitabilityResult,
SiteCitabilityResult,
CitabilityDimension,
ContentHint,
AeoReport,
PlatformHint,
} from 'aeo.js';
[Previous
Configuration](/reference/configuration/)
Built by [rubenmarcus](https://github.com/rubenmarcus) & [multivmlabs](https://github.com/multivmlabs)
---
# Configuration
URL: https://aeojs.org/reference/configuration
> Full configuration reference for aeo.js.
## Configuration
All framework plugins and the CLI accept the same AeoConfig object.
## Full example
[Section titled “Full example”](#full-example)
import { defineConfig } from 'aeo.js';
export default defineConfig({
// Required
title: 'My Site',
url: 'https://mysite.com',
// Optional
description: 'A description of your site',
contentDir: 'docs', // Directory with handwritten .md files
outDir: 'public', // Output directory for generated files
// Toggle individual generators
generators: {
robotsTxt: true,
llmsTxt: true,
llmsFullTxt: true,
rawMarkdown: true,
manifest: true,
sitemap: true,
aiIndex: true,
schema: true,
},
// Configure ai-index.json generation
aiIndex: {
maxChunkLength: 2000,
maxKeywords: 10,
},
// Customize robots.txt
robots: {
allow: ['/'],
disallow: ['/admin'],
crawlDelay: 0,
sitemap: 'https://mysite.com/sitemap.xml',
},
// JSON-LD structured data
schema: {
enabled: true,
organization: {
name: 'My Company',
url: 'https://mysite.com',
logo: 'https://mysite.com/logo.png',
sameAs: ['https://twitter.com/mycompany'],
},
defaultType: 'WebPage',
},
// Open Graph meta tags
og: {
enabled: true,
image: 'https://mysite.com/og.png',
twitterHandle: '@mycompany',
type: 'website',
},
// Widget configuration
widget: {
enabled: true,
position: 'bottom-right',
humanLabel: 'Human',
aiLabel: 'AI',
showBadge: true,
theme: {
background: 'rgba(18, 18, 24, 0.9)',
text: '#C0C0C5',
accent: '#E8E8EA',
badge: '#4ADE80',
},
},
});
## Options reference
[Section titled “Options reference”](#options-reference)
### Top-level
[Section titled “Top-level”](#top-level)
Option
Type
Default
Description
title
string
''
Site title
description
string
''
Site description
url
string
''
Site URL (used for absolute URLs in generated files)
contentDir
string
'docs'
Directory with handwritten markdown files
outDir
string
auto-detected
Output directory for generated files
pages
PageEntry[]
[]
Manually specify pages to include
### generators
[Section titled “generators”](#generators)
Option
Type
Default
Description
robotsTxt
boolean
true
Generate robots.txt
llmsTxt
boolean
true
Generate llms.txt
llmsFullTxt
boolean
true
Generate llms-full.txt
rawMarkdown
boolean
true
Generate per-page .md files
manifest
boolean
true
Generate docs.json
sitemap
boolean
true
Generate sitemap.xml
aiIndex
boolean
true
Generate ai-index.json
schema
boolean
false
Generate JSON-LD structured data
### aiIndex
[Section titled “aiIndex”](#aiindex)
Option
Type
Default
Description
maxChunkLength
number
2000
Target chunk length (soft limit). Chunks split on \n\n paragraph boundaries, so a single long paragraph can exceed this value. Set with embedding-model token limits in mind.
maxKeywords
number
10
Maximum number of keywords extracted per ai-index.json entry.
### robots
[Section titled “robots”](#robots)
Option
Type
Default
Description
allow
string[]
['/']
Allowed paths
disallow
string[]
[]
Disallowed paths
crawlDelay
number
0
Crawl delay in seconds
sitemap
string
auto
Sitemap URL
### schema
[Section titled “schema”](#schema)
Option
Type
Default
Description
enabled
boolean
false
Enable schema generation
organization.name
string
''
Organization name
organization.url
string
''
Organization URL
organization.logo
string
''
Organization logo URL
organization.sameAs
string[]
[]
Social profile URLs
defaultType
'Article' | 'WebPage'
'WebPage'
Default schema type for pages
### og
[Section titled “og”](#og)
Option
Type
Default
Description
enabled
boolean
false
Enable OG tag generation
image
string
''
Default OG image URL
twitterHandle
string
''
Twitter handle (e.g. @company)
type
'website' | 'article'
'website'
Default OG type
### widget
[Section titled “widget”](#widget)
Option
Type
Default
Description
enabled
boolean
true
Enable the widget
position
string
'bottom-right'
Widget position
humanLabel
string
'Human'
Label for human view
aiLabel
string
'AI'
Label for AI view
showBadge
boolean
true
Show the AEO badge
theme.background
string
'rgba(18, 18, 24, 0.9)'
Widget background
theme.text
string
'#C0C0C5'
Text color
theme.accent
string
'#E8E8EA'
Accent color
theme.badge
string
'#4ADE80'
Badge color
[Previous
Audit & Citability](/features/audit/) [Next
API](/reference/api/)
Built by [rubenmarcus](https://github.com/rubenmarcus) & [multivmlabs](https://github.com/multivmlabs)
---
# Audit & Citability
Source: docs/features/audit.mdx
> Audit your AEO setup and measure your site's citability score.
### Site Audit
The `auditSite` function checks your site for AEO best practices and returns a detailed report:
```ts
import { auditSite, formatAuditReport, getGrade, resolveConfig } from 'aeo.js';
const config = resolveConfig({
title: 'My Site',
url: 'https://mysite.com',
pages: [{ pathname: '/', title: 'Home', content: 'Welcome to my site.' }],
});
const result = auditSite(config);
console.log(formatAuditReport(result));
console.log('Grade:', getGrade(result.score));
```
The audit checks for:
- Presence of `robots.txt`, `llms.txt`, `llms-full.txt`
- Sitemap accessibility
- Structured data (JSON-LD)
- Open Graph meta tags
- AI crawler accessibility
You can also run it from the CLI:
```bash
npx aeo.js check
```
### Citability Score
Measure how likely AI engines are to cite your content:
```ts
import { formatPageCitability, resolveConfig, scorePageCitability, scoreSiteCitability } from 'aeo.js';
// Score a single page
const pageScore = scorePageCitability({
pathname: '/',
title: 'Home',
content: '# Home\n\nMy site publishes practical guides for AI-ready content.',
});
console.log(formatPageCitability(pageScore));
// Score the whole site
const config = resolveConfig({
title: 'My Site',
url: 'https://mysite.com',
pages: [{ pathname: '/', title: 'Home', content: 'Welcome to my site.' }],
});
const siteScore = scoreSiteCitability(config);
```
The citability score evaluates:
- Content structure and headings
- Factual density and specificity
- Source attribution
- Unique data and statistics
- Clear definitions and explanations
### Reports
Generate a comprehensive AEO report:
```ts
import { formatReportJson, formatReportMarkdown, generateReport, resolveConfig } from 'aeo.js';
const config = resolveConfig({
title: 'My Site',
url: 'https://mysite.com',
pages: [{ pathname: '/', title: 'Home', content: 'Welcome to my site.' }],
});
const report = generateReport(config);
// Get as markdown
console.log(formatReportMarkdown(report));
// Get as JSON
console.log(formatReportJson(report));
```
### Platform Hints
Get platform-specific optimization suggestions:
```ts
import { auditSite, generatePlatformHints, resolveConfig, scoreSiteCitability } from 'aeo.js';
const config = resolveConfig({
title: 'My Site',
url: 'https://mysite.com',
pages: [{ pathname: '/', title: 'Home', content: 'Welcome to my site.' }],
});
const audit = auditSite(config);
const citability = scoreSiteCitability(config);
const hints = generatePlatformHints(audit, citability);
// Returns platform-specific optimization hints for ChatGPT, Perplexity, Google AI, and Bing Copilot
```
---
# CLI
Source: docs/features/cli.mdx
> Run aeo.js from the command line without any framework integration.
### Usage
```bash
npx aeo.js
World
:
```
### Programmatic usage
Generate AEO files from source routes without a build — files land in `public/`:
```ts
import { generate } from 'aeo.js/remix';
await generate({ title: 'My Site', url: 'https://mysite.com' });
```
### Configuration
Pass any [AeoConfig](/reference/configuration/) options to `postBuild` or `generate`:
```ts
import { postBuild } from 'aeo.js/remix';
await postBuild({
title: 'My Site',
url: 'https://mysite.com',
description: 'A site optimized for AI discovery',
schema: { organization: { name: 'My Company' } },
});
```
---
# SvelteKit
Source: docs/frameworks/sveltekit.mdx
> Use aeo.js with SvelteKit.
import { Steps, Aside } from '@astrojs/starlight/components';
### Setup
If your site isn't optimized for AI engines, you're invisible to a growing share of users who never open a search results page. AEO is the new SEO.
### Widget
The Human/AI widget lets visitors toggle between the normal page and its AI-readable markdown version. Framework plugins inject it automatically — for Next.js or manual setups:
```tsx
'use client';
import { useEffect } from 'react';
export function AeoWidgetLoader() {
useEffect(() => {
import('aeo.js/widget').then(({ AeoWidget }) => {
new AeoWidget({
config: {
title: 'My Site',
url: 'https://mysite.com',
widget: { enabled: true, position: 'bottom-right' },
},
});
});
}, []);
return null;
}
```
React and Vue wrapper components are also available:
--- # API Reference Source: docs/reference/api.mdx > Programmatic API for aeo.js. ### Core #### `defineConfig(config)` Type-safe configuration helper: ```ts import { defineConfig } from 'aeo.js'; export default defineConfig({ title: 'My Site', url: 'https://mysite.com', }); ``` #### `generateAll(config)` Generate all AEO files programmatically: ```ts import { generateAll } from 'aeo.js'; await generateAll({ title: 'My Site', url: 'https://mysite.com', outDir: 'dist', }); ``` #### `resolveConfig(config)` Resolve a partial config into a fully resolved config with defaults: ```ts import { resolveConfig } from 'aeo.js'; const resolved = resolveConfig({ title: 'My Site' }); // All fields now have defaults ``` The examples below use `resolvedConfig` for a config object returned by `resolveConfig`. #### `validateConfig(config)` Validate a config object and return any errors: ```ts import { validateConfig } from 'aeo.js'; const errors = validateConfig(config); ``` #### `detectFramework()` Auto-detect the framework used in the current project: ```ts import { detectFramework } from 'aeo.js'; const info = detectFramework(); // { framework: 'next', contentDir: 'pages', outDir: '.next' } ``` ### HTML Extraction #### `htmlToMarkdown(html, pagePath, config)` Convert HTML to clean markdown: ```ts import { htmlToMarkdown } from 'aeo.js'; const md = htmlToMarkdown( '