Building a static blog with Nuxt Content v3
How this site works under the hood: Markdown collections, SQLite-backed queries, and full static generation with Nuxt.
This site is a fully static Nuxt app. Every post is a Markdown file in the repo, and nuxt generate turns the whole thing into plain HTML. Here's the interesting part of the setup.
Collections are typed
Nuxt Content v3 replaced the old file-based querying with collections — you declare a schema, and frontmatter gets validated at build time:
const blogSchema = z.object({
title: z.string(),
description: z.string(),
date: z.string(),
tags: z.array(z.string()).default([]),
})
Typos in frontmatter now fail the build instead of silently rendering undefined. This is the single biggest quality-of-life win over v2.
Queries look like SQL because they are
Under the hood, content lives in SQLite. The query API compiles to SQL:
const posts = await queryCollection('blog_en')
.order('date', 'DESC')
.limit(3)
.all()
Two languages, two collections
For a bilingual site, one collection per locale keeps things simple:
content/
en/blog/ → collection blog_en, served at /blog
uz/blog/ → collection blog_uz, served at /uz/blog
A tiny composable picks the right collection from the active locale, and the rest of the code never thinks about i18n.
The result
No server, no database in production, no CMS subscription. Just Markdown files, a git history, and static HTML that any CDN can serve.