12/18/2025

Nuxt Content: What It Is, How It Works, and When to Use It

Nuxt Content: What It Is, How It Works, and When to Use It

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.

How It Works

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>

MDC: Vue Components in Markdown

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.

SQL Under the Hood

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.

Nuxt Studio

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.

Comparison with Alternatives

Nuxt ContentStrapi / ContentfulVitePressAstro Content
TypeGit-based CMSHeadless CMSSSG for docsGit-based CMS
BackendNot requiredRequired (self-hosted or cloud)Not requiredNot required
FormatsMD, YAML, JSON, CSVAny via APIMD onlyMD, YAML, JSON
Components in MDYes (MDC)NoYes (Vue)Yes (.mdx)
TypingFull (Zod schemas)Type generationBasicFull (Zod)
Visual EditorNuxt Studio (alpha)Built-inNoNo
DeployStatic, SSR, EdgeSeparate serverStatic, SSRStatic, SSR

Nuxt Content vs Headless CMS (Strapi, Contentful, Sanity)

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.

Nuxt Content vs VitePress

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.

Nuxt Content vs Astro Content Collections

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.

Pros and Cons

Pros

  • Zero infrastructure — no need for a separate backend, database, or CMS server. Content is deployed with the code.
  • Git workflow — versioning, code reviews, and rollbacks using familiar tools. Content goes through the same CI/CD pipeline as the code.
  • Type safety — Zod schemas validate content at build time. TypeScript autocompletion for all fields.
  • MDC syntax — use Vue components in Markdown without compromise. Props, slots, and nested components are all supported.
  • Performance — an SQL database and client-side caching make navigation instantaneous. Works on the edge without cold starts.
  • Deployment flexibility — static generation, SSR, serverless, edge workers — one codebase, multiple platforms.

Cons

  • Not for non-technical editors — without Nuxt Studio, editing requires knowledge of Git and Markdown. Even with Studio, it's not as simple as Notion.
  • Scale — for thousands of pages or complex content relationships, a traditional database will be more efficient.
  • Previewing changes — to see the result, you need a local dev server or a configured preview deployment.
  • Vendor lock-in — the MDC syntax is specific to Nuxt. Migrating to another framework would require rewriting content.
  • Learning curve — collections, schemas, and the queryCollection API take time to master, especially if coming from v2.

Typical Use Cases

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.

Initialization

Automatic Setup

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.

Manual Setup

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.

Recommendations

  • Use the latest versions of Node.js (22.5.0+ for native SQLite) and all dependencies.
  • Use pnpm as your package manager.
  • Install the MDC extension for VS Code for syntax highlighting.
  • Keep it simple — start with a basic structure and complicate it only when necessary.

Official Resources