---
title: "Nitro Hooks"
description: "Nitro runtime hooks for modifying markdown output."
canonical_url: "https://nuxtseo.com/docs/ai-ready/nitro-api/nitro-hooks"
last_updated: "2026-05-06T18:45:42.121Z"
---

## `'ai-ready:page:markdown'`

**Type:** `(ctx: MarkdownContext) => void | Promise<void>`

Called during runtime HTML→markdown conversion. Modify markdown before response.

```ts [server/plugins/markdown-footer.ts]
export default defineNitroPlugin((nitroApp) => {
  nitroApp.hooks.hook('ai-ready:page:markdown', (ctx) => {
    ctx.markdown += '\n\n---\n*Generated with mdream*'
  })
})
```

**MarkdownContext:**

<table>
<thead>
  <tr>
    <th>
      Property
    </th>
    
    <th>
      Type
    </th>
    
    <th>
      Description
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        html
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      Original HTML
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        markdown
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      Generated markdown (modify this)
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        route
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      The route the module is processing
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        title
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      Page title
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        description
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      Page description
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        isPrerender
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      Whether during prerendering
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        event
      </code>
    </td>
    
    <td>
      <code>
        H3Event
      </code>
    </td>
    
    <td>
      H3 event object
    </td>
  </tr>
</tbody>
</table>

## `'ai-ready:mdreamConfig'`

**Type:** `(config: MdreamOptions) => void | Promise<void>`

Called before HTML→markdown conversion. Modify mdream options per-request.

```ts [server/plugins/mdream-config.ts]
export default defineNitroPlugin((nitroApp) => {
  nitroApp.hooks.hook('ai-ready:mdreamConfig', (config) => {
    // Check origin from config.origin if available
    if (config.origin?.includes('/blog/'))
      config.ignoreElements = [...(config.ignoreElements || []), '.author-bio']
  })
})
```

Add custom plugins:

```ts [server/plugins/custom-mdream.ts]
export default defineNitroPlugin((nitroApp) => {
  nitroApp.hooks.hook('ai-ready:mdreamConfig', (config) => {
    config.plugins = config.plugins || []
    config.plugins.push(myCustomPlugin())
  })
})
```

## `'ai-ready:page:indexed'`

**Type:** `(ctx: PageIndexedContext) => void | Promise<void>`

Called when a page is indexed at runtime. Use this to sync with external systems like vector databases or search indexes.

```ts [server/plugins/embeddings.ts]
export default defineNitroPlugin((nitroApp) => {
  nitroApp.hooks.hook('ai-ready:page:indexed', async (ctx) => {
    // Generate embeddings for new pages
    if (!ctx.isUpdate) {
      await generateEmbeddings(ctx.route, ctx.markdown)
    }
    // Or always update
    await updateSearchIndex(ctx)
  })
})
```

**PageIndexedContext:**

<table>
<thead>
  <tr>
    <th>
      Property
    </th>
    
    <th>
      Type
    </th>
    
    <th>
      Description
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        route
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      Page route
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        title
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      Page title
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        description
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      Page description
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        headings
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      Pipe-delimited headings (e.g., <code>
        h1:Title|h2:Subtitle
      </code>
      
      )
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        keywords
      </code>
    </td>
    
    <td>
      <code>
        string[]
      </code>
    </td>
    
    <td>
      Extracted keywords from content
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        markdown
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      Full markdown content
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        updatedAt
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      ISO timestamp
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        isUpdate
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
      
       if re-indexing existing page
    </td>
  </tr>
</tbody>
</table>

## Manual Indexing Utils

For custom indexing logic, use the exported utilities:

```ts [server/api/reindex.post.ts]
import { indexPage, indexPageByRoute } from '#ai-ready'

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

  // Option 1: Index with HTML you already have
  const html = await fetchHtmlSomehow(path)
  const result = await indexPage(path, html, { force: true })

  // Option 2: Fetch and index in one call
  const result = await indexPageByRoute(path, event, { force: true })

  return result
})
```

**Options:**

<table>
<thead>
  <tr>
    <th>
      Option
    </th>
    
    <th>
      Type
    </th>
    
    <th>
      Description
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        ttl
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      Override config TTL
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        force
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      Re-index even if fresh
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        skipHook
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      Don't call <code>
        ai-ready:page:indexed
      </code>
    </td>
  </tr>
</tbody>
</table>
