# Nuxt Content Reference **Last Updated:** 2025-11 (v3.8.0) **Check:** `@nuxt/content` in package.json Nuxt Content v3 is a Git-based CMS that uses SQL-backed collections for querying markdown, YAML, JSON, and CSV files. Perfect for blogs, documentation sites, and content-heavy applications. ## Installation & Setup ```bash pnpm add @nuxt/content ``` **nuxt.config.ts:** ```typescript export default defineNuxtConfig({ modules: ["@nuxt/content"], content: { // Optional configuration highlight: { theme: "github-dark", preload: ["typescript", "vue", "bash"], }, }, }) ``` ## Collections ### Defining Collections Create `content.config.ts` in your project root: ```typescript import { defineCollection, defineContentConfig, z } from "@nuxt/content" export default defineContentConfig({ collections: { blog: defineCollection({ type: "page", source: "blog/**/*.md", schema: z.object({ title: z.string(), description: z.string(), date: z.string(), tags: z.array(z.string()).optional(), author: z.string().optional(), image: z.string().optional(), draft: z.boolean().default(false), }), }), docs: defineCollection({ type: "page", source: "docs/**/*.md", schema: z.object({ title: z.string(), description: z.string(), category: z.string().optional(), }), }), }, }) ``` ### Directory Structure ``` content/ ├── blog/ │ ├── post-1.md │ ├── post-2.md │ └── post-3.md └── docs/ ├── getting-started.md └── api/ └── components.md ``` ## Frontmatter Add metadata to content files: ```markdown --- title: "My Blog Post" description: "A brief description" date: "2025-01-04" tags: ["nuxt", "vue", "web-dev"] author: "John Doe" image: "/images/cover.jpg" draft: false --- # Content starts here Your markdown content... ``` ## Querying Collections ### queryCollection Composable The main API for querying content in v3: ```vue ``` ### Query Methods #### all() Get all matching documents: ```typescript const posts = await queryCollection("blog").all() ``` #### first() Get first matching document: ```typescript const post = await queryCollection("blog").path("/blog/my-post").first() ``` #### where() Filter by field with SQL operators: ```typescript // Single condition const published = await queryCollection("blog").where("draft", "=", false).all() // Multiple conditions (AND) const filtered = await queryCollection("blog") .where("draft", "=", false) .where("category", "=", "tech") .all() ``` #### andWhere() Complex AND conditions: ```typescript const posts = await queryCollection("blog") .where("published", "=", true) .andWhere((query) => query.where("date", ">", "2024-01-01").where("category", "=", "news"), ) .all() ``` #### orWhere() OR conditions: ```typescript const posts = await queryCollection("blog") .where("published", "=", true) .orWhere((query) => query.where("featured", "=", true).where("priority", ">", 5), ) .all() ``` #### order() Sort results: ```typescript // Descending const posts = await queryCollection("blog").order("date", "DESC").all() // Ascending const posts = await queryCollection("blog").order("title", "ASC").all() ``` #### limit() Limit results: ```typescript const latest = await queryCollection("blog") .order("date", "DESC") .limit(5) .all() ``` #### skip() Skip results (for pagination): ```typescript const page = 2 const perPage = 10 const posts = await queryCollection("blog") .order("date", "DESC") .skip((page - 1) * perPage) .limit(perPage) .all() ``` #### select() Select specific fields: ```typescript const posts = await queryCollection("blog") .select(["title", "description", "date", "path"]) .all() ``` #### path() Filter by path: ```typescript const post = await queryCollection("blog").path("/blog/my-post").first() ``` ## ContentRenderer Component Renders parsed markdown content (the main component for v3): ```vue ``` With error handling: ```vue ``` ## Common Patterns ### Blog List Page ```vue ``` ### Single Blog Post with Related Articles ```vue ``` ### Documentation Site ```vue ``` ### Search ```vue ``` ### Pagination ```vue ``` ### Filter by Tags/Categories ```vue ``` ## Server-Side Queries Use `queryCollection` in API routes with event parameter: ```typescript // server/api/posts.get.ts export default defineEventHandler(async (event) => { const posts = await queryCollection(event, "blog") .where("draft", "=", false) .order("date", "DESC") .limit(10) .all() return posts }) ``` With query parameters: ```typescript // server/api/posts/[slug].get.ts export default defineEventHandler(async (event) => { const slug = getRouterParam(event, "slug") const post = await queryCollection(event, "blog") .path(`/blog/${slug}`) .first() if (!post) { throw createError({ statusCode: 404, message: "Post not found", }) } return post }) ``` ## Navigation Use `queryCollectionNavigation` for generating navigation: ```vue ``` ## Markdown Features ### Syntax Highlighting Configure in `nuxt.config.ts`: ```typescript export default defineNuxtConfig({ content: { highlight: { theme: { default: "github-light", dark: "github-dark", }, preload: ["typescript", "vue", "bash", "json"], }, }, }) ``` Use in markdown: ````markdown ```typescript interface User { name: string age: number } ``` ```` ### Custom Vue Components in Markdown Use Vue components directly in markdown (MDC syntax): ```markdown # My Article ::Alert{type="info"} This is an informational alert! :: ::CallToAction{title="Get Started" url="/docs"} Learn more about Nuxt Content :: ``` Register components in `components/content/` directory: ```vue ``` ## TypeScript Support Type your collections: ```typescript // types/content.ts export interface BlogPost { title: string description: string date: string tags?: string[] author?: string image?: string draft: boolean path: string body: any } ``` Use in components: ```vue ``` ## Migration from v2 ### Key Changes 1. **`queryContent()` → `queryCollection(name)`** ```typescript // v2 queryContent("/blog").find() // v3 queryCollection("blog").all() ``` 2. **Collections must be defined in `content.config.ts`** 3. **Components removed:** - ❌ `` - use ContentRenderer - ❌ `` - query manually with queryCollection - ❌ `` - use queryCollectionNavigation - ❌ `` - use queryCollection 4. **New query methods:** - `.all()` instead of `.find()` - `.first()` instead of `.findOne()` - `.where()` with SQL operators - `.order()` instead of `.sort()` 5. **SQL-backed storage** - faster queries for large datasets ## Best Practices 1. **Define collections** - Always create `content.config.ts` with schemas 2. **Use TypeScript** - Type your collections for better DX 3. **Cache queries** - Use `useAsyncData` with proper keys 4. **Server-side queries** - Query on server for API routes 5. **Index for performance** - Consider indexing frequently queried fields 6. **Validate frontmatter** - Use Zod schemas in collection definitions 7. **Handle 404s** - Always check if content exists and throw errors 8. **Use path()** - More efficient than where() for path filtering 9. **Select fields** - Use `.select()` to reduce payload size 10. **Pagination** - Implement for large collections ## Official Resources - **Documentation:** https://content.nuxt.com - **Collections:** https://content.nuxt.com/docs/collections - **Query API:** https://content.nuxt.com/docs/utils/query-collection - **Migration Guide:** https://content.nuxt.com/docs/getting-started/migration - **GitHub:** https://github.com/nuxt/content --- **Note:** This reference covers Nuxt Content v3. The v2 API (`queryContent`, `ContentDoc`, `ContentList`) is deprecated and not compatible with v3.