[{"data":1,"prerenderedAt":164},["ShallowReactive",2],{"blog-building-this-cv-with-nuxt":3},{"id":4,"title":5,"body":6,"date":150,"description":151,"extension":152,"image":153,"meta":154,"navigation":155,"next":153,"path":156,"prev":153,"seo":157,"series":153,"stem":158,"tags":159,"__hash__":163,"prevPost":153,"nextPost":153},"blog\u002Fblog\u002Fbuilding-this-cv-with-nuxt.md","Building this CV site with Nuxt 4",{"type":7,"value":8,"toc":145},"minimark",[9,13,18,21,101,105,134,138,141],[10,11,12],"p",{},"This site is a small case study in doing a lot with a little. The goal was a personal CV that\nloads instantly, costs nothing to host, and stays easy to maintain.",[14,15,17],"h2",{"id":16},"the-approach","The approach",[10,19,20],{},"Every page is pre-rendered at build time, so visitors get static HTML. Only one route stays\ndynamic — the contact form endpoint — because it needs a server-side API key.",[22,23,28],"pre",{"className":24,"code":25,"language":26,"meta":27,"style":27},"language-ts shiki shiki-themes github-light github-dark","export default defineNuxtConfig({\n  nitro: {\n    preset: 'node-server',\n    prerender: { crawlLinks: true, routes: ['\u002F'] },\n  },\n})\n","ts","",[29,30,31,51,57,70,89,95],"code",{"__ignoreMap":27},[32,33,36,40,43,47],"span",{"class":34,"line":35},"line",1,[32,37,39],{"class":38},"szBVR","export",[32,41,42],{"class":38}," default",[32,44,46],{"class":45},"sScJk"," defineNuxtConfig",[32,48,50],{"class":49},"sVt8B","({\n",[32,52,54],{"class":34,"line":53},2,[32,55,56],{"class":49},"  nitro: {\n",[32,58,60,63,67],{"class":34,"line":59},3,[32,61,62],{"class":49},"    preset: ",[32,64,66],{"class":65},"sZZnC","'node-server'",[32,68,69],{"class":49},",\n",[32,71,73,76,80,83,86],{"class":34,"line":72},4,[32,74,75],{"class":49},"    prerender: { crawlLinks: ",[32,77,79],{"class":78},"sj4cs","true",[32,81,82],{"class":49},", routes: [",[32,84,85],{"class":65},"'\u002F'",[32,87,88],{"class":49},"] },\n",[32,90,92],{"class":34,"line":91},5,[32,93,94],{"class":49},"  },\n",[32,96,98],{"class":34,"line":97},6,[32,99,100],{"class":49},"})\n",[14,102,104],{"id":103},"what-went-in","What went in",[106,107,108,116,122,128],"ul",{},[109,110,111,115],"li",{},[112,113,114],"strong",{},"Nuxt 4"," with TypeScript for the app shell",[109,117,118,121],{},[112,119,120],{},"Tailwind CSS v4"," for styling, with theme-aware design tokens",[109,123,124,127],{},[112,125,126],{},"Nuxt Content"," for this blog",[109,129,130,133],{},[112,131,132],{},"Resend"," for the contact form, behind a rate-limited, validated endpoint",[14,135,137],{"id":136},"what-i-optimised-for","What I optimised for",[10,139,140],{},"Performance and accessibility were non-negotiable: self-hosted fonts, responsive images, reduced\nmotion support, and a strict content security policy. The result is a site that feels instant and\nscores well without any manual babysitting.",[142,143,144],"style",{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}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);}",{"title":27,"searchDepth":53,"depth":53,"links":146},[147,148,149],{"id":16,"depth":53,"text":17},{"id":103,"depth":53,"text":104},{"id":136,"depth":53,"text":137},"2026-07-13","How I built a fast, minimal, self-hosted portfolio with Nuxt 4, static generation, and a Resend contact form.","md",null,{},true,"\u002Fblog\u002Fbuilding-this-cv-with-nuxt",{"title":5,"description":151},"blog\u002Fbuilding-this-cv-with-nuxt",[160,161,162],"Nuxt","Performance","DX","q8zPKpKaY7iVwEeD2HPd7eVh98gONSaNkJgGWrWOf9c",1786402986405]