@nuxt/content always shows the first page but only in production mode

Hello All! Complete newbie to nuxt here. Trying to create a basic blog. Its now working in development but failing when building. When running the static build, it always shows the first blog item regardless of URL! The first warning sign which I did not notice before, is this one when starting the dev server:
WARN Module @nuxt/content is disabled due to incompatibility issues:
- [nuxt] Nuxt version ^3.0.0-rc.3 is required but currently using 4.0.3
WARN Module @nuxt/content is disabled due to incompatibility issues:
- [nuxt] Nuxt version ^3.0.0-rc.3 is required but currently using 4.0.3
Should I worry about that? Cause the module seems to work Here is the setup I am using. I have simplified it as much as possible, in an effort to find the cause of the issue.
<script setup lang="ts">
// app/pages/blog/[...blog].vue
const route = useRoute()
const { data:page } = await useAsyncData(route.path, () => {
return queryCollection('blog').path(route.path).first()
})
</script>

<template>
<div class="blog-wrapper">
<ContentRenderer v-if="page" :value="page" class="blog-content"/>
</div>
</template>
<script setup lang="ts">
// app/pages/blog/[...blog].vue
const route = useRoute()
const { data:page } = await useAsyncData(route.path, () => {
return queryCollection('blog').path(route.path).first()
})
</script>

<template>
<div class="blog-wrapper">
<ContentRenderer v-if="page" :value="page" class="blog-content"/>
</div>
</template>
// part of nuxt.config.ts
content: {
watch: {
enabled: false
},
},
nitro: {
static: true,
},
ssr: true,
// part of nuxt.config.ts
content: {
watch: {
enabled: false
},
},
nitro: {
static: true,
},
ssr: true,
// content.config.ts
import {defineContentConfig, defineCollection, z} from "@nuxt/content";

export default defineContentConfig({
collections: {
blog: defineCollection({
type: 'page',
source: 'blog/*.md',
schema: z.object({
title: z.string().min(1, 'Title is required'),
description: z.string().optional(),
date: z.date(),
tags: z.array(z.string()).optional(),
})
})
}
})
// content.config.ts
import {defineContentConfig, defineCollection, z} from "@nuxt/content";

export default defineContentConfig({
collections: {
blog: defineCollection({
type: 'page',
source: 'blog/*.md',
schema: z.object({
title: z.string().min(1, 'Title is required'),
description: z.string().optional(),
date: z.date(),
tags: z.array(z.string()).optional(),
})
})
}
})
I did find something of a clue perhaps but I am not sure of its significance; in network tab I see a request to e.g. /blog/using-material-design/_payload* but then in the response I see a value like blog/blog/moving-to-nuxt.md on key 5. Notice the double blog/
12 Replies
kapa.ai
kapa.ai3w ago
Hi @holkerveen. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave: ---------------------- The warning you are seeing:
WARN Module @nuxt/content is disabled due to incompatibility issues:
- [nuxt] Nuxt version ^3.0.0-rc.3 is required but currently using 4.0.3
WARN Module @nuxt/content is disabled due to incompatibility issues:
- [nuxt] Nuxt version ^3.0.0-rc.3 is required but currently using 4.0.3
is significant. According to the official documentation, @nuxt/content v3 is designed for Nuxt 3.x, and there is no mention of compatibility with Nuxt 4.x. The module being "disabled" means it is not actually running, even if it appears to work in development. This can lead to unpredictable behavior, especially in production or static builds, such as always showing the first blog post regardless of the URL. Your code and configuration are correct for a Nuxt 3 + Nuxt Content v3 setup, but since you are using Nuxt 4.0.3, the module is not supported and is being disabled. This is likely the root cause of your issue. The documentation clearly states that you should use Nuxt 3.x for Nuxt Content v3:
Nuxt Content v3 is only compatible with Nuxt 3.x. If you're using Nuxt Content v2, you must use Nuxt 2.x.
Getting Started with Nuxt Content
What should you do? - Downgrade your Nuxt version to a compatible 3.x release (e.g., 3.10.2). - Ensure your @nuxt/content version matches the Nuxt 3.x compatibility. Once you are on compatible versions, your dynamic blog routes and static generation should work as expected. Summary:
The incompatibility warning is not cosmetic—it means the module is not functioning properly, which explains the production issues you are seeing. Use Nuxt 3.x with Nuxt Content v3 for correct behavior. If you need to check the correct installation and setup, refer to the official installation guide. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai3w ago
<@600975674735329280> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
holkerveen
holkerveenOP3w ago
@kapa.ai Your conclusion seems incorrect. The docs you found are regarding nuxt2 and nuxt3. There is no mention of compatibility issues in the nuxt4 upgrade guide and there is even a page about @nuxt/content in the nuxt4 docs! https://nuxt.com/docs/4.x/guide/directory-structure/content
Nuxt
content · Nuxt Directory Structure v4
Use the content/ directory to create a file-based CMS for your application.
kapa.ai
kapa.ai3w ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- Thank you for your feedback and for pointing out the Nuxt 4 documentation reference. Based on the knowledge sources provided, here’s what we can confirm: - The Nuxt 4 documentation does include a section about using @nuxt/content with Nuxt 4, and describes the expected usage and setup. This suggests that @nuxt/content is intended to work with Nuxt 4. - However, your warning message indicates:
Module @nuxt/content is disabled due to incompatibility issues:
- [nuxt] Nuxt version ^3.0.0-rc.3 is required but currently using 4.0.3

