---
title: "Nuxt Content"
description: "Integrating Nuxt SEO with Nuxt Content."
canonical_url: "https://nuxtseo.com/docs/nuxt-seo/guides/nuxt-content"
last_updated: "2026-05-20T04:04:38.905Z"
---

<callout icon="i-heroicons-document-text" to="/tools/html-to-markdown">

**Migrating content?** Use our [HTML to Markdown Converter](/tools/html-to-markdown) to convert existing pages to Nuxt Content format.

</callout>

## Introduction

Most Nuxt SEO modules integrate with Nuxt Content by default.

- Nuxt Robots: `robots` ([docs](/docs/robots/guides/content))
- Nuxt Sitemap: `sitemap` ([docs](/docs/sitemap/guides/content))
- Nuxt OG Image: `ogImage` ([docs](/docs/og-image/integrations/content))
- Nuxt Schema.org: `schemaOrg` ([docs](/docs/schema-org/guides/content))
- Nuxt Link Checker: Uses content APIs to check links

<callout icon="i-heroicons-information-circle">

The Nuxt Content setup is identical whether you installed `@nuxtjs/seo` or the modules individually. Each module provides its own `defineXxxSchema()` function that you compose in your collection schema. You only need to include schemas for the modules you use.

</callout>

For Nuxt Content v3 you need to add each module's schema to your collection using their `defineXxxSchema()` functions.

For Nuxt Content v2, please see the individual module documentation for how to configure them.

## Setup Nuxt Content v3

Add each module's schema field to your collection using `defineXxxSchema()` from the relevant module.

```ts [content.config.ts]
import { defineCollection, defineContentConfig } from '@nuxt/content'
import { defineRobotsSchema } from '@nuxtjs/robots/content'
import { defineSitemapSchema } from '@nuxtjs/sitemap/content'
import { defineOgImageSchema } from 'nuxt-og-image/content'
import { defineSchemaOrgSchema } from 'nuxt-schema-org/content'
import { z } from 'zod'

export default defineContentConfig({
  collections: {
    content: defineCollection({
      type: 'page',
      source: '**/*.md',
      schema: z.object({
        robots: defineRobotsSchema(),
        sitemap: defineSitemapSchema(),
        ogImage: defineOgImageSchema(),
        schemaOrg: defineSchemaOrgSchema(),
      }),
    }),
  },
})
```

You only need to include the schemas for the modules you use. For example, if you only need sitemap and robots support, include those two:

```ts [content.config.ts]
import { defineCollection, defineContentConfig } from '@nuxt/content'
import { defineRobotsSchema } from '@nuxtjs/robots/content'
import { defineSitemapSchema } from '@nuxtjs/sitemap/content'
import { z } from 'zod'

export default defineContentConfig({
  collections: {
    content: defineCollection({
      type: 'page',
      source: '**/*.md',
      schema: z.object({
        robots: defineRobotsSchema(),
        sitemap: defineSitemapSchema(),
      }),
    }),
  },
})
```

<warning>

The `asSeoCollection()` wrapper is deprecated. Use the individual `defineXxxSchema()` functions shown above instead.

</warning>

To ensure the tags gets rendered you need to ensure you're using the SEO composable.

```vue [[...slug].vue]
<script setup lang="ts">
const route = useRoute()
const { data: page } = await useAsyncData(`page-${route.path}`, () => {
  return queryCollection('content').path(route.path).first()
})
if (page.value?.ogImage?.component) {
  defineOgImage({ component: page.value.ogImage.component, ...page.value.ogImage.props }) // <-- Nuxt OG Image
}
// Ensure the schema.org is rendered
useSchemaOrg(page.value.schemaOrg || []) // <-- Nuxt Schema.org
useSeoMeta(page.value.seo || {}) // <-- useSeoMeta
</script>
```

<warning>

Due to Nuxt Content v3 limitations, you must load `@nuxtjs/seo` before `@nuxt/content` in your modules array.

</warning>

