**Status**: Production Ready
/plugin marketplace add secondsky/claude-skills/plugin install nuxt-content@claude-skillsThis skill inherits all available tools. When active, it can use any tool Claude has access to.
assets/blog-collection.example.tsassets/content.config.example.tsassets/docs-collection.example.tsassets/nuxt.config.example.tsreferences/collection-examples.mdreferences/deployment-checklists.mdreferences/error-catalog.mdreferences/mdc-syntax-reference.mdreferences/query-operators.mdreferences/studio-setup-guide.mdscripts/deploy-cloudflare.shscripts/deploy-vercel.shscripts/setup-nuxt-content.shscripts/setup-studio.shtemplates/blog-collection-setup.tsStatus: Production Ready Last Updated: 2025-01-10 Dependencies: None Latest Versions: @nuxt/content@^3.0.0, nuxt-studio@^0.1.0-alpha, zod@^4.1.12, valibot@^0.42.0, better-sqlite3@^11.0.0
Nuxt Content v3 is a powerful Git-based CMS for Nuxt projects that manages content through Markdown, YAML, JSON, and CSV files. It transforms content files into structured data with type-safe queries, automatic validation, and SQL-based storage for optimal performance.
Major Improvements:
Use this skill when:
# Bun (recommended)
bun add @nuxt/content better-sqlite3
# npm
npm install @nuxt/content better-sqlite3
# pnpm
pnpm add @nuxt/content better-sqlite3
Why this matters:
@nuxt/content is the core CMS modulebetter-sqlite3 provides SQL storage for optimal performance// nuxt.config.ts
export default defineNuxtConfig({
modules: ['@nuxt/content']
})
CRITICAL:
modules array (not buildModules)// content.config.ts
import { defineContentConfig, defineCollection } from '@nuxt/content'
import { z } from 'zod'
export default defineContentConfig({
collections: {
content: defineCollection({
type: 'page',
source: '**/*.md',
schema: z.object({
tags: z.array(z.string()).optional(),
date: z.date().optional()
})
})
}
})
Create content file:
<!-- content/index.md -->
---
title: Hello World
description: My first Nuxt Content page
tags: ['nuxt', 'content']
---
# Welcome to Nuxt Content v3
This is my first content-driven site!
<!-- pages/[...slug].vue -->
<script setup>
const route = useRoute()
const { data: page } = await useAsyncData(route.path, () =>
queryCollection('content').path(route.path).first()
)
</script>
<template>
<ContentRenderer v-if="page" :value="page" />
</template>
See Full Template: templates/blog-collection-setup.ts
content.config.ts before querying2024-01-15 or 2024-01-15T10:30:00Zcontent.config.ts.only() to select specific fields (performance)components/content/ directory01-, 02-better-sqlite3 required<!--more--> in content to separate excerptscontent.config.ts.only())components/content/01- not 1-)<!--more--> dividerCollections organize related content with shared configuration:
// content.config.ts
import { defineCollection, defineContentConfig } from '@nuxt/content'
import { z } from 'zod'
export default defineContentConfig({
collections: {
blog: defineCollection({
type: 'page',
source: 'blog/**/*.md',
schema: z.object({
title: z.string(),
date: z.date(),
tags: z.array(z.string()).default([])
})
}),
authors: defineCollection({
type: 'data',
source: 'authors/*.yml',
schema: z.object({
name: z.string(),
bio: z.string()
})
})
}
})
type: 'page')Use for: Content that maps to URLs
Features:
path, title, description, body, navigationPath Mapping:
content/index.md → /
content/about.md → /about
content/blog/hello.md → /blog/hello
type: 'data')Use for: Structured data without URLs
Features:
bun add -D zod@^4.1.12
# or: npm install -D zod@^4.1.12
import { z } from 'zod'
schema: z.object({
title: z.string(),
date: z.date(),
published: z.boolean().default(false),
tags: z.array(z.string()).optional(),
category: z.enum(['news', 'tutorial', 'update'])
})
bun add -D valibot@^0.42.0
# or: npm install -D valibot@^0.42.0
import * as v from 'valibot'
schema: v.object({
title: v.string(),
date: v.date(),
published: v.boolean(false),
tags: v.optional(v.array(v.string()))
})
// Get all posts
const posts = await queryCollection('blog').all()
// Get single post by path
const post = await queryCollection('blog').path('/blog/hello').first()
// Get post by ID
const post = await queryCollection('blog').where('_id', '=', 'hello').first()
// Select specific fields (performance optimization)
const posts = await queryCollection('blog')
.only(['title', 'description', 'date', 'path'])
.all()
// Exclude fields
const posts = await queryCollection('blog')
.without(['body'])
.all()
// Single condition
const posts = await queryCollection('blog')
.where('published', '=', true)
.all()
// Multiple conditions
const posts = await queryCollection('blog')
.where('published', '=', true)
.where('category', '=', 'tutorial')
.all()
// Operators: =, !=, <, <=, >, >=, in, not-in, like
const recent = await queryCollection('blog')
.where('date', '>', new Date('2024-01-01'))
.all()
const tagged = await queryCollection('blog')
.where('tags', 'in', ['nuxt', 'vue'])
.all()
// Sort by field
const posts = await queryCollection('blog')
.sort('date', 'DESC')
.all()
// Pagination
const posts = await queryCollection('blog')
.sort('date', 'DESC')
.limit(10)
.offset(0)
.all()
// Count results
const total = await queryCollection('blog')
.where('published', '=', true)
.count()
// server/api/posts.get.ts
export default defineEventHandler(async (event) => {
const posts = await queryCollection('blog')
.where('published', '=', true)
.sort('date', 'DESC')
.all()
return { posts }
})
Auto-generate navigation tree from content structure:
const navigation = await queryCollectionNavigation('blog').all()
Returns hierarchical structure:
[
{
title: 'Getting Started',
path: '/docs/getting-started',
children: [{ title: 'Installation', path: '/docs/getting-started/installation' }]
}
]
Advanced patterns (filters, ordering, custom structures): See references/collection-examples.md
<!-- Default slot -->
::my-alert
This is an alert message
::
<!-- Named slots -->
::my-card
#title
Card Title
#default
Card content here
::
Component:
<!-- components/content/MyAlert.vue -->
<template>
<div class="alert">
<slot />
</div>
</template>
::my-button{href="/docs" type="primary"}
Click me
::
Component:
<!-- components/content/MyButton.vue -->
<script setup>
defineProps<{
href: string
type: 'primary' | 'secondary'
}>()
</script>
// Search across all content
const results = await queryCollectionSearchSections('blog', 'nuxt content')
.where('published', '=', true)
.all()
Returns:
[
{
id: 'hello',
path: '/blog/hello',
title: 'Hello World',
content: '...matching text...'
}
]
bun add -D @nuxthub/core
bunx wrangler d1 create nuxt-content
bun run build && bunx wrangler pages deploy dist
wrangler.toml:
[[d1_databases]]
binding = "DB" # Must be exactly "DB" (case-sensitive)
database_name = "nuxt-content"
database_id = "your-database-id"
CRITICAL: D1 binding MUST be named DB (case-sensitive).
See: references/deployment-checklists.md for complete Cloudflare deployment guide with troubleshooting.
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['@nuxt/content'],
routeRules: {
'/blog/**': { prerender: true }
}
})
vercel deploy
See: references/deployment-checklists.md for complete Vercel configuration and prerender strategy.
bun add -D nuxt-studio@alpha
# or: npm install -D nuxt-studio@alpha
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['@nuxt/content', 'nuxt-studio'],
studio: {
enabled: true,
gitInfo: {
name: 'your-name',
email: 'your-email@example.com'
}
}
})
https://yourdomain.com/api/__studio/oauth/callbackCRITICAL: Callback URL must match production domain exactly (including https://).
Error: Collection 'xyz' not found
Solution: Define collection in content.config.ts and restart dev server
rm -rf .nuxt && bun dev
Error: Validation error: Expected date, received string
Solution: Use ISO 8601 format:
---
date: 2024-01-15 # ✅ Correct
# NOT: "January 15, 2024" # ❌ Wrong
---
Error: DB is not defined
Solution: D1 binding name must be exactly DB (case-sensitive) in Cloudflare dashboard.
Error: Components show as raw text
Solution: Place components in components/content/ with exact name matching:
<!-- components/content/MyAlert.vue -->
::my-alert
Content
::
Error: New content doesn't appear in navigation
Solution: Clear .nuxt cache:
rm -rf .nuxt && bun dev
See All 18 Issues: references/error-catalog.md
Load references/error-catalog.md when:
Load references/collection-examples.md when:
Load references/query-operators.md when:
Load references/deployment-checklists.md when:
Load references/mdc-syntax-reference.md when:
Load references/studio-setup-guide.md when:
Templates (templates/):
blog-collection-setup.ts - Complete blog setup with collections, queries, navigation, search, and deployment (334 lines).only() to select specific fieldscomponents/content/<!--more--> for excerpts.nuxt after config changesThis skill composes well with:
Official Documentation:
Examples:
Production Tested: Documentation sites, blogs, content platforms Last Updated: 2025-01-27 Token Savings: ~60% (reduces content + error documentation)
Create distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.