---
title: "Nuxt Content"
description: "Integrating Nuxt SEO with Nuxt Content."
canonical_url: "https://nuxtseo.com/docs/nuxt-seo/guides/nuxt-content"
last_updated: "2026-08-29T03:08:05.843Z"
---

::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.
::

## Introduction

Most Nuxt SEO modules integrate with Nuxt Content by default.

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

::note{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()`{lang="ts"} function that you compose in your collection schema. You only need to include schemas for the modules you use.
::

For Nuxt Content v3 you need to add each module's schema to your collection using their `defineXxxSchema()`{lang="ts"} 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()`{lang="ts"} 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(),
      }),
    }),
  },
})
```

::tip
If you installed the `@nuxtjs/seo` meta-module, you can import all four `defineXxxSchema()`{lang="ts"} functions from `@nuxtjs/seo/content` instead of each module, so you don't need the individual modules as direct dependencies.

```ts [content.config.ts]
import { defineOgImageSchema, defineRobotsSchema, defineSchemaOrgSchema, defineSitemapSchema } from '@nuxtjs/seo/content'
```
::

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()`{lang="ts"} wrapper is deprecated. Use the individual `defineXxxSchema()`{lang="ts"} functions shown above instead.
::

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.
::

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

## Setup comark-content

[comark-content](https://github.com/harlan-zw/harlan-nuxt/tree/main/packages/comark-content) is a Markdown-only content module. Nuxt SEO supports it first-party.

It needs no schema opt in. Declare the collection and write the frontmatter keys you want.

```ts [content.config.ts]
import { defineCollection, defineContentConfig } from '@harlan-zw/comark-content'

export default defineContentConfig({
  collections: {
    content: defineCollection({
      type: 'page',
      source: '**/*.md',
    }),
  },
})
```

```md [content/about.md]
---
title: About
robots: false
sitemap:
  priority: 0.8
schemaOrg:
  "@type": "AboutPage"
---

# About
```

Three differences from Nuxt Content v3:

- Every page collection is in the sitemap by default. Set `sitemap: false`{lang="ts"} on the collection to opt out.
- Only Markdown is supported. There are no YAML, JSON, or CSV sources.
- Nuxt Sitemap names its data source `@harlan-zw/comark-content:urls`, not `@nuxt/content@v3:urls`.

Per-module detail lives in each module's own guide, linked in [Introduction](#introduction).

## 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()`{lang="ts"} - 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 the module uses as-is instead of generating an image on the fly.

```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 the schema of every "page" collection defines those properties by default - 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 differentiating 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>
```