N
Nuxt6d ago
Mike

Exposed values in a queryCollection

Hi, I'm trying to output my Front Matter data in a .vue file, but something seems off. I'm wondering if I might have missed something that explains this behavior. Example markdown file:
---
title: "Chicken Honey Mustard"
description: "A delicious honey mustard recipe"
image: "/img/chicken-honey-mustard.jpg"
---
---
title: "Chicken Honey Mustard"
description: "A delicious honey mustard recipe"
image: "/img/chicken-honey-mustard.jpg"
---
example .vue file:
<script setup lang="ts">
const { data: recipes } = await useAsyncData('recipes', () => queryCollection('recipes').all())
</script>

<template>
<div class="container">
<ul v-if="recipes" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3">
<li v-for="recipe in recipes" :key="recipe.id">
<NuxtLink :to="recipe.path">
<!-- todo: :src gives IDE error: `Vue: Type unknown is not assignable to type string | undefined`. Find out why and how to fix it. -->
<NuxtPicture :src="recipe.meta.image" />

<h2 class="font-bold">
{{ recipe.title }}
</h2>

{{ recipe.description }}
</NuxtLink>
</li>
</ul>
</div>
</template>
<script setup lang="ts">
const { data: recipes } = await useAsyncData('recipes', () => queryCollection('recipes').all())
</script>

<template>
<div class="container">
<ul v-if="recipes" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3">
<li v-for="recipe in recipes" :key="recipe.id">
<NuxtLink :to="recipe.path">
<!-- todo: :src gives IDE error: `Vue: Type unknown is not assignable to type string | undefined`. Find out why and how to fix it. -->
<NuxtPicture :src="recipe.meta.image" />

<h2 class="font-bold">
{{ recipe.title }}
</h2>

{{ recipe.description }}
</NuxtLink>
</li>
</ul>
</div>
</template>
In the example above, I can access recipe.title and recipe.description directly, but I have to use recipe.meta.image to access the image value. Is this the expected behavior for @nuxt/content? Shouldn't the image field be available in the same way as title and description? Any help in understanding why this happens or how to configure it differently (better?) would be appreciated!
5 Replies
kapa.ai
kapa.ai6d ago
Hi @Mike. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave: ---------------------- Based on the knowledge sources provided, I can explain the behavior you're experiencing with Nuxt Content. The difference in how you access the title, description, and image fields is related to how Nuxt Content handles frontmatter data in Markdown files, particularly for collections of type "page". According to the Page type documentation, when you use the "page" type for a collection, Nuxt Content automatically generates several standard fields, including title and description. These fields are directly accessible on the content object. However, custom fields that are not part of these standard fields (like image in your case) are placed in the meta object. This is why you need to access image through recipe.meta.image. To make image directly accessible like title and description, you have two options: 1. Define a schema for your collection that includes the image field. This way, Nuxt Content will treat it as a top-level field. Here's an example of how you could do this:
import { defineCollection, defineContentConfig, z } from '@nuxt/content'

export default defineContentConfig({
collections: {
recipes: defineCollection({
type: 'page',
source: '**/*.md',
schema: z.object({
image: z.string()
})
})
}
})
import { defineCollection, defineContentConfig, z } from '@nuxt/content'

export default defineContentConfig({
collections: {
recipes: defineCollection({
type: 'page',
source: '**/*.md',
schema: z.object({
image: z.string()
})
})
}
})
2. Continue accessing image through meta as you're currently doing. This is the default behavior for custom fields not defined in the schema. Regarding the IDE error you're getting with :src, this is likely because TypeScript doesn't know the type of recipe.meta.image. You can resolve this by either defining the schema as mentioned above, or by using a type assertion if you're sure about the type:
<NuxtPicture :src="recipe.meta.image as string" />
<NuxtPicture :src="recipe.meta.image as string" />
Remember, defining a schema not only helps with TypeScript types but also enforces consistency in your content structure. Collections documentation provides more details on how to define and configure collections in Nuxt Content. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai6d ago
<@283958409366339584> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
Mike
MikeOP6d ago
@kapa.ai Thanks, that certainly helps. Out of the two options you provided (continue using it as-is, or defining a schema), which is the recommended way within Nuxt?
kapa.ai
kapa.ai6d ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- Based on the knowledge sources and best practices for Nuxt Content, the recommended approach is to define a schema for your collection. Here's why: 1. Type Safety: Defining a schema provides better type safety and autocompletion in your Vue files. As mentioned in the Content homepage, you can "Define schema for the collection frontmatter" and "Get auto-completion in your Vue files". 2. Consistency: A schema helps enforce a consistent structure for your content, which is especially useful as your project grows. 3. Validation: The schema can be used to validate your content, ensuring that all required fields are present and have the correct type. 4. Direct Access: With a defined schema, you can access custom fields directly (like recipe.image) instead of through the meta object, which aligns with how you're accessing title and description. Here's an example of how you could define your schema:
import { defineCollection, defineContentConfig, z } from '@nuxt/content'

export default defineContentConfig({
collections: {
recipes: defineCollection({
type: 'page',
source: '**/*.md',
schema: z.object({
title: z.string(),
description: z.string(),
image: z.string()
})
})
}
})
import { defineCollection, defineContentConfig, z } from '@nuxt/content'

export default defineContentConfig({
collections: {
recipes: defineCollection({
type: 'page',
source: '**/*.md',
schema: z.object({
title: z.string(),
description: z.string(),
image: z.string()
})
})
}
})
This approach is highlighted in the Query with Type-Safety section of the Nuxt Content documentation, which emphasizes creating collections for similar content files and defining schemas for collection frontmatter. By using this method, you're leveraging Nuxt Content's features to their fullest, providing a more robust and maintainable structure for your content. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai6d ago
<@283958409366339584> Kapa.ai is still learning and improving, please let me know how I did by reacting below.

Did you find this page helpful?