Core Concepts

Nuxt AI Ready can automatically track when page content was last updated by hashing markdown content and comparing it across builds. This helps AI agents assess content freshness when consuming your site's data.

How It Works

During prerender, the module determines timestamps using this priority:

  1. Manual meta tags (highest priority): Checks for timestamps in HTML meta tags:
    • <meta property="article:modified_time" content="...">
    • <meta name="last-modified" content="...">
    • <meta name="updated" content="...">
    • <meta property="og:updated_time" content="...">
    • <meta name="lastmod" content="...">
  2. Content hashing (fallback): If no meta tag found, hashes markdown content using SHA256:
    • New pages: Current build timestamp
    • Content changed: Current build timestamp
    • Content unchanged: Previous timestamp (preserved)

The updatedAt field is included in the llms.toon page-level export, making it available to AI agents via the bulk API.

Manual Timestamps via Meta Tags

If your pages include timestamps in meta tags, they'll be used as the source of truth:

<head>
  <meta property="article:modified_time" content="2024-01-15T10:30:00Z">
</head>

This is useful when:

  • Migrating from another system with existing timestamps
  • Manually maintaining update dates
  • Using a CMS that provides modification timestamps

Manual timestamps sync to the manifest, so they persist across builds even if the meta tag is later removed.

Enabling the Feature

Enable automatic timestamp tracking in your Nuxt config:

nuxt.config.ts
export default defineNuxtConfig({
  aiReady: {
    timestamps: {
      enabled: true
    }
  }
})

Persistent Storage

For timestamps to work correctly across deployments, the content hash manifest must persist between builds. By default, the manifest is stored at node_modules/.cache/nuxt-seo/ai-index/content-hashes.json.

This is critical: Without persistent storage, every build treats all pages as new, assigning fresh timestamps each time.

GitHub Actions

Use GitHub Actions caching to persist the manifest between builds:

your-github-workflow.yml
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      # Install...
      - name: Cache AI Ready content hashes
        uses: actions/cache@v4
        with:
          path: node_modules/.cache/nuxt-seo
          key: nuxt-seo-${{ github.ref_name }}
      # Build, etc..

Cloudflare Pages

Cloudflare Pages automatically persists the node_modules directory between builds, so no additional configuration is needed.

Vercel

Vercel automatically caches node_modules/.cache, so no additional configuration is needed.

Netlify

Use Netlify's cache plugin to persist the cache directory:

netlify.toml
[[plugins]]
package = "netlify-plugin-cache"

[plugins.inputs]
paths = [ "node_modules/.cache/nuxt-seo" ]

Custom Storage Path

You can customize where the manifest is stored:

nuxt.config.ts
export default defineNuxtConfig({
  aiReady: {
    timestamps: {
      enabled: true,
      manifestPath: '.cache/content-hashes.json' // Relative to output dir
    }
  }
})

Disabling Timestamps

If you don't need timestamp tracking, or your deployment environment doesn't support persistent storage:

nuxt.config.ts
export default defineNuxtConfig({
  aiReady: {
    timestamps: {
      enabled: false
    }
  }
})

When disabled, the updatedAt field is omitted from the TOON export.

Accessing Timestamps

Timestamps are available in the page-level TOON export (/llms.toon):

import { decode } from '@toon-format/toon'

const response = await fetch('/llms.toon')
const text = await response.text()
const data = decode(text)

data.pages.forEach((page) => {
  console.log(`${page.route} last updated: ${page.updatedAt}`)
  // Example: / last updated: 2025-01-15T10:30:45.123Z
})

Timestamps follow ISO 8601 format and represent when the content was last modified (not when the build occurred, unless the content changed in that build).

How Hashing Works

The module uses SHA256 to hash the full reassembled markdown content of each page. Even minor content changes (typo fixes, formatting) will generate a new hash and update the timestamp.

Route-based chunk IDs remain stable (unchanged by content edits), while content hashes track actual changes. This separation allows:

  • Stable vector embeddings IDs (route-based)
  • Accurate freshness tracking (content-based)

Sitemap Integration

When both timestamps and Nuxt Sitemap are enabled, the module automatically injects lastmod values into your sitemap.xml during prerender.

nuxt.config.ts
export default defineNuxtConfig({
  modules: ['nuxt-ai-ready', '@nuxtjs/sitemap'],
  aiReady: {
    timestamps: {
      enabled: true
    }
  }
})

The sitemap integration:

  • Hooks into the sitemap:resolved event during prerender
  • Reads timestamps from the content hash manifest
  • Sets lastmod on matching sitemap URLs

Example output in sitemap.xml:

<url>
  <loc>https://example.com/docs/getting-started</loc>
  <lastmod>2025-01-15T10:30:00Z</lastmod>
</url>

This ensures search engines see accurate content freshness dates without manual configuration.

First Build Behavior

On the first build (or when the manifest doesn't exist), all pages receive the current build timestamp for both updatedAt and firstSeenAt (internal field).

Subsequent builds preserve firstSeenAt but update updatedAt only when content changes.

Did this page help you?