If you're building documentation, a product blog, or a marketing site in Nuxt, you've probably wondered whether there's a better way than managing content in a headless CMS or a database. Nuxt Content is that answer. In this piece, we'll walk through what every Vue and Nuxt developer should know about Nuxt Content: what every Vue & Nuxt developer should know when it comes to treating Markdown files as queryable data, embedding Vue components directly in your content, and deploying content-rich applications with minimal overhead.
Write in Markdown — Files in content/ become queryable data
The core philosophy of Nuxt Content is elegantly simple: drop your Markdown files into a content/ directory, and they automatically become structured, queryable data. No database setup, no migrations, no API endpoints to write. Your filesystem becomes your content store.
Let's say you're building a blog. You'd create files like:
content/
├── blog/
│ ├── getting-started-with-vue-3.md
│ ├── nuxt-3-performance-tips.md
│ └── real-world-ssr-patterns.md
└── docs/
├── installation.md
├── configuration.md
└── api-reference.mdEach Markdown file automatically becomes a content object with frontmatter parsed into structured metadata. Here's what a typical blog post might look like:
---
title: Getting Started with Vue 3 Composition API
description: A practical guide to migrating from Options API
author: Jane Doe
publishedAt: 2024-01-15
tags:
- vue
- composition-api
- performance
---
# Getting Started with Vue 3 Composition API
This is your post content in Markdown...
## Section heading
More content here.When Nuxt Content processes this file, the frontmatter becomes queryable fields, and the body becomes parsed HTML. You fetch it programmatically rather than managing a database schema. For teams shipping documentation or blogs, this eliminates a category of infrastructure decisions entirely.
The beauty here is that your content lives in version control. Changes to posts are Git commits. Rollbacks are Git reverts. Your entire editorial history is auditable. This is particularly valuable for technical documentation where accuracy and change tracking matter.
Query — queryCollection for lists and pages
Nuxt Content provides a composable called queryCollection() that lets you fetch and filter content with a clean, chainable API. This is where your Markdown files transition from static files to dynamic data.
Fetching a single post by slug is straightforward:
const { data: post } = await queryCollection('blog').where({ _path: `/blog/${route.params.slug}` }).first();Listing all published posts, sorted by date, with filtering:
const { data: posts } = await queryCollection('blog')
.where({ published: true })
.sort({ publishedAt: -1 })
.limit(10)
.all();In a real blog component, you'd do this in a server route or a server utility to avoid exposing your entire content structure to the client. Here's a practical pattern:
// server/api/blog/posts.ts
export default defineEventHandler(async () => {
const { data: posts } = await queryCollection('blog')
.where({ published: true })
.sort({ publishedAt: -1 })
.all();
return posts.map(post => ({
title: post.title,
slug: post._path.replace(/^\/blog\//, ''),
description: post.description,
publishedAt: post.publishedAt,
tags: post.tags,
}));
});Then in your page or component:
// app.vue
const { data: posts } = await useFetch('/api/blog/posts');
const filteredByTag = computed(() => {
if (!selectedTag.value) return posts.value;
return posts.value.filter(p => p.tags.includes(selectedTag.value));
});The query API supports filtering by any frontmatter field, sorting in ascending or descending order, limiting results, and even full-text search. For documentation sites with hundreds of pages, this scales well because queries are built at build time or at request time without a database round-trip.
Components in Markdown — MDC syntax embeds Vue components
One of the most powerful features is Markdown Components (MDC) syntax, which lets you embed Vue components directly in your Markdown files. This bridges the gap between content and interactivity.
Consider a scenario: you're writing a technical tutorial and want to include an interactive code sandbox or a styled alert. Instead of limiting yourself to plain HTML, you write:
::alert{type="warning"}
This is a warning message. The `alert` component is defined in your codebase.
::In your Markdown file it looks like:
# Advanced State Management
Learn how to structure state across multiple modules.
::code-example
---
title: "Pinia Store Setup"
language: "typescript"
---
```typescript
export const useUserStore = defineStore('user', {
state: () => ({ isLoggedIn: false }),
actions: { login() { this.isLoggedIn = true } }
})
```
::
The pattern above works great for enterprise apps.
::callout{type="info"}
Remember to hydrate stores on the client side.
::You define these components in your components/content/ directory, and Nuxt automatically makes them available in your Markdown files. Here's what the Alert component might look like:
// components/content/Alert.vue
<div>
</div>
This approach is powerful because non-technical writers can focus on content while developers create reusable, styled components once. Marketing sites get rich, interactive elements without duplicating layout or styling logic.
Great for — Docs, blogs and marketing sites
Nuxt Content shines in specific use cases where Markdown-first is a natural fit.
Documentation sites: API reference docs, SDK guides, setup instructions. When your docs live in Markdown in the same Git repo as your code, developers keep them updated. Tools like Starlight are built on top of Nuxt Content for exactly this reason. A team shipping a Node.js library can version docs alongside the package, and the docs site auto-deploys whenever the repo changes.
Product blogs: Internal blogs, engineering blogs, customer success stories. Editors write Markdown locally, commit to Git, and the blog auto-updates on deploy. You get versioning, auditability, and no separate CMS to manage. We've built this pattern at companies with 50+ published articles and it holds up well.
Marketing sites: Landing pages, case studies, knowledge bases. For static or semi-static content with occasional interactive elements, Nuxt Content keeps the site lightweight while remaining flexible. A SaaS marketing site might have a directory of use cases or integrations, each as a Markdown file, filterable and searchable without a database.
What Nuxt Content is not great for: highly dynamic, real-time content (live feeds, chat, inventory). For those, you need a database. But for anything that changes occasionally and lives in version control, Nuxt Content removes friction.
Common mistakes
Treating Markdown files as truly dynamic: Nuxt Content works best when content is built or generated at deploy time. If you need to update a blog post and see changes instantly in production without a redeploy, you'll want a headless CMS instead. Content does support live updates in development, but production requires a rebuild.
Overloading frontmatter: It's tempting to stick complex objects or arrays into frontmatter. Keep it simple: strings, numbers, dates, arrays of primitives. For richer metadata, use a structured data directory separate from your Markdown files.
Forgetting to secure server routes: When you expose a server API to list or filter all content, remember that route is public. If you have draft posts or private documentation, add proper auth checks in your server routes.
Not leveraging build-time queries: Many developers fetch content in client-side composables. Prefer server routes or async middleware that run at build time. This keeps your client bundle small and improves performance.
Key takeaways
- Markdown files in
content/become queryable objects automatically, no database needed. - Use
queryCollection()to fetch, filter, sort, and limit content with a clean chainable API. - MDC syntax lets you embed Vue components directly in Markdown, blending static content with interactivity.
- Nuxt Content works best for documentation, blogs, and marketing sites that change occasionally and benefit from version control.
- Always run heavy queries server-side to keep your client bundle lean and queries fast.
- Combine Nuxt Content with a headless CMS only if you need real-time editorial workflows; Markdown-first works well for teams comfortable with Git.






Comments
Loading comments…