# 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 7 contributors have shaped aeo.js. Thank you. - [rubenmarcus43 commits](https://github.com/rubenmarcus) - [KimHyeongRae07 commits](https://github.com/KimHyeongRae0) - [FabioRocha2313 commits](https://github.com/FabioRocha231) - [ribeiroevandro2 commits](https://github.com/ribeiroevandro) - [mhanelia2 commits](https://github.com/mhanelia) - [olucasleitedev1 commit](https://github.com/olucasleitedev) - [HusseinAdeiza1 commit](https://github.com/HusseinAdeiza) Want to join them?[Open a PR](https://github.com/rubenmarcus/aeo.js). Built by[rubenmarcus](https://rubenmarcus.dev) --- # 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 MCP Server](/features/mcp/) Built by[rubenmarcus](https://rubenmarcus.dev) --- # 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 [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; } ### check — audit any live site [Section titled “check — audit any live site”](#check-url--audit-any-live-site) Pass a URL (or bare domain) to audit a deployed site** instead of your local setup — no install, no config: Terminal window npx aeo.js check mysite.com npx aeo.js check https://mysite.com --json ** This fetches the site’s AEO surface (robots.txt, llms.txt, llms-full.txt, sitemap.xml, ai-index.json), crawls the homepage plus up to 10 inner pages, and reports: the same 5-category GEO readiness score (0–100) used by the local audit and [check.aeojs.org](https://check.aeojs.org) - which of 23 known AI crawlers (GPTBot, ClaudeBot, PerplexityBot, Google-Extended, …) can access the site - average content citability across crawled pages - the top fixes, ranked Works on any site — yours, a competitor’s, or one you’re evaluating. Requires Node 18+. Exits 1 if the target is invalid or unreachable. ### 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"> report also accepts a live URL, adding platform hints (including Claude and Gemini, driven by the site’s actual robots.txt) and per-page citability to the remote scan: Terminal window npx aeo.js report mysite.com npx aeo.js report mysite.com --json > scan.json scan.json"> ## Options [Section titled “Options”](#options) Flag Description --out Output directory (default: auto-detected) --url Site URL --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/rubenmarcus/aeo.js/blob/main/docs/cli.md). [Previous Widget](/features/widget/)[Next Schema & Open Graph](/features/schema-og/) Built by[rubenmarcus](https://rubenmarcus.dev) --- # 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: { "version": "1.0", "entries": [{ "id": "…", "url": "https://mysite.com/tools/roi", "title": "ROI Calculator", "content": "Full page content in markdown...", "description": "Page description", "keywords": ["keyword1", "keyword2"], "metadata": { "tags": ["calculator", "template"] } }] } Optional PageEntry.tags values are copied into each entry’s metadata.tags for downstream tools. They are informational only and do not change scoring. ## 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://rubenmarcus.dev) --- # 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/rubenmarcus/aeo.js/blob/main/docs/json-ld.md). [Previous Schema & Open Graph](/features/schema-og/)[Next Audit & Citability](/features/audit/) Built by[rubenmarcus](https://rubenmarcus.dev) --- # MCP Server URL: https://aeojs.org/features/mcp > Let AI agents audit sites and generate AEO files via the Model Context Protocol. ## MCP Server aeo.js ships an [MCP](https://modelcontextprotocol.io) server so AI agents — Claude Code, Claude Desktop, Cursor, or anything MCP-compatible — can audit sites and generate AEO files conversationally. ## Setup [Section titled “Setup”](#setup) No install needed. Add to your MCP client config: - { "mcpServers": { "aeo": { "command": "npx", "args": ["-y", "aeo.js", "mcp"] } } } For Claude Code: Terminal window claude mcp add aeo -- npx -y aeo.js mcp ## Tools [Section titled “Tools”](#tools) ### audit_url [Section titled “audit_url”](#audit_url) Audit any live site for AI answer-engine readiness. Crawls the AEO surface (robots.txt, llms.txt, sitemap.xml, ai-index.json) plus the homepage and up to 10 inner pages, and returns the 0–100 GEO readiness score, the AI crawler access matrix, citability scores, and top fixes. > “Audit example.com and tell me why ChatGPT can’t see it.” ### score_citability [Section titled “score_citability”](#score_citability) Score draft content (markdown or plain text) for AI citability — how likely answer engines are to extract and cite it. Returns a 0–100 score across four dimensions with concrete hints. Useful while writing: ask your agent to score and revise a page until it’s citable. > “Score this blog post for AI citability and rewrite the weakest sections.” ### generate_aeo_files [Section titled “generate_aeo_files”](#generate_aeo_files) Generate the full AEO file set (robots.txt, llms.txt, llms-full.txt, sitemap.xml, ai-index.json, schema.json, per-page markdown) into the current project’s output directory. > “Generate AEO files for this project, title ‘My Site’, url [https://mysite.com](https://mysite.com).” ## Notes [Section titled “Notes”](#notes) The server speaks newline-delimited JSON-RPC over stdio; logs go to stderr. - audit_url requires network access; the other tools work offline. - Requires Node 18+. [Previous Audit & Citability](/features/audit/)[Next Configuration](/reference/configuration/) Built by[rubenmarcus](https://rubenmarcus.dev) --- # 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://rubenmarcus.dev) --- # 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://rubenmarcus.dev) --- # 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 TanStack Start](/frameworks/tanstack-start/)[Next Docusaurus](/frameworks/docusaurus/) Built by[rubenmarcus](https://rubenmarcus.dev) --- # 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://rubenmarcus.dev) --- # Docusaurus URL: https://aeojs.org/frameworks/docusaurus > Use aeo.js with Docusaurus documentation sites. ## Docusaurus ## Setup [Section titled “Setup”](#setup) - Install the package: Terminal window npm install aeo.js ** - Add the plugin to your docusaurus.config.js: docusaurus.config.js module.exports = { plugins: [['aeo.js/docusaurus', { url: 'https://mysite.com', title: 'My Docs' }],], }; - Build your site: Terminal window npm run build ## How it works [Section titled “How it works”](#how-it-works) The Docusaurus plugin uses two lifecycle hooks: - postBuild** — after docusaurus build, it scans the generated HTML in the build output for titles, descriptions, and full text content, then writes all AEO files (robots.txt, llms.txt, sitemap.xml, ai-index.json, …) alongside the static output - **injectHtmlTags** — injects the AEO widget on every page url, title, and description default to your Docusaurus siteConfig (url, title, tagline) when omitted, and baseUrl is folded into the generated URLs so project sub-path sites (e.g. https://user.github.io/project/) resolve correctly. Note Generation runs during docusaurus build only — the postBuild hook does not run under docusaurus start (the dev server), by Docusaurus design. ## Configuration [Section titled “Configuration”](#configuration) Explicit plugin options always override the values inferred from siteConfig: plugins: [['aeo.js/docusaurus', { url: 'https://mysite.com', title: 'My Docs', description: 'Documentation optimized for AI discovery', schema: { organization: { name: 'My Company' } }, },],], See the [configuration reference](/reference/configuration/) for all available options. [Previous Angular](/frameworks/angular/)[Next Eleventy](/frameworks/eleventy/) Built by[rubenmarcus](https://rubenmarcus.dev) --- # Eleventy URL: https://aeojs.org/frameworks/eleventy > Use aeo.js with Eleventy (11ty) static sites. ## Eleventy ## Setup [Section titled “Setup”](#setup) - Install the package: Terminal window npm install aeo.js ** - Add the plugin in your eleventy.config.js: const aeo = require('aeo.js/eleventy'); module.exports = function (eleventyConfig) { eleventyConfig.addPlugin(aeo, { url: 'https://mysite.com', title: 'My Site', }); }; - Build your site: Terminal window npx @11ty/eleventy Caution Requires Eleventy 2.0 or newer** — the plugin uses the eleventy.after event’s results API and the directories argument. ## How it works [Section titled “How it works”](#how-it-works) The Eleventy plugin: - Registers a transform that injects the AEO widget into every rendered .html page - Listens for the eleventy.after event and reads Eleventy’s build results — each page’s URL and rendered HTML — to extract titles, descriptions, and full text content - Normalizes Eleventy’s trailing-slash URLs (/about/ → /about) and skips non-HTML output and 404 pages - Generates all AEO files (robots.txt, llms.txt, sitemap.xml, ai-index.json, …) into your Eleventy output directory (_site by default) ## Configuration [Section titled “Configuration”](#configuration) Pass any [AeoConfig](/reference/configuration/) options as the second argument to addPlugin. Add pages that Eleventy doesn’t emit as HTML via pages — your entries always win over auto-discovered ones: eleventyConfig.addPlugin(aeo, { url: 'https://mysite.com', title: 'My Site', description: 'A site optimized for AI discovery', schema: { organization: { name: 'My Company' } }, }); If your output directory isn’t the default _site, either pass outDir or rely on the directories value Eleventy reports — the plugin reads it automatically. [Previous Docusaurus](/frameworks/docusaurus/)[Next VitePress](/frameworks/vitepress/) Built by[rubenmarcus](https://rubenmarcus.dev) --- # 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://rubenmarcus.dev) --- # 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://rubenmarcus.dev) --- # 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://rubenmarcus.dev) --- # 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 TanStack Start](/frameworks/tanstack-start/) Built by[rubenmarcus](https://rubenmarcus.dev) --- # TanStack Start URL: https://aeojs.org/frameworks/tanstack-start > Use aeo.js with TanStack Start and TanStack Router. ## TanStack Start ## 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/tanstack-start').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 TanStack Start plugin: - Scans src/routes (falling back to app/routes) using the [TanStack Router file conventions](https://tanstack.com/router/latest/docs/routing/file-naming-conventions): flat (.) and directory nesting, index tokens, route.tsx layout tokens, pathless _layout segments, and trailing-underscore un-nesting - Skips routes that can’t be enumerated statically — dynamic params ($postId), splats ($), the __root layout, and -prefixed files - Scans prerendered HTML from the build output (.output/public, dist/client, or dist) for titles, descriptions, and full text content - Generates all AEO files (robots.txt, llms.txt, sitemap.xml, ai-index.json, …) alongside the static assets so they’re served from the site root Tip Dynamic routes are skipped because they can’t be listed at build time. Add them explicitly via the [pages](/reference/configuration/) option — your entries always win over auto-discovered ones. ## Widget [Section titled “Widget”](#widget) TanStack Start renders HTML on the server, so add the widget script once in your root document (src/routes/__root.tsx): src/routes/__root.tsx import { getWidgetScript } from 'aeo.js/tanstack-start'; const widgetScript = getWidgetScript({ title: 'My Site', url: 'https://mysite.com', }); // In your root document body, before </body>: <div dangerouslySetInnerHTML={{ __html: widgetScript }} /> : "> ## Programmatic usage [Section titled “Programmatic usage”](#programmatic-usage) No build output yet? Generate AEO files from source routes only — files land in public/: import { generate } from 'aeo.js/tanstack-start'; 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/tanstack-start'; await postBuild({ title: 'My Site', url: 'https://mysite.com', description: 'A site optimized for AI discovery', schema: { organization: { name: 'My Company' } }, }); [Previous SvelteKit](/frameworks/sveltekit/)[Next Angular](/frameworks/angular/) Built by[rubenmarcus](https://rubenmarcus.dev) --- # 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/rubenmarcus/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://rubenmarcus.dev) --- # 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://rubenmarcus.dev) --- # VitePress URL: https://aeojs.org/frameworks/vitepress > Use aeo.js with VitePress documentation sites. ## VitePress ## Setup [Section titled “Setup”](#setup) - Install the package: Terminal window npm install aeo.js ** - Wrap your config in .vitepress/config.ts: import { defineConfig } from 'vitepress'; import { withAeo } from 'aeo.js/vitepress'; export default defineConfig( withAeo( { title: 'My Docs', description: 'My documentation site', // …the rest of your VitePress config… }, { url: 'https://mysite.com' } ) ); - Build your site: Terminal window npm run docs:build ## How it works [Section titled “How it works”](#how-it-works) withAeo(config, options) wraps your VitePress config and composes two build hooks: - transformHtml** — runs per page during the build. It collects each page’s URL, title, description, and rendered text content, and injects the AEO widget before </body>. - **buildEnd** — after the build, generates all AEO files (robots.txt, llms.txt, sitemap.xml, ai-index.json, …) into the VitePress output directory. url, title, and description default from your VitePress site config (and sitemap.hostname) when omitted. Tip Any transformHtml or buildEnd hooks you already define are preserved — they run first, then aeo.js adds its behavior. These hooks only run during vitepress build, not the dev server. ## Configuration [Section titled “Configuration”](#configuration) Pass any [AeoConfig](/reference/configuration/) options as the second argument. Add pages VitePress doesn’t emit via pages — your entries always win: withAeo(config, { url: 'https://mysite.com', title: 'My Docs', schema: { organization: { name: 'My Company' } }, }); [Previous Eleventy](/frameworks/eleventy/)[Next Webpack](/frameworks/webpack/) Built by[rubenmarcus](https://rubenmarcus.dev) --- # 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 VitePress](/frameworks/vitepress/)[Next Vanilla JS / Static HTML](/frameworks/vanilla/) Built by[rubenmarcus](https://rubenmarcus.dev) --- # 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://rubenmarcus.dev) --- # 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://rubenmarcus.dev) --- # 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://rubenmarcus.dev) --- # 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/) **InstallAuditGenerateConfig $▌ 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/mhanelia)[](https://github.com/olucasleitedev)[](https://github.com/HusseinAdeiza) [View all 7 contributors →](/contributors/) Built by[rubenmarcus](https://rubenmarcus.dev) --- # 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

