[{"data":1,"prerenderedAt":271},["ShallowReactive",2],{"blog-post-\u002Fblog\u002Fnuxt-content-static-blog":3,"blog-surround-\u002Fblog\u002Fnuxt-content-static-blog":266},{"id":4,"title":5,"body":6,"date":252,"description":253,"extension":254,"image":255,"meta":256,"navigation":257,"path":258,"seo":259,"stem":260,"tags":261,"__hash__":265},"blog_en\u002Fblog\u002Fnuxt-content-static-blog.md","Building a static blog with Nuxt Content v3",{"type":7,"value":8,"toc":246},"minimark",[9,18,23,31,128,135,139,142,217,221,224,232,235,239,242],[10,11,12,13,17],"p",{},"This site is a fully static Nuxt app. Every post is a Markdown file in the repo, and ",[14,15,16],"code",{},"nuxt generate"," turns the whole thing into plain HTML. Here's the interesting part of the setup.",[19,20,22],"h2",{"id":21},"collections-are-typed","Collections are typed",[10,24,25,26,30],{},"Nuxt Content v3 replaced the old file-based querying with ",[27,28,29],"strong",{},"collections"," — you declare a schema, and frontmatter gets validated at build time:",[32,33,38],"pre",{"className":34,"code":35,"language":36,"meta":37,"style":37},"language-ts shiki shiki-themes github-light github-dark","const blogSchema = z.object({\n  title: z.string(),\n  description: z.string(),\n  date: z.string(),\n  tags: z.array(z.string()).default([]),\n})\n","ts","",[14,39,40,67,79,89,99,122],{"__ignoreMap":37},[41,42,45,49,53,56,60,64],"span",{"class":43,"line":44},"line",1,[41,46,48],{"class":47},"szBVR","const",[41,50,52],{"class":51},"sj4cs"," blogSchema",[41,54,55],{"class":47}," =",[41,57,59],{"class":58},"sVt8B"," z.",[41,61,63],{"class":62},"sScJk","object",[41,65,66],{"class":58},"({\n",[41,68,70,73,76],{"class":43,"line":69},2,[41,71,72],{"class":58},"  title: z.",[41,74,75],{"class":62},"string",[41,77,78],{"class":58},"(),\n",[41,80,82,85,87],{"class":43,"line":81},3,[41,83,84],{"class":58},"  description: z.",[41,86,75],{"class":62},[41,88,78],{"class":58},[41,90,92,95,97],{"class":43,"line":91},4,[41,93,94],{"class":58},"  date: z.",[41,96,75],{"class":62},[41,98,78],{"class":58},[41,100,102,105,108,111,113,116,119],{"class":43,"line":101},5,[41,103,104],{"class":58},"  tags: z.",[41,106,107],{"class":62},"array",[41,109,110],{"class":58},"(z.",[41,112,75],{"class":62},[41,114,115],{"class":58},"()).",[41,117,118],{"class":62},"default",[41,120,121],{"class":58},"([]),\n",[41,123,125],{"class":43,"line":124},6,[41,126,127],{"class":58},"})\n",[10,129,130,131,134],{},"Typos in frontmatter now fail the build instead of silently rendering ",[14,132,133],{},"undefined",". This is the single biggest quality-of-life win over v2.",[19,136,138],{"id":137},"queries-look-like-sql-because-they-are","Queries look like SQL because they are",[10,140,141],{},"Under the hood, content lives in SQLite. The query API compiles to SQL:",[32,143,145],{"className":34,"code":144,"language":36,"meta":37,"style":37},"const posts = await queryCollection('blog_en')\n  .order('date', 'DESC')\n  .limit(3)\n  .all()\n",[14,146,147,172,193,207],{"__ignoreMap":37},[41,148,149,151,154,156,159,162,165,169],{"class":43,"line":44},[41,150,48],{"class":47},[41,152,153],{"class":51}," posts",[41,155,55],{"class":47},[41,157,158],{"class":47}," await",[41,160,161],{"class":62}," queryCollection",[41,163,164],{"class":58},"(",[41,166,168],{"class":167},"sZZnC","'blog_en'",[41,170,171],{"class":58},")\n",[41,173,174,177,180,182,185,188,191],{"class":43,"line":69},[41,175,176],{"class":58},"  .",[41,178,179],{"class":62},"order",[41,181,164],{"class":58},[41,183,184],{"class":167},"'date'",[41,186,187],{"class":58},", ",[41,189,190],{"class":167},"'DESC'",[41,192,171],{"class":58},[41,194,195,197,200,202,205],{"class":43,"line":81},[41,196,176],{"class":58},[41,198,199],{"class":62},"limit",[41,201,164],{"class":58},[41,203,204],{"class":51},"3",[41,206,171],{"class":58},[41,208,209,211,214],{"class":43,"line":91},[41,210,176],{"class":58},[41,212,213],{"class":62},"all",[41,215,216],{"class":58},"()\n",[19,218,220],{"id":219},"two-languages-two-collections","Two languages, two collections",[10,222,223],{},"For a bilingual site, one collection per locale keeps things simple:",[32,225,230],{"className":226,"code":228,"language":229},[227],"language-text","content\u002F\n  en\u002Fblog\u002F    → collection blog_en, served at \u002Fblog\n  uz\u002Fblog\u002F    → collection blog_uz, served at \u002Fuz\u002Fblog\n","text",[14,231,228],{"__ignoreMap":37},[10,233,234],{},"A tiny composable picks the right collection from the active locale, and the rest of the code never thinks about i18n.",[19,236,238],{"id":237},"the-result","The result",[10,240,241],{},"No server, no database in production, no CMS subscription. Just Markdown files, a git history, and static HTML that any CDN can serve.",[243,244,245],"style",{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}",{"title":37,"searchDepth":69,"depth":81,"links":247},[248,249,250,251],{"id":21,"depth":69,"text":22},{"id":137,"depth":69,"text":138},{"id":219,"depth":69,"text":220},{"id":237,"depth":69,"text":238},"2026-07-14","How this site works under the hood: Markdown collections, SQLite-backed queries, and full static generation with Nuxt.","md",null,{},true,"\u002Fblog\u002Fnuxt-content-static-blog",{"title":5,"description":253},"blog\u002Fnuxt-content-static-blog",[262,263,264],"nuxt","vue","tooling","Vjjtutnkn2zGY1coUA-L_dr9pk_zjeJYAjL0ciEyAk8",[255,267],{"title":268,"path":269,"stem":270,"children":-1},"Hello, world — why I'm starting this blog","\u002Fblog\u002Fhello-world","blog\u002Fhello-world",1784073506410]