Skip to content
npm downloadsGitHub stars

TanStack Start

  1. Install the package:

    Terminal window
    npm install aeo.js
  2. 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' }))\""
    }
    }
  3. Build your app:

    Terminal window
    npm run build

The TanStack Start plugin:

  • Scans src/routes (falling back to app/routes) using the TanStack Router file 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

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 }} />

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' });

Pass any AeoConfig 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' } },
});