```ts
export default defineNuxtConfig({
  modules: [
    '@nuxtjs/seo',
    '@nuxt/content' // <-- Must be after @nuxtjs/seo
  ]
})
```

## Usage

For the full options available for each module i.e. for the schemas specific to each of the different SEO modules, please see the individual module documentation.

E.g.: The schema of the [OG Image](/docs/og-image/integrations/content) module (as provided by `defineOgImageSchema()` - see above) defines 3 properties:

- `component` ⇒ corresponding to a real component name as implemented within the `components/OgImage` folder.
- `props` ⇒ holding the values of properties defined in the aforementioned component.
- `url` ⇒ providing the address of an already existing (og compatible) image file, which is to be used as-is instead of an image generated on the fly by the module.

```md [content/index.md]
---
title: "Hello World Page"
description: "The purpose of this page is to say 'hello!' to the world."
ogImage:
  component: HelloWorld
  props:
    title: "Hello World"
    description: "This is a description"
sitemap:
  lastmod: 2025-01-01
robots: index, nofollow
schemaOrg:
  - "@type": "BlogPosting"
    headline: "How to Use Our Product"
    author:
      type: "Person"
      name: "Jane Smith"
    datePublished: "2023-10-01"
---

# {{$doc.title}}

{{ $doc.description}}
```

Using the following (example) og-image template "Simple.takumi" and the YAML data defined above

```vue [components/OgImage/Simple.takumi.vue]
<script setup lang="ts">
const props = defineProps({
  title: { type: String, required: false, default: 'title' },
  description: { type: String, required: false },
})
</script>

<template>
  <div style="background-color: darkgoldenrod;display: flex; flex-direction: column; font-size: 2rem; padding: 3rem; color: white">
    <h1 style="font-size: 4rem; font-weight: 800;">
      {{ title }}
    </h1>
    <h2 v-if="description" style="display: block;">
      {{ description }}
    </h2>
  </div>
</template>
```

You may now choose how to generate the og-image for any given content page:

- Using the frontmatter properties `title` and `description`, since those properties are defined by default in the schema of every "page" collection - see [Standard Collection Fields](https://content.nuxt.com/docs/collections/types#schema-overrides) for the **page** collection type)
```vue [[...slug].vue]
<script lang="ts" setup>
const route = useRoute()
const { data: page } = await useAsyncData(`page-${route.path}`, () => {
  return queryCollection('content').path(route.path).first()
})

const [ogImagePath] = defineOgImage('Simple.takumi', { title: () => page.value?.title, description: () => page.value?.description })

useSeoMeta({
  title: page.value.title,
  description: page.value.description,
  ogImage: ogImagePath,
  // ...
})
</script>
//...
```


or
- Using the `ogImage` frontmatter property and its attributes `component` and `props````vue [[...slug].vue]
<script lang="ts" setup>
const route = useRoute()
const { data: page } = await useAsyncData(`page-${route.path}`, () => {
  return queryCollection('content').path(route.path).first()
})

const [ogImagePath] = defineOgImage(page.value.ogImage.component, page.value.ogImage.props)

useSeoMeta({
  title: page.value.title,
  description: page.value.description,
  ogImage: ogImagePath,
  // ...
})
</script>
//...
```


Note: this would require that **all** content pages provide the adequate frontmatter data
or
- any combining (failover) strategy like
```vue [[...slug].vue]
<script lang="ts" setup>
// ...
const component = page.value?.ogImage?.component || 'Simple.takumi'
const props = page.value?.ogImage?.props || { title: page.value?.title, description: page.value?.description }
const [ogImagePath] = defineOgImage(component, props)
// ...
</script>
```

Note: Consider to differentiate the **multiple/different** usages and requirements of such **description** fields on purpose:

```html
<head>
  <meta name="description" content="...">
  <meta property="og:description" content="...">
  <meta name="twitter:description" content="...">
  <meta property="og:image:alt" content="...">
  <!-- ... -->
</head>
```