World

', '/', { 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('

Hello world

'); 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 `. A schema value containing `` (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: ```ts // lib/serialize-json-ld.ts export function serializeJsonForHtml(value: unknown): string { return JSON.stringify(value) .replace(//g, '\\u003E') .replace(/&/g, '\\u0026') .replace(/\u2028/g, '\\u2028') .replace(/\u2029/g, '\\u2029'); } ``` ### FAQ Page ```ts 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 ```ts 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 ```ts 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' }, }, }; ``` ### Product ```ts 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 ```ts 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 ```tsx import { serializeJsonForHtml } from '@/lib/serialize-json-ld'; ``` ```svelte ``` ### 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 --- # MCP Server Source: features/mcp.mdx > Let AI agents audit sites and generate AEO files via the Model Context Protocol. aeo.js ships an [MCP](https://modelcontextprotocol.io) server so AI agents — Claude Code, Claude Desktop, Cursor, or anything MCP-compatible — can audit sites and generate AEO files conversationally. ### Setup No install needed. Add to your MCP client config: ```json { "mcpServers": { "aeo": { "command": "npx", "args": ["-y", "aeo.js", "mcp"] } } } ``` For Claude Code: ```bash claude mcp add aeo -- npx -y aeo.js mcp ``` ### Tools #### `audit_url` Audit any live site for AI answer-engine readiness. Crawls the AEO surface (robots.txt, llms.txt, sitemap.xml, ai-index.json) plus the homepage and up to 10 inner pages, and returns the 0–100 GEO readiness score, the AI crawler access matrix, citability scores, and top fixes. > "Audit example.com and tell me why ChatGPT can't see it." #### `score_citability` Score draft content (markdown or plain text) for AI citability — how likely answer engines are to extract and cite it. Returns a 0–100 score across four dimensions with concrete hints. Useful while writing: ask your agent to score and revise a page until it's citable. > "Score this blog post for AI citability and rewrite the weakest sections." #### `generate_aeo_files` Generate the full AEO file set (robots.txt, llms.txt, llms-full.txt, sitemap.xml, ai-index.json, schema.json, per-page markdown) into the current project's output directory. > "Generate AEO files for this project, title 'My Site', url https://mysite.com." ### Notes - The server speaks newline-delimited JSON-RPC over stdio; logs go to stderr. - `audit_url` requires network access; the other tools work offline. - Requires Node 18+. --- # Schema & Open Graph Source: features/schema-og.mdx > Auto-generated JSON-LD structured data and Open Graph meta tags. import { Aside } from '@astrojs/starlight/components'; aeo.js can generate JSON-LD structured data and Open Graph meta tags for your pages. ### JSON-LD Schema Enable schema generation in your config: ```js { 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 ` ``` ### Configuration ```js 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 | Default | Small | Icon | |---------|-------|------| | ![Default widget](/widget-default.webp) | ![Small widget](/widget-small.webp) | ![Icon widget](/widget-icon.webp) | | `size: 'default'` | `size: 'small'` | `size: 'icon-only'` | | Full labels with icons | Compact — ~30% smaller | Just icons, no labels | --- # Angular Source: frameworks/angular.mdx > Use aeo.js with Angular. import { Steps, Aside } from '@astrojs/starlight/components'; ### Setup 1. Install the package: ```bash npm install aeo.js ``` 2. Add a post-build step to your `package.json`: ```json { "scripts": { "postbuild": "node -e \"import('aeo.js/angular').then(m => m.postBuild({ title: 'My App', url: 'https://myapp.com' }))\"" } } ``` 3. Build your app: ```bash npm run build ``` ### How it works The Angular plugin: - Reads `angular.json` to auto-detect the output directory (`dist//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 You can also generate AEO files from source routes without building: ```ts import { generate } from 'aeo.js/angular'; await generate({ title: 'My App', url: 'https://myapp.com' }); ``` ### Configuration Pass any [AeoConfig](/reference/configuration/) options to `postBuild` or `generate`: ```ts import { postBuild } from 'aeo.js/angular'; await postBuild({ title: 'My App', url: 'https://myapp.com', generators: { robotsTxt: true, llmsTxt: true, schema: true, }, }); ``` --- # Astro Source: frameworks/astro.mdx > Use aeo.js with Astro. import { Steps } from '@astrojs/starlight/components'; ### Setup 1. Install the package: ```bash npm install aeo.js ``` 2. Add the integration to your Astro config: ```js // 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', }), ], }); ``` 3. Build your site: ```bash npm run build ``` ### 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 Pass any [AeoConfig](/reference/configuration/) options to the integration: ```js 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', }, }, }); ``` --- # Docusaurus Source: frameworks/docusaurus.mdx > Use aeo.js with Docusaurus documentation sites. import { Steps, Aside } from '@astrojs/starlight/components'; ### Setup 1. Install the package: ```bash npm install aeo.js ``` 2. Add the plugin to your `docusaurus.config.js`: ```js // docusaurus.config.js module.exports = { plugins: [ ['aeo.js/docusaurus', { url: 'https://mysite.com', title: 'My Docs' }], ], }; ``` 3. Build your site: ```bash npm run build ``` ### How it works The Docusaurus plugin uses two lifecycle hooks: - **`postBuild`** — after `docusaurus build`, it scans the generated HTML in the build output for titles, descriptions, and full text content, then writes all AEO files (robots.txt, llms.txt, sitemap.xml, ai-index.json, …) alongside the static output - **`injectHtmlTags`** — injects the AEO widget on every page `url`, `title`, and `description` default to your Docusaurus `siteConfig` (`url`, `title`, `tagline`) when omitted, and `baseUrl` is folded into the generated URLs so project sub-path sites (e.g. `https://user.github.io/project/`) resolve correctly. ### Configuration Explicit plugin options always override the values inferred from `siteConfig`: ```js plugins: [ [ 'aeo.js/docusaurus', { url: 'https://mysite.com', title: 'My Docs', description: 'Documentation optimized for AI discovery', schema: { organization: { name: 'My Company' } }, }, ], ], ``` See the [configuration reference](/reference/configuration/) for all available options. --- # Eleventy Source: frameworks/eleventy.mdx > Use aeo.js with Eleventy (11ty) static sites. import { Steps, Aside } from '@astrojs/starlight/components'; ### Setup 1. Install the package: ```bash npm install aeo.js ``` 2. Add the plugin in your `eleventy.config.js`: ```js const aeo = require('aeo.js/eleventy'); module.exports = function (eleventyConfig) { eleventyConfig.addPlugin(aeo, { url: 'https://mysite.com', title: 'My Site', }); }; ``` 3. Build your site: ```bash npx @11ty/eleventy ``` ### How it works The Eleventy plugin: - Registers a transform that injects the AEO widget into every rendered `.html` page - Listens for the `eleventy.after` event and reads Eleventy's build `results` — each page's URL and rendered HTML — to extract titles, descriptions, and full text content - Normalizes Eleventy's trailing-slash URLs (`/about/` → `/about`) and skips non-HTML output and 404 pages - Generates all AEO files (robots.txt, llms.txt, sitemap.xml, ai-index.json, …) into your Eleventy output directory (`_site` by default) ### Configuration Pass any [AeoConfig](/reference/configuration/) options as the second argument to `addPlugin`. Add pages that Eleventy doesn't emit as HTML via `pages` — your entries always win over auto-discovered ones: ```js eleventyConfig.addPlugin(aeo, { url: 'https://mysite.com', title: 'My Site', description: 'A site optimized for AI discovery', schema: { organization: { name: 'My Company' } }, }); ``` If your output directory isn't the default `_site`, either pass `outDir` or rely on the `directories` value Eleventy reports — the plugin reads it automatically. --- # Next.js Source: frameworks/nextjs.mdx > Use aeo.js with Next.js. import { Steps, Aside } from '@astrojs/starlight/components'; ### Setup 1. Install the package: ```bash npm install aeo.js ``` 2. Wrap your Next.js config with `withAeo`: ```js // 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', }, }); ``` 3. Add the post-build step to your `package.json`: ```json { "scripts": { "postbuild": "node -e \"import('aeo.js/next').then(m => m.postBuild({ title: 'My Site', url: 'https://mysite.com' }))\"" } } ``` ### Widget For Next.js, add the widget manually as a client component: ```tsx // 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; } ``` ### Configuration Pass any [AeoConfig](/reference/configuration/) options in the `aeo` key: ```js export default withAeo({ aeo: { title: 'My Site', url: 'https://mysite.com', generators: { robotsTxt: true, llmsTxt: true, schema: true, }, widget: { enabled: true, position: 'bottom-right', }, }, }); ``` --- # Nuxt Source: frameworks/nuxt.mdx > Use aeo.js with Nuxt 3. import { Steps } from '@astrojs/starlight/components'; ### Setup 1. Install the package: ```bash npm install aeo.js ``` 2. Add the module to your Nuxt config: ```ts // 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 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 `` and `` tags for AEO discoverability ### Configuration Pass any [AeoConfig](/reference/configuration/) options in the `aeo` key: ```ts 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', }, }, }); ``` --- # Remix Source: frameworks/remix.mdx > Use aeo.js with Remix and React Router 7. import { Steps, Aside } from '@astrojs/starlight/components'; ### Setup 1. Install the package: ```bash npm install aeo.js ``` 2. Add a post-build step to your `package.json`: ```json { "scripts": { "postbuild": "node -e \"import('aeo.js/remix').then(m => m.postBuild({ title: 'My Site', url: 'https://mysite.com' }))\"" } } ``` 3. Build your app: ```bash npm run build ``` ### 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 ### Widget Remix renders HTML on the server, so add the widget script to your root route once: ```tsx // app/root.tsx import { getWidgetScript } from 'aeo.js/remix'; const widgetScript = getWidgetScript({ title: 'My Site', url: 'https://mysite.com', }); // In your Layout component, before :
``` ### 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: frameworks/sveltekit.mdx > Use aeo.js with SvelteKit. import { Steps, Aside } from '@astrojs/starlight/components'; ### Setup 1. Install the package: ```bash npm install aeo.js ``` 2. Add a post-build step to your `package.json`: ```json { "scripts": { "postbuild": "node -e \"import('aeo.js/sveltekit').then(m => m.postBuild({ title: 'My Site', url: 'https://mysite.com' }))\"" } } ``` 3. Build your app: ```bash npm run build ``` ### 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 ### Programmatic usage Generate AEO files from source routes without a build — files land in `static/` so any adapter ships them: ```ts import { generate } from 'aeo.js/sveltekit'; 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/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, }); ``` --- # TanStack Start Source: frameworks/tanstack-start.mdx > Use aeo.js with TanStack Start and TanStack Router. import { Steps, Aside } from '@astrojs/starlight/components'; ### Setup 1. Install the package: ```bash npm install aeo.js ``` 2. Add a post-build step to your `package.json`: ```json { "scripts": { "postbuild": "node -e \"import('aeo.js/tanstack-start').then(m => m.postBuild({ title: 'My Site', url: 'https://mysite.com' }))\"" } } ``` 3. Build your app: ```bash npm run build ``` ### How it works The TanStack Start plugin: - Scans `src/routes` (falling back to `app/routes`) using the [TanStack Router file conventions](https://tanstack.com/router/latest/docs/routing/file-naming-conventions): flat (`.`) and directory nesting, `index` tokens, `route.tsx` layout tokens, pathless `_layout` segments, and trailing-underscore un-nesting - Skips routes that can't be enumerated statically — dynamic params (`$postId`), splats (`$`), the `__root` layout, and `-`prefixed files - Scans prerendered HTML from the build output (`.output/public`, `dist/client`, or `dist`) for titles, descriptions, and full text content - Generates all AEO files (robots.txt, llms.txt, sitemap.xml, ai-index.json, …) alongside the static assets so they're served from the site root ### Widget TanStack Start renders HTML on the server, so add the widget script once in your root document (`src/routes/__root.tsx`): ```tsx // src/routes/__root.tsx import { getWidgetScript } from 'aeo.js/tanstack-start'; const widgetScript = getWidgetScript({ title: 'My Site', url: 'https://mysite.com', }); // In your root document body, before :
``` ### Programmatic usage No build output yet? Generate AEO files from source routes only — files land in `public/`: ```ts import { generate } from 'aeo.js/tanstack-start'; 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/tanstack-start'; await postBuild({ title: 'My Site', url: 'https://mysite.com', description: 'A site optimized for AI discovery', schema: { organization: { name: 'My Company' } }, }); ``` --- # Vanilla JS / Static HTML Source: frameworks/vanilla.mdx > Use aeo.js with a plain HTML site, hand-rolled JS, or any static-site generator — no framework required. import { Tabs, TabItem, Aside } from '@astrojs/starlight/components'; aeo.js works on any project that produces static HTML. Use the CLI directly — no plugin, no module, no framework. ### Quick Start ```bash npx aeo.js generate --url https://mysite.com --title "My Site" --out public ``` ```bash 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. ### How aeo.js 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: ```js // 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 #### Hand-rolled HTML site ```jsonc // package.json { "scripts": { "build:aeo": "aeo.js generate --url https://mysite.com --title \"My Site\" --out ." } } ``` #### Eleventy / Hugo / Jekyll ```jsonc // 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) The CLI's `--out` flag covers built HTML; to also pull `.md` front-matter and bodies from a `contentDir`, use the programmatic API: ```js // 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 })); ``` ```jsonc // package.json { "scripts": { "build:aeo": "node scripts/aeo.mjs" } } ``` ### Adding the Widget ```html ``` Or install and bundle locally with `import { AeoWidget } from 'aeo.js/widget'`. ### CI Integration ```yaml - 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; } ``` ### 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/) --- # Vite Source: frameworks/vite.mdx > Use aeo.js with Vite (React, Vue, Svelte, etc.). import { Steps } from '@astrojs/starlight/components'; ### Setup 1. Install the package: ```bash npm install aeo.js ``` 2. Add the plugin to your Vite config: ```js // 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 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 Pass any [AeoConfig](/reference/configuration/) options to the plugin: ```js aeoVitePlugin({ title: 'My Site', url: 'https://mysite.com', widget: { position: 'top-right', theme: { background: 'rgba(0, 0, 0, 0.85)', accent: '#f59e0b', }, }, }); ``` --- # VitePress Source: frameworks/vitepress.mdx > Use aeo.js with VitePress documentation sites. import { Steps, Aside } from '@astrojs/starlight/components'; ### Setup 1. Install the package: ```bash npm install aeo.js ``` 2. Wrap your config in `.vitepress/config.ts`: ```ts import { defineConfig } from 'vitepress'; import { withAeo } from 'aeo.js/vitepress'; export default defineConfig( withAeo( { title: 'My Docs', description: 'My documentation site', // …the rest of your VitePress config… }, { url: 'https://mysite.com' } ) ); ``` 3. Build your site: ```bash npm run docs:build ``` ### How it works `withAeo(config, options)` wraps your VitePress config and composes two build hooks: - **`transformHtml`** — runs per page during the build. It collects each page's URL, title, description, and rendered text content, and injects the AEO widget before ``. - **`buildEnd`** — after the build, generates all AEO files (robots.txt, llms.txt, sitemap.xml, ai-index.json, …) into the VitePress output directory. `url`, `title`, and `description` default from your VitePress site config (and `sitemap.hostname`) when omitted. ### Configuration Pass any [AeoConfig](/reference/configuration/) options as the second argument. Add pages VitePress doesn't emit via `pages` — your entries always win: ```ts withAeo(config, { url: 'https://mysite.com', title: 'My Docs', schema: { organization: { name: 'My Company' } }, }); ``` --- # Webpack Source: frameworks/webpack.mdx > Use aeo.js with Webpack. import { Steps } from '@astrojs/starlight/components'; ### Setup 1. Install the package: ```bash npm install aeo.js ``` 2. Add the plugin to your Webpack config: ```js // 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 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 Pass any [AeoConfig](/reference/configuration/) options to the plugin: ```js new AeoWebpackPlugin({ title: 'My Site', url: 'https://mysite.com', widget: { enabled: true, position: 'bottom-right', }, }); ``` --- # Installation Source: getting-started/installation.mdx > Install aeo.js in your project. import { Tabs, TabItem } from '@astrojs/starlight/components'; ### Install the package ```bash npm install aeo.js ``` ```bash pnpm add aeo.js ``` ```bash yarn add aeo.js ``` ### Or use the CLI directly No installation needed — run it with `npx`: ```bash npx aeo.js generate ``` ### 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 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/) --- # Introduction Source: getting-started/introduction.mdx > What is aeo.js and why you need Answer Engine Optimization. import { Aside } from '@astrojs/starlight/components'; ![aeo.js in action](/example.webp) ### 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? 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 1. **Install** the package and add the plugin for your framework 2. **Build** your site as usual 3. **aeo.js** scans your output, extracts content, and generates all AEO files 4. The optional **widget** lets visitors toggle between human and AI views of your pages ### Scope: what aeo.js does and doesn't 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: 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 | 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` | --- # Quick Start Source: getting-started/quick-start.mdx > Get aeo.js running in under 2 minutes. import { Tabs, TabItem, Steps } from '@astrojs/starlight/components'; The fastest way to add AEO to your site depends on your framework. ### Astro 1. Install the package: ```bash npm install aeo.js ``` 2. Add the integration to your Astro config: ```js // 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', }), ], }); ``` 3. Build your site — all AEO files are generated automatically. ### Next.js 1. Install the package: ```bash npm install aeo.js ``` 2. Wrap your Next.js config: ```js // 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', }, }); ``` 3. Add the post-build step to `package.json`: ```json { "scripts": { "postbuild": "node -e \"import('aeo.js/next').then(m => m.postBuild({ title: 'My Site', url: 'https://mysite.com' }))\"" } } ``` ### Vite 1. Install the package: ```bash npm install aeo.js ``` 2. Add the plugin to your Vite config: ```js // 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) No framework integration needed: ```bash npx aeo.js generate --url https://mysite.com --title "My Site" ``` See [CLI docs](/features/cli/) for all commands and options. --- # aeo.js Source: index.mdx > Answer Engine Optimization for the modern web. Make your site discoverable by AI crawlers and LLMs. import { Tabs, TabItem } from '@astrojs/starlight/components'; import StickyNav from '../../components/StickyNav.astro'; import HomepageContributors from '../../components/HomepageContributors.astro'; ### Quick Start ```bash npm install aeo.js ``` ```js // 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', }), ], }); ``` ```js // 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`: ```json { "scripts": { "postbuild": "node -e \"import('aeo.js/next').then(m => m.postBuild({ title: 'My Site', url: 'https://mysite.com' }))\"" } } ``` ```js // 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', }), ], }); ``` ```ts // 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`: ```json { "scripts": { "postbuild": "node -e \"import('aeo.js/angular').then(m => m.postBuild({ title: 'My App', url: 'https://myapp.com' }))\"" } } ``` Or generate programmatically: ```ts import { generate } from 'aeo.js/angular'; await generate({ title: 'My App', url: 'https://myapp.com' }); ``` ```js // 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: ```bash # 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? 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
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 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: ```tsx import { AeoReactWidget } from 'aeo.js/react'; ``` ```vue ``` ### Configuration All frameworks use the same config shape — [see full reference](/reference/configuration/): ```ts 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 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

Free — no signup required. Powered by AEO Checker

--- # API Reference Source: 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( 'Hello

World

', '/', { url: 'https://mysite.com' } ); // Returns a markdown document with YAML frontmatter ``` #### `extractTextFromHtml(html)` Extract plain text from HTML: ```ts import { extractTextFromHtml } from 'aeo.js'; const text = extractTextFromHtml('

Hello world

'); ``` #### `extractTitle(html)` / `extractDescription(html)` Extract the title or meta description from an HTML document. #### `extractJsonLd(html)` Extract JSON-LD structured data from an HTML document. ### Schema #### `generateSchemaObjects(config)` Generate JSON-LD schema objects: ```ts import { generateSchemaObjects } from 'aeo.js'; const schemas = generateSchemaObjects(resolvedConfig); ``` #### `generateJsonLdScript(schemas)` Generate a `