Core Concepts

Runtime Indexing

Last updated by Harlan Wilton in chore: missing files.

By default, nuxt-ai-ready indexes pages during prerendering (nuxi generate). Runtime indexing enables on-demand indexing for SSR-only deployments or dynamic content.

Enable Runtime Indexing

nuxt.config.ts
export default defineNuxtConfig({
  aiReady: {
    runtimeIndexing: {
      enabled: true,
      storage: 'ai-ready',
      ttl: 3600 // re-index after 1 hour
    }
  }
})

How It Works

┌─────────────────────────────────────────────────────────────┐
│ 1. Server Start                                              │
│    storage-init plugin loads prerendered pages.json         │
│    into unstorage (if exists)                                │
├─────────────────────────────────────────────────────────────┤
│ 2. Page Visit                                                │
│    afterResponse hook triggers                               │
│    waitUntil() runs HTML→markdown in background              │
│    Page stored in unstorage                                  │
├─────────────────────────────────────────────────────────────┤
│ 3. Hook Fires                                                │
│    ai-ready:page:indexed called                              │
│    Integrate with embeddings, search, etc.                   │
├─────────────────────────────────────────────────────────────┤
│ 4. Data Access                                               │
│    getPages() / getPagesList() read from storage             │
│    MCP tools and llms.txt use indexed data                   │
└─────────────────────────────────────────────────────────────┘

Production Storage

Memory storage works for development but is ephemeral on serverless. Configure a persistent driver:

export default defineNuxtConfig({
  aiReady: {
    runtimeIndexing: { enabled: true }
  },
  nitro: {
    storage: {
      'ai-ready': {
        driver: 'cloudflare-kv-binding',
        binding: 'AI_READY_KV'
      }
    }
  }
})

See Nitro Storage for all drivers.

Sync with External Systems

Use the ai-ready:page:indexed hook to sync with vector databases, search indexes, or analytics:

server/plugins/embeddings.ts
export default defineNitroPlugin((nitro) => {
  nitro.hooks.hook('ai-ready:page:indexed', async (ctx) => {
    // Generate embeddings for vector search
    const embedding = await openai.embeddings.create({
      model: 'text-embedding-3-small',
      input: ctx.markdown
    })

    await vectorDb.upsert({
      id: ctx.route,
      vector: embedding.data[0].embedding,
      metadata: {
        title: ctx.title,
        description: ctx.description,
        route: ctx.route
      }
    })

    console.log(`Indexed ${ctx.route} (update: ${ctx.isUpdate})`)
  })
})

Manual Indexing

Trigger indexing from API routes or plugins:

server/api/reindex.post.ts
import { indexPageByRoute } from '#ai-ready'

export default defineEventHandler(async (event) => {
  const { paths } = await readBody(event)

  const results = await Promise.all(
    paths.map((path: string) =>
      indexPageByRoute(path, event, { force: true })
    )
  )

  return {
    indexed: results.filter(r => r.success && !r.skipped).length,
    skipped: results.filter(r => r.skipped).length,
    failed: results.filter(r => !r.success).length
  }
})

Warm Cache on Startup

Pre-index important pages when the server starts:

server/plugins/warm-pages.ts
import { indexPageByRoute } from '#ai-ready'

export default defineNitroPlugin(async () => {
  // Skip during prerender
  if (import.meta.prerender) return

  const criticalPages = ['/', '/docs', '/pricing', '/features']

  await Promise.all(
    criticalPages.map(route => indexPageByRoute(route))
  )

  console.log(`Warmed ${criticalPages.length} pages`)
})

TTL and Re-indexing

The ttl option controls how often pages are re-indexed:

TTL ValueBehavior
0Never re-index (index once per server lifetime)
3600Re-index if page was indexed >1 hour ago
86400Re-index daily
aiReady: {
  runtimeIndexing: {
    enabled: true,
    ttl: 3600 // Check freshness on each visit
  }
}

Force re-index regardless of TTL:

await indexPage('/about', html, { force: true })

Hybrid Mode

Combine prerendering with runtime indexing for best of both worlds:

  1. Prerender static pages during build
  2. Runtime index dynamic/SSR pages on first visit
nuxt.config.ts
export default defineNuxtConfig({
  aiReady: {
    runtimeIndexing: {
      enabled: true,
      ttl: 3600
    }
  },
  nitro: {
    prerender: {
      routes: ['/', '/about', '/docs'] // Static pages
    }
  }
})

Prerendered pages are loaded into storage on startup, and new SSR pages are added as visitors access them.

Did this page help you?