[{"data":1,"prerenderedAt":556},["ShallowReactive",2],{"post-\u002Fblog\u002Fwhy-nuxt-content":3},{"id":4,"title":5,"body":6,"date":543,"description":544,"draft":545,"extension":54,"meta":546,"navigation":103,"path":547,"readingTime":548,"seo":549,"stem":550,"tags":551,"__hash__":555},"blog\u002Fblog\u002Fwhy-nuxt-content.md","Why Nuxt Content for a personal site",{"type":7,"value":8,"toc":537},"minimark",[9,13,31,41,46,49,111,114,118,125,317,320,324,327,443,446,489,492,496,499,517,524,530,533],[10,11,12],"p",{},"When I rebuilt my personal site I had three requirements:",[14,15,16,20,28],"ol",{},[17,18,19],"li",{},"Writing should feel like writing, not like configuring a CMS.",[17,21,22,23,27],{},"The output should be ",[24,25,26],"strong",{},"static"," — fast, cheap, and boring to maintain.",[17,29,30],{},"Types everywhere, because I am that kind of person.",[10,32,33,40],{},[34,35,39],"a",{"href":36,"rel":37},"https:\u002F\u002Fcontent.nuxt.com",[38],"nofollow","Nuxt Content"," hit all three.",[42,43,45],"h2",{"id":44},"markdown-lives-in-the-repo","Markdown lives in the repo",[10,47,48],{},"Every post is just a file:",[50,51,56],"pre",{"className":52,"code":53,"language":54,"meta":55,"style":55},"language-md shiki shiki-themes github-dark github-dark","---\ntitle: Why Nuxt Content for a personal site\ndescription: Markdown in a Git repo, typed frontmatter...\ndate: 2026-08-14\ntags: [nuxt, content, cloudflare]\n---\n\nContent goes here.\n","md","",[57,58,59,68,75,81,87,93,98,105],"code",{"__ignoreMap":55},[60,61,64],"span",{"class":62,"line":63},"line",1,[60,65,67],{"class":66},"sNs27","---\n",[60,69,71],{"class":62,"line":70},2,[60,72,74],{"class":73},"suv1-","title: Why Nuxt Content for a personal site\n",[60,76,78],{"class":62,"line":77},3,[60,79,80],{"class":73},"description: Markdown in a Git repo, typed frontmatter...\n",[60,82,84],{"class":62,"line":83},4,[60,85,86],{"class":73},"date: 2026-08-14\n",[60,88,90],{"class":62,"line":89},5,[60,91,92],{"class":73},"tags: [nuxt, content, cloudflare]\n",[60,94,96],{"class":62,"line":95},6,[60,97,67],{"class":66},[60,99,101],{"class":62,"line":100},7,[60,102,104],{"emptyLinePlaceholder":103},true,"\n",[60,106,108],{"class":62,"line":107},8,[60,109,110],{"class":73},"Content goes here.\n",[10,112,113],{},"Commit a file, and it is content. No database, no admin panel, no login to forget.",[42,115,117],{"id":116},"typed-frontmatter-with-zod","Typed frontmatter with Zod",[10,119,120,121,124],{},"The part I love most is the schema. In ",[57,122,123],{},"content.config.ts"," I describe exactly what a post looks like:",[50,126,130],{"className":127,"code":128,"language":129,"meta":55,"style":55},"language-ts shiki shiki-themes github-dark github-dark","import { defineContentConfig, defineCollection, z } from '@nuxt\u002Fcontent'\n\nexport default defineContentConfig({\n  collections: {\n    blog: defineCollection({\n      type: 'page',\n      source: 'blog\u002F**\u002F*.md',\n      schema: z.object({\n        title: z.string(),\n        description: z.string(),\n        date: z.date(),\n        tags: z.array(z.string()).default([]),\n        draft: z.boolean().default(false)\n      })\n    })\n  }\n})\n","ts",[57,131,132,148,152,167,172,182,193,203,213,225,235,246,269,293,299,305,311],{"__ignoreMap":55},[60,133,134,138,141,144],{"class":62,"line":63},[60,135,137],{"class":136},"sOPea","import",[60,139,140],{"class":73}," { defineContentConfig, defineCollection, z } ",[60,142,143],{"class":136},"from",[60,145,147],{"class":146},"s4wv1"," '@nuxt\u002Fcontent'\n",[60,149,150],{"class":62,"line":70},[60,151,104],{"emptyLinePlaceholder":103},[60,153,154,157,160,164],{"class":62,"line":77},[60,155,156],{"class":136},"export",[60,158,159],{"class":136}," default",[60,161,163],{"class":162},"sFR8T"," defineContentConfig",[60,165,166],{"class":73},"({\n",[60,168,169],{"class":62,"line":83},[60,170,171],{"class":73},"  collections: {\n",[60,173,174,177,180],{"class":62,"line":89},[60,175,176],{"class":73},"    blog: ",[60,178,179],{"class":162},"defineCollection",[60,181,166],{"class":73},[60,183,184,187,190],{"class":62,"line":95},[60,185,186],{"class":73},"      type: ",[60,188,189],{"class":146},"'page'",[60,191,192],{"class":73},",\n",[60,194,195,198,201],{"class":62,"line":100},[60,196,197],{"class":73},"      source: ",[60,199,200],{"class":146},"'blog\u002F**\u002F*.md'",[60,202,192],{"class":73},[60,204,205,208,211],{"class":62,"line":107},[60,206,207],{"class":73},"      schema: z.",[60,209,210],{"class":162},"object",[60,212,166],{"class":73},[60,214,216,219,222],{"class":62,"line":215},9,[60,217,218],{"class":73},"        title: z.",[60,220,221],{"class":162},"string",[60,223,224],{"class":73},"(),\n",[60,226,228,231,233],{"class":62,"line":227},10,[60,229,230],{"class":73},"        description: z.",[60,232,221],{"class":162},[60,234,224],{"class":73},[60,236,238,241,244],{"class":62,"line":237},11,[60,239,240],{"class":73},"        date: z.",[60,242,243],{"class":162},"date",[60,245,224],{"class":73},[60,247,249,252,255,258,260,263,266],{"class":62,"line":248},12,[60,250,251],{"class":73},"        tags: z.",[60,253,254],{"class":162},"array",[60,256,257],{"class":73},"(z.",[60,259,221],{"class":162},[60,261,262],{"class":73},"()).",[60,264,265],{"class":162},"default",[60,267,268],{"class":73},"([]),\n",[60,270,272,275,278,281,283,286,290],{"class":62,"line":271},13,[60,273,274],{"class":73},"        draft: z.",[60,276,277],{"class":162},"boolean",[60,279,280],{"class":73},"().",[60,282,265],{"class":162},[60,284,285],{"class":73},"(",[60,287,289],{"class":288},"s8ozJ","false",[60,291,292],{"class":73},")\n",[60,294,296],{"class":62,"line":295},14,[60,297,298],{"class":73},"      })\n",[60,300,302],{"class":62,"line":301},15,[60,303,304],{"class":73},"    })\n",[60,306,308],{"class":62,"line":307},16,[60,309,310],{"class":73},"  }\n",[60,312,314],{"class":62,"line":313},17,[60,315,316],{"class":73},"})\n",[10,318,319],{},"Now a typo in a date fails the build instead of shipping silently. That is the deal I want.",[42,321,323],{"id":322},"querying-is-a-one-liner","Querying is a one-liner",[10,325,326],{},"Listing posts, newest first, draft-free:",[50,328,330],{"className":127,"code":329,"language":129,"meta":55,"style":55},"const { data: posts } = await useAsyncData('blog-list', () =>\n  queryCollection('blog')\n    .where('draft', '=', false)\n    .order('date', 'DESC')\n    .all()\n)\n",[57,331,332,373,385,410,429,439],{"__ignoreMap":55},[60,333,334,337,340,344,347,350,353,356,359,362,364,367,370],{"class":62,"line":63},[60,335,336],{"class":136},"const",[60,338,339],{"class":73}," { ",[60,341,343],{"class":342},"s-3mD","data",[60,345,346],{"class":73},": ",[60,348,349],{"class":288},"posts",[60,351,352],{"class":73}," } ",[60,354,355],{"class":136},"=",[60,357,358],{"class":136}," await",[60,360,361],{"class":162}," useAsyncData",[60,363,285],{"class":73},[60,365,366],{"class":146},"'blog-list'",[60,368,369],{"class":73},", () ",[60,371,372],{"class":136},"=>\n",[60,374,375,378,380,383],{"class":62,"line":70},[60,376,377],{"class":162},"  queryCollection",[60,379,285],{"class":73},[60,381,382],{"class":146},"'blog'",[60,384,292],{"class":73},[60,386,387,390,393,395,398,401,404,406,408],{"class":62,"line":77},[60,388,389],{"class":73},"    .",[60,391,392],{"class":162},"where",[60,394,285],{"class":73},[60,396,397],{"class":146},"'draft'",[60,399,400],{"class":73},", ",[60,402,403],{"class":146},"'='",[60,405,400],{"class":73},[60,407,289],{"class":288},[60,409,292],{"class":73},[60,411,412,414,417,419,422,424,427],{"class":62,"line":83},[60,413,389],{"class":73},[60,415,416],{"class":162},"order",[60,418,285],{"class":73},[60,420,421],{"class":146},"'date'",[60,423,400],{"class":73},[60,425,426],{"class":146},"'DESC'",[60,428,292],{"class":73},[60,430,431,433,436],{"class":62,"line":89},[60,432,389],{"class":73},[60,434,435],{"class":162},"all",[60,437,438],{"class":73},"()\n",[60,440,441],{"class":62,"line":95},[60,442,292],{"class":73},[10,444,445],{},"And rendering a post:",[50,447,451],{"className":448,"code":449,"language":450,"meta":55,"style":55},"language-vue shiki shiki-themes github-dark github-dark","\u003CContentRenderer :value=\"post\" class=\"prose\" \u002F>\n","vue",[57,452,453],{"__ignoreMap":55},[60,454,455,458,462,465,468,470,473,476,478,481,483,486],{"class":62,"line":63},[60,456,457],{"class":73},"\u003C",[60,459,461],{"class":460},"sxg3X","ContentRenderer",[60,463,464],{"class":73}," :",[60,466,467],{"class":162},"value",[60,469,355],{"class":73},[60,471,472],{"class":146},"\"",[60,474,475],{"class":73},"post",[60,477,472],{"class":146},[60,479,480],{"class":162}," class",[60,482,355],{"class":73},[60,484,485],{"class":146},"\"prose\"",[60,487,488],{"class":73}," \u002F>\n",[10,490,491],{},"That is the whole blog.",[42,493,495],{"id":494},"static-on-cloudflare-pages","Static on Cloudflare Pages",[10,497,498],{},"Because everything is generated at build time, I do not need a server. The build command is literally:",[50,500,504],{"className":501,"code":502,"language":503,"meta":55,"style":55},"language-bash shiki shiki-themes github-dark github-dark","npm run generate\n","bash",[57,505,506],{"__ignoreMap":55},[60,507,508,511,514],{"class":62,"line":63},[60,509,510],{"class":162},"npm",[60,512,513],{"class":146}," run",[60,515,516],{"class":146}," generate\n",[10,518,519,520,523],{},"The output lands in ",[57,521,522],{},".output\u002Fpublic",", and Cloudflare Pages serves it from the edge. Free tier, instant cache invalidation on deploy, done.",[525,526,527],"blockquote",{},[10,528,529],{},"A personal site should cost you a coffee, not a subscription.",[10,531,532],{},"If you are on the fence: pick the boring static option. Your future self, half-asleep at 2am debugging a cache, will thank you.",[534,535,536],"style",{},"html pre.shiki code .sNs27, html code.shiki .sNs27{--shiki-default:#79B8FF;--shiki-default-font-weight:bold;--shiki-dark:#79B8FF;--shiki-dark-font-weight:bold}html pre.shiki code .suv1-, html code.shiki .suv1-{--shiki-default:#E1E4E8;--shiki-dark:#E1E4E8}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 .sOPea, html code.shiki .sOPea{--shiki-default:#F97583;--shiki-dark:#F97583}html pre.shiki code .s4wv1, html code.shiki .s4wv1{--shiki-default:#9ECBFF;--shiki-dark:#9ECBFF}html pre.shiki code .sFR8T, html code.shiki .sFR8T{--shiki-default:#B392F0;--shiki-dark:#B392F0}html pre.shiki code .s8ozJ, html code.shiki .s8ozJ{--shiki-default:#79B8FF;--shiki-dark:#79B8FF}html pre.shiki code .s-3mD, html code.shiki .s-3mD{--shiki-default:#FFAB70;--shiki-dark:#FFAB70}html pre.shiki code .sxg3X, html code.shiki .sxg3X{--shiki-default:#85E89D;--shiki-dark:#85E89D}",{"title":55,"searchDepth":70,"depth":70,"links":538},[539,540,541,542],{"id":44,"depth":70,"text":45},{"id":116,"depth":70,"text":117},{"id":322,"depth":70,"text":323},{"id":494,"depth":70,"text":495},"2026-08-14","Markdown in a Git repo, typed frontmatter, and a static build that deploys to Cloudflare Pages for free. Here is the setup I actually use.",false,{},"\u002Fblog\u002Fwhy-nuxt-content","6 min read",{"title":5,"description":544},"blog\u002Fwhy-nuxt-content",[552,553,554],"nuxt","content","cloudflare","nyXiUzN8c4sTUcWBs2AKXR7MVF73zzdPtBueJAtLubg",1789383482962]