Module @nuxt/content is disabled due to incompatibility issues:
- [nuxt] Nuxt version ^3.0.0-rc.3 is required but currently using 4.0.3

This warning means that the installed version of @nuxt/content is expecting Nuxt 3, not Nuxt 4. This is controlled by the module’s compatibility field, and if the version check fails, the module is disabled at runtime, even if some features appear to work in development mode. This is a real issue, not just a cosmetic warning. What does this mean for your issue? - If you see this warning, the version of @nuxt/content you have installed is not compatible with Nuxt 4, despite the documentation page. This can cause unpredictable behavior, especially in production or static builds, such as always showing the first blog post regardless of the URL. - The Nuxt 4 documentation does not specify which version of @nuxt/content to use, and the knowledge sources do not provide explicit confirmation that a Nuxt 4-compatible version of @nuxt/content is available at this time. What should you do? - Check that you are using the latest version of @nuxt/content. If you are, and you still see this warning, it means that full compatibility with Nuxt 4 is not yet available in the version you have installed. - If you need a stable production setup, you may need to use Nuxt 3.x with @nuxt/content v3 until a Nuxt 4-compatible release is available and does not show this warning. Summary:
The knowledge sources confirm that the warning you see is real and indicates a compatibility problem. The Nuxt 4 docs mention @nuxt/content, but the module itself is not yet compatible with Nuxt 4 if you see this warning. This is likely the cause of your production issue. There is not enough information in the knowledge sources to confirm a Nuxt 4-compatible release of @nuxt/content at this time, so you should use Nuxt 3.x for now if you require @nuxt/content to work reliably in production. If you want to follow the official upgrade path, keep an eye on the Nuxt Content changelog for updates on Nuxt 4 compatibility. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai3w ago
<@600975674735329280> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
holkerveen
holkerveenOP3w ago
@kapa.ai Ah, excuse me for the misunderstanding. The version error was introduced when running npm audit fix --force which installed an older version of nuxt content. Now at @nuxt/content v3.6.3 (latest). The original problem remains though
kapa.ai
kapa.ai3w ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- Thank you for clarifying the version situation. Based on your updated setup—using Nuxt 4.0.3 and @nuxt/content v3.6.3—the original problem remains: in production/static build, your blog always shows the first post regardless of the URL. What the knowledge sources say: - Your code for dynamic routing and content fetching is correct for a catch-all blog route using Nuxt Content v3, as shown in the official migration and usage guides. The pattern of using queryCollection('blog').path(route.path).first() in a catch-all page is the recommended approach for document-driven mode and dynamic content rendering Implement Document Driven mode in v3. - The static generation process (prerendering) in Nuxt relies on the Nitro crawler to discover and generate all routes. By default, it only prerenders routes that are linked from other pages or explicitly listed in your nitro.prerender.routes array. If your dynamic blog routes (e.g., /blog/my-post) are not linked from anywhere or not listed in the config, they may not be prerendered, which can cause issues like always showing the first post Prerendering. - The knowledge sources do not mention any known bug in @nuxt/content v3.6.3 or Nuxt 4 that would cause this specific behavior. Possible causes based on the sources: 1. Prerendering is not discovering your dynamic blog routes.
If your blog post pages are not linked from a static page (e.g., an index listing), or not included in nitro.prerender.routes, the static build may only generate the catch-all route for /blog and not for each individual post. This can result in the catch-all route always rendering the first post, regardless of the URL. 2. Route path mismatch or double prefix.
You mentioned seeing a value like blog/blog/moving-to-nuxt.md in the payload. This suggests a possible path mismatch between your content files and the route you are querying. Make sure your content files are in content/blog/*.md and your collection source is set to 'blog/*.md', as in your config. What you can try (based on the docs): - Ensure all blog post routes are discoverable for prerendering:
Add all your blog post routes to the nitro.prerender.routes array in your nuxt.config.ts:
nitro: {
prerender: {
routes: [
'/',
'/blog',
'/blog/post-1',
'/blog/post-2',
// ...add all your blog post slugs here
]
}
}

nitro: {
prerender: {
routes: [
'/',
'/blog',
'/blog/post-1',
'/blog/post-2',
// ...add all your blog post slugs here
]
}
}

Or, if you have a dynamic way to generate these routes, use that to populate the array Prerendering. - Check your content file structure and collection source:
Your content.config.ts should match your file structure:
collections: {
blog: defineCollection({
type: 'page',
source: 'blog/*.md',
// ...
})
}

collections: {
blog: defineCollection({
type: 'page',
source: 'blog/*.md',
// ...
})
}

And your files should be in content/blog/slug.md. - Check for double prefix in your path queries:
When querying, ensure you use .path('/blog/slug') and not .path('/blog/blog/slug'). Summary:
The knowledge sources suggest your issue is likely due to static prerendering not discovering your dynamic blog routes, or a path mismatch in your content structure or queries. There is no mention of a bug in the current versions. Make sure all dynamic routes are included in the prerender config, and double-check your file structure and path usage. If after these checks the issue persists, the knowledge sources do not provide further troubleshooting steps for this specific scenario. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai3w ago
<@600975674735329280> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
holkerveen
holkerveenOP3w ago
@kapa.ai I added my blog routes but that did not resolve the issue. Here is the block I added to nuxt.config.ts:
nitro: {
prerender: {
routes: [
'/blog/using-material-io',
'/blog/moving-to-nuxt',
'/blog/starting-a-blog',
]
}
},
nitro: {
prerender: {
routes: [
'/blog/using-material-io',
'/blog/moving-to-nuxt',
'/blog/starting-a-blog',
]
}
},
kapa.ai
kapa.ai3w ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- Based on the knowledge sources, explicitly adding your blog routes to nitro.prerender.routes in your nuxt.config.ts is the correct approach to ensure those dynamic routes are prerendered during the static build Prerendering. However, since this did not resolve your issue, there are a few additional points to consider: 1. Crawling and Route Discovery:
The Nitro prerenderer will only generate the routes you specify or those it can discover by crawling links in your app. If the content at those routes is not rendered correctly, it may be due to how the data is being fetched or how the route is being resolved during prerendering. 2. Dynamic Route Data Fetching:
When using useAsyncData with a dynamic key (like route.path), ensure that the key uniquely identifies the data for each route. The knowledge sources highlight that if the key is not unique per route, Nuxt may reuse the same data across different pages, especially during prerendering. You should use the route parameter (e.g., slug) as the key:
const route = useRoute()
const { data: page } = await useAsyncData(route.params.blog, () => {
return queryCollection('blog').path(`/blog/${route.params.blog}`).first()
})

const route = useRoute()
const { data: page } = await useAsyncData(route.params.blog, () => {
return queryCollection('blog').path(`/blog/${route.params.blog}`).first()
})

This ensures that each blog post fetches its own data Shared Prerender Data. 3. Catch-all Route Handling:
If you are using a catch-all route like [...blog].vue, make sure that the path you query matches the actual route. For example, if your route is /blog/using-material-io, then route.path should be /blog/using-material-io. Double-check that your content files and collection source match this structure. 4. Potential Data Sharing Issue:
The knowledge sources mention that Nuxt's shared prerender data feature can cause issues if the key for useAsyncData is not unique per route. If you use the same key (or a non-unique key), the first page's data may be reused for all routes during prerendering Shared Prerender Data. Summary:
- Double-check that your useAsyncData key is unique per route (e.g., use the slug). - Ensure your content file structure and collection source match your route structure. - The knowledge sources do not mention any other known issues that would cause this behavior. If after these checks the issue persists, the knowledge sources do not provide further troubleshooting steps for this specific scenario. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai3w ago
<@600975674735329280> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
holkerveen
holkerveenOP3w ago
Ok, so I have been unable to resolve the issue as of yet. Anyone who can help me here? Below once again the config I have. Also definitely running latest versions of both nuxt and nuxt/content!
<script setup lang="ts">
// app/pages/blog/[...blog].vue
const route = useRoute()
const { data:page } = await useAsyncData(route.path, () => {
return queryCollection('blog').path(route.path).first()
})
</script>

<template>
<div class="blog-wrapper">
<ContentRenderer v-if="page" :value="page" class="blog-content"/>
</div>
</template>
<script setup lang="ts">
// app/pages/blog/[...blog].vue
const route = useRoute()
const { data:page } = await useAsyncData(route.path, () => {
return queryCollection('blog').path(route.path).first()
})
</script>

<template>
<div class="blog-wrapper">
<ContentRenderer v-if="page" :value="page" class="blog-content"/>
</div>
</template>
// part of nuxt.config.ts
content: {
watch: {
enabled: false
},
},
nitro: {
static: true,
},
ssr: true,
// part of nuxt.config.ts
content: {
watch: {
enabled: false
},
},
nitro: {
static: true,
},
ssr: true,
// content.config.ts
import {defineContentConfig, defineCollection, z} from "@nuxt/content";

export default defineContentConfig({
collections: {
blog: defineCollection({
type: 'page',
source: 'blog/*.md',
schema: z.object({
title: z.string().min(1, 'Title is required'),
description: z.string().optional(),
date: z.date(),
tags: z.array(z.string()).optional(),
})
})
}
})
// content.config.ts
import {defineContentConfig, defineCollection, z} from "@nuxt/content";

export default defineContentConfig({
collections: {
blog: defineCollection({
type: 'page',
source: 'blog/*.md',
schema: z.object({
title: z.string().min(1, 'Title is required'),
description: z.string().optional(),
date: z.date(),
tags: z.array(z.string()).optional(),
})
})
}
})

Did you find this page helpful?