
A few words about Nuxt Content itself, and whether you really need it.
Nuxt Content is a module that turns your content folder into a full-fledged content database. It parses Markdown, YAML, CSV, and JSON files and provides a convenient API for querying them directly from your Vue components.
The architecture of Nuxt Content v3 is built around three concepts: collections, queries, and rendering.
Collections define the structure of your content. In the content.config.ts file, you describe which files belong to which collection and what validation schema to use:
import { defineContentConfig, defineCollection, z } from '@nuxt/content'
export default defineContentConfig({
collections: {
blog: defineCollection({
type: 'page',
source: 'blog/**/*.md',
schema: z.object({
date: z.string(),
tags: z.array(z.string()),
image: z.string()
})
})
}
})
Queries are executed via queryCollection() — a type-safe API with autocompletion based on your schemas:
// Get all posts
const posts = await queryCollection('blog').all()
// Find a specific page
const post = await queryCollection('blog').path('/hello-world').first()
// Filtering and sorting
const recent = await queryCollection('blog')
.where('date', '>', '2024-01-01')
.order('date', 'DESC')
.limit(5)
.all()
Content is rendered using the <ContentRenderer> component:
<script setup lang="ts">
const { data: page } = await useAsyncData(() =>
queryCollection('blog').path('/hello-world').first()
)
</script>
<template>
<ContentRenderer v-if="page" :value="page" />
</template>
One of the most powerful features is the MDC (Markdown Components) syntax. It allows you to use Vue components directly in your Markdown files:
---
title: My Article
date: 2024-12-15
---
# Introduction
A regular paragraph with **bold** text.
::alert{type="warning"}
This is a custom Vue component with props and slots.
::
::hero
#title
Title in a slot
#description
Description in another slot
::
For a better experience with MDC, I recommend installing the VS Code extension — it adds syntax highlighting and autocompletion for your components.
In version 3, Nuxt Content switched from a file-based storage to SQLite. This solved the problem of large bundles when deploying to serverless platforms and significantly sped up queries.
During client-side navigation, the module loads an SQL dump into the browser and executes all subsequent queries locally—without additional requests to the server. This makes navigating between pages instantaneous.
For those who want to edit content without an IDE, there's Nuxt Studio — a visual editor in the style of Notion. As of November 2025, it's a free, open-source module under the MIT license. It works directly with your Git repository via GitHub OAuth and provides a real-time preview of changes.
The module is currently in alpha, with a stable release planned for the end of 2025. Important: Studio requires SSR hosting (Vercel, Netlify, NuxtHub); a purely static deploy won't work due to server-side authentication.
| Nuxt Content | Strapi / Contentful | VitePress | Astro Content | |
|---|---|---|---|---|
| Type | Git-based CMS | Headless CMS | SSG for docs | Git-based CMS |
| Backend | Not required | Required (self-hosted or cloud) | Not required | Not required |
| Formats | MD, YAML, JSON, CSV | Any via API | MD only | MD, YAML, JSON |
| Components in MD | Yes (MDC) | No | Yes (Vue) | Yes (.mdx) |
| Typing | Full (Zod schemas) | Type generation | Basic | Full (Zod) |
| Visual Editor | Nuxt Studio (alpha) | Built-in | No | No |
| Deploy | Static, SSR, Edge | Separate server | Static, SSR | Static, SSR |
Headless CMSs offer more features for non-technical editors: a drag-and-drop interface, access rights, and UI-based versioning. However, this requires separate infrastructure with hosting and maintenance costs. Nuxt Content is a zero-infra solution: content lives in Git, is edited in an IDE or via Nuxt Studio, and is deployed with the code.
VitePress is a specialized tool for documentation. It's faster in this niche and comes with a built-in theme and search. Nuxt Content is more versatile: it's suitable for blogs, landing pages, and e-commerce catalogs. If you're building only documentation, VitePress is the simpler choice. If the documentation is part of a larger Nuxt project, Content integrates natively.
They are conceptually similar: both use collections, Zod schemas, and file-based storage. The difference is the framework: Astro is optimized for content-heavy sites with minimal JS, while Nuxt is a full-fledged Vue framework with SSR, middleware, and server routes. If you need interactivity and the Vue ecosystem, choose Nuxt Content. If you need the maximum speed of a static site, choose Astro.
queryCollection API take time to master, especially if coming from v2.Nuxt Content is best suited for projects where content lives alongside the code and is edited by the technical team.
Technical Documentation — the folder structure automatically becomes navigation, search is available via queryCollectionSearchSections(), and versioning is handled through Git branches. For a quick start, there's Docus, a ready-made documentation template based on Nuxt Content.
Developer Blog — Markdown files with frontmatter for metadata, MDC for interactive demos in posts, and an RSS feed via a server route.
Landing Pages — text content in YAML/JSON, which marketers can edit via Nuxt Studio, while developers control the layout.
Portfolio — each project as a separate Markdown file with a gallery implemented via MDC components.
It's not suitable for: sites with non-technical editors without Studio, content with complex relationships, large media portals, or real-time collaboration.
Let's start with a new project. We'll use nuxi for a quick start and immediately select the content template.
pnpm create nuxt <project-name>
This will create the complete structure for your blog, including an example page.
Next, add a few lines of configuration to nuxt.config.ts to use the native sqlite connector:
export default defineNuxtConfig({
content: {
experimental: {
sqliteConnector: 'native',
}
}
})
You can also enable the configuration to use the Nuxt Studio Preview:
export default defineNuxtConfig({
content: {
preview: {
api: 'https://api.nuxt.studio',
}
}
})
Run pnpm run dev and check the result at http://localhost:3000.
Use pnpm to install the package:
pnpm add @nuxt/content
Register the module in nuxt.config.ts:
export default defineNuxtConfig({
modules: ['@nuxt/content']
})
Create a content.config.ts file in the project root:
import { defineContentConfig, defineCollection } from '@nuxt/content'
export default defineContentConfig({
collections: {
content: defineCollection({
type: 'page',
source: '**/*.md'
})
}
})
Create a content folder with an index.md file in the project root:
# My First Page
Here is some content.
And configure the page display:
<script setup lang="ts">
const { data: home } = await useAsyncData(() => queryCollection('content').path('/').first())
useSeoMeta({
title: home.value?.title,
description: home.value?.description
})
</script>
<template>
<ContentRenderer v-if="home" :value="home" />
<div v-else>Page not found</div>
</template>
Add the configuration for the native sqlite connector and Nuxt Studio (optional) to nuxt.config.ts:
export default defineNuxtConfig({
content: {
experimental: {
sqliteConnector: 'native',
},
preview: {
api: 'https://api.nuxt.studio', // optional
}
}
})
Done, Nuxt Content is configured.