[{"data":1,"prerenderedAt":2812},["ShallowReactive",2],{"\u002Fblog\u002F2026\u002Fnuxtcontentschema":3},{"id":4,"title":5,"author":6,"body":7,"content_type":2788,"date_created":2789,"date_modified":2790,"description":2791,"editor":2790,"extension":2792,"guide":2790,"image":2793,"image_alt":2794,"is_list_exclude":2795,"is_manual_image":2795,"is_manual_title":2795,"is_toc":151,"keywords":2796,"meta":2803,"navigation":151,"og_description":2790,"og_image":2790,"og_image_alt":2790,"og_title":2790,"path":70,"peer_order":2804,"project":2790,"projects":2805,"published":2807,"seo":2808,"sitemap":2809,"stem":2810,"tagline":2790,"version":2790,"x_card":2790,"x_creator_handle":2790,"x_description":2790,"x_image":2790,"x_image_alt":2790,"x_title":2790,"__hash__":2811},"content\u002Fblog\u002F2026\u002FNuxtContentSchema.md","Schema for Nuxt Content v3","John Pennock",{"type":8,"value":9,"toc":2775},"minimark",[10,15,19,22,26,39,72,80,87,91,97,341,344,349,352,356,359,364,434,437,441,444,500,503,510,522,702,708,731,845,851,1134,1138,1141,1199,1205,1270,1700,1705,1720,1842,1847,1875,2097,2104,2110,2122,2761,2764,2768,2771],[11,12,14],"h2",{"id":13},"introduction","Introduction",[16,17,18],"p",{},"In my previous blog post about upgrading to Nuxt Content v3, I discussed the new collections system and how to define collections and schemas for your content.  In this article, I'll go into more detail about how I implemented a unified data schema for my content using Zod schemas for both the collection and page front matter, and how this has improved the structure and consistency of my content data.",[16,20,21],{},"Usually, you don't want to reorganize the content system and introduce data schema when you do an upgrade, but in this case, with the introduction of the Zod schema to the content collection, it made it simpler and presented opportunities to solve the previous problems.",[11,23,25],{"id":24},"nuxt-content-collection","Nuxt Content Collection",[16,27,28,29,33,34,38],{},"The introduction of the ",[30,31,32],"strong",{},"collection"," concept in Nuxt Content v3 allows for multiple collections of content and data. You now define multiple collections and for each specify the root directory, files, and schemas in a new configuration file ",[35,36,37],"code",{},"content.config.ts"," with the use of two new functions:",[40,41,42,48,53,60,66],"ol",{},[43,44,45],"li",{},[35,46,47],{},"defineContentConfig()",[43,49,50],{},[35,51,52],{},"defineCollection()",[43,54,55],{},[56,57,59],"a",{"href":58},"\u002Fblog\u002F2026\u002Fnuxtcontentv2to3","Nuxt Content v2 to v3 (with Nuxt v4 Upgrade)",[43,61,62],{},[56,63,65],{"href":64},"\u002Fblog\u002F2026\u002Fnuxtv3to4","Nuxt v3 to v4 Upgrade (with Nuxt Content v3 upgrade)",[43,67,68],{},[56,69,71],{"href":70},"\u002Fblog\u002F2026\u002Fnuxtcontentschema","Nuxt Content v3 Schema and Collections",[16,73,74,75,79],{},"I've covered the basics of ",[56,76,78],{"href":77},"\u002Fblog\u002F2025\u002Fnuxtcontentupgrade#collections","upgrading to collections here",".",[16,81,82,83],{},"I further discussed some other ",[56,84,86],{"href":85},"\u002Fblog\u002F2026\u002Fnuxtcontentupgradev3","Nuxt Content migration issues here",[11,88,90],{"id":89},"zod-schemas","Zod Schemas",[16,92,93,94,96],{},"The new use of Zod schemas in Nuxt Content v3 allows for structured data validation. This structured data schema was incredibly useful, for example here is a basic ",[35,95,47],{}," using a Zod schema.",[98,99,104],"pre",{"className":100,"code":101,"language":102,"meta":103,"style":103},"language-ts shiki shiki-themes min-light min-dark monokai","import { defineCollection, defineContentConfig } from '@nuxt\u002Fcontent'\nimport { z } from 'zod'\n\nexport default defineContentConfig({\n  collections: {\n    blog: defineCollection({\n      type: 'page',\n      source: 'blog\u002F*.md',\n      \u002F\u002F Define custom schema for docs collection\n      schema: z.object({\n        tags: z.array(z.string()),\n        image: z.string(),\n        date: z.date()\n      })\n    })\n  }\n})\n\n","ts","",[35,105,106,133,146,153,169,182,195,209,222,229,249,280,299,317,323,329,335],{"__ignoreMap":103},[107,108,111,115,119,123,126,129],"span",{"class":109,"line":110},"line",1,[107,112,114],{"class":113},"s-2sM","import",[107,116,118],{"class":117},"sxUMQ"," { defineCollection",[107,120,122],{"class":121},"sizxJ",",",[107,124,125],{"class":117}," defineContentConfig } ",[107,127,128],{"class":113},"from",[107,130,132],{"class":131},"shHn5"," '@nuxt\u002Fcontent'\n",[107,134,136,138,141,143],{"class":109,"line":135},2,[107,137,114],{"class":113},[107,139,140],{"class":117}," { z } ",[107,142,128],{"class":113},[107,144,145],{"class":131}," 'zod'\n",[107,147,149],{"class":109,"line":148},3,[107,150,152],{"emptyLinePlaceholder":151},true,"\n",[107,154,156,159,162,166],{"class":109,"line":155},4,[107,157,158],{"class":113},"export",[107,160,161],{"class":113}," default",[107,163,165],{"class":164},"srTi1"," defineContentConfig",[107,167,168],{"class":117},"({\n",[107,170,172,175,179],{"class":109,"line":171},5,[107,173,174],{"class":117},"  collections",[107,176,178],{"class":177},"skixG",":",[107,180,181],{"class":117}," {\n",[107,183,185,188,190,193],{"class":109,"line":184},6,[107,186,187],{"class":117},"    blog",[107,189,178],{"class":177},[107,191,192],{"class":164}," defineCollection",[107,194,168],{"class":117},[107,196,198,201,203,206],{"class":109,"line":197},7,[107,199,200],{"class":117},"      type",[107,202,178],{"class":177},[107,204,205],{"class":131}," 'page'",[107,207,208],{"class":121},",\n",[107,210,212,215,217,220],{"class":109,"line":211},8,[107,213,214],{"class":117},"      source",[107,216,178],{"class":177},[107,218,219],{"class":131}," 'blog\u002F*.md'",[107,221,208],{"class":121},[107,223,225],{"class":109,"line":224},9,[107,226,228],{"class":227},"sNgeA","      \u002F\u002F Define custom schema for docs collection\n",[107,230,232,235,237,241,244,247],{"class":109,"line":231},10,[107,233,234],{"class":117},"      schema",[107,236,178],{"class":177},[107,238,240],{"class":239},"sraLd"," z",[107,242,79],{"class":243},"s_OQ2",[107,245,246],{"class":164},"object",[107,248,168],{"class":117},[107,250,252,255,257,259,261,264,267,270,272,275,278],{"class":109,"line":251},11,[107,253,254],{"class":117},"        tags",[107,256,178],{"class":177},[107,258,240],{"class":239},[107,260,79],{"class":243},[107,262,263],{"class":164},"array",[107,265,266],{"class":117},"(",[107,268,269],{"class":239},"z",[107,271,79],{"class":243},[107,273,274],{"class":164},"string",[107,276,277],{"class":117},"())",[107,279,208],{"class":121},[107,281,283,286,288,290,292,294,297],{"class":109,"line":282},12,[107,284,285],{"class":117},"        image",[107,287,178],{"class":177},[107,289,240],{"class":239},[107,291,79],{"class":243},[107,293,274],{"class":164},[107,295,296],{"class":117},"()",[107,298,208],{"class":121},[107,300,302,305,307,309,311,314],{"class":109,"line":301},13,[107,303,304],{"class":117},"        date",[107,306,178],{"class":177},[107,308,240],{"class":239},[107,310,79],{"class":243},[107,312,313],{"class":164},"date",[107,315,316],{"class":117},"()\n",[107,318,320],{"class":109,"line":319},14,[107,321,322],{"class":117},"      })\n",[107,324,326],{"class":109,"line":325},15,[107,327,328],{"class":117},"    })\n",[107,330,332],{"class":109,"line":331},16,[107,333,334],{"class":117},"  }\n",[107,336,338],{"class":109,"line":337},17,[107,339,340],{"class":117},"})\n",[16,342,343],{},"This Zod schema brings the power of schema design and typing to both JavaScript and TypeScript modules, allowing for consistent data structures and type safety. My previous system was decidedly unstructured and very JavaScript only.",[345,346,348],"h3",{"id":347},"the-big-picture","The Big Picture",[16,350,351],{},"My goals in my content system is that I wanted to give the author of each content page the ability to create content as a single source of truth for the page and and meld system defaults to the page data so each page can have unique SEO metadata and Social Sharing appearances, say having a different image or title for Facebook OpenGraph or Twitter Cards, but also to having default SEO metadata for the site as a whole. I also wanted to be able to easily query against the content and its front matter variables to populate custom controls on the page, such as a list of related articles based on shared tags.  In short I wanted each markdown content page to be self contained and at the control of the author.",[345,353,355],{"id":354},"old-vs-new-data-system","Old vs. New Data System",[16,357,358],{},"To illustrate the new data system, I'll illustrate using different types of data and showing how it was done previously and how it is done in the new system with a schema.  To show the differences, I'll use these data variables as examples.",[360,361,363],"h4",{"id":362},"selected-data-variables","Selected Data Variables",[40,365,366,380,398,407,415,426],{},[43,367,368,371,372,375,376,379],{},[35,369,370],{},"robots"," - ",[30,373,374],{},"default"," variable for the robots meta tag, e.g. 'index, follow', for the ",[30,377,378],{},"APP"," (set once and not per page)",[43,381,382,371,385,387,388,393,394,397],{},[35,383,384],{},"rootUrl",[30,386,374],{}," global variable for the root URL of the site, used for constructing full URLs for social sharing and canonical links, e.g. '",[56,389,390],{"href":390,"rel":391},"https:\u002F\u002Fpennockprojects.com",[392],"nofollow","', for use in the ",[30,395,396],{},"PAGE"," to attach to the page path for full URL construction.",[43,399,400,371,403,406],{},[35,401,402],{},"title",[30,404,405],{},"Front matter"," default front matter variable for the title of the markdown page",[43,408,409,371,412,414],{},[35,410,411],{},"image",[30,413,405],{}," custom front matter variable for the image of the markdown page",[43,416,417,371,420,422,423,425],{},[35,418,419],{},"ogImage",[30,421,405],{}," custom front matter variable for the image of the page when shared on Facebook OpenGraph, which can be different from the default ",[35,424,411],{}," variable for the page when shared on other platforms, e.g. X\u002FTwitter, etc.",[43,427,428,371,431,433],{},[35,429,430],{},"dateCreated",[30,432,405],{}," custom front matter variable for the date created of the markdown page",[16,435,436],{},"The page author could add an image variable to the front matter of the markdown page, and this would override the default image variable for that page if that page was shared on social media.  If the page author didn't include a new image, the default image would be used.  etc.",[345,438,440],{"id":439},"original-system","Original System",[16,442,443],{},"Here is the pseudocode for the old method",[40,445,446,461],{},[43,447,448,449,452,453,456,457,460],{},"In ",[35,450,451],{},"app.vue"," define ",[35,454,455],{},"metaDefaults = {}"," JSON object and ",[35,458,459],{},"provide()"," it.",[43,462,448,463,466,467],{},[35,464,465],{},"[...slug].vue"," (and any custom pages)\n",[468,469,470,476,487],"ul",{},[43,471,472,473,122],{},"query the front-matter content for the page using ",[35,474,475],{},"queryContent()",[43,477,478,479,482,483,486],{},"import ",[35,480,481],{},"setSEO()"," function and call it with page content data, ",[35,484,485],{},"metaDefaults",", and route path.",[43,488,489,490,492,493,496,497,79],{},"with the result of the ",[35,491,481],{}," function, set the SEO metadata for the page with ",[35,494,495],{},"useSeoMeta()"," and ",[35,498,499],{},"useHead()",[16,501,502],{},"All the objects here are POJOs and untyped, and the front matter variables are unstructured and not defined in any schema, so there is a lot of flexibility but also a lot of potential for errors and inconsistencies.",[360,504,506,507,509],{"id":505},"original-appvue-metadata","Original ",[35,508,451],{}," metadata",[16,511,448,512,514,515,517,518,521],{},[35,513,451],{}," I defined a ",[35,516,485],{}," POJO object that contained all the default metadata and then I provided this object to all child components using the ",[35,519,520],{},"provide()\u002Finject()"," function.",[98,523,527],{"className":524,"code":525,"language":526,"meta":103,"style":103},"language-vue shiki shiki-themes min-light min-dark monokai","\u002F\u002F app.vue\n\u003Cscript setup>\n\nconst metaDefaults = {\n  title: 'Pennock Projects',\n  rootUrl: \"https:\u002F\u002Fpennockprojects.com\",\n  robots: 'index, follow',\n  image2x1: '\u002Fimages\u002FPennockProjectsFB.jpg',\n}\n\n\u002F\u002F allow children components readonly access to social defaults.\nprovide(\"metaDefaults\", metaDefaults);\n\n\u002F\u002F Setting Global SEO on each page\nuseSeoMeta({\n  robots: metaDefaults.robots\n  \u002F\u002F other global SEO meta tags can go here as well, e.g. copyright\n})\n\n\u002F\u002F ... rest of app.vue code\n\u003C\u002Fscript>\n\n","vue",[35,528,529,534,549,553,567,579,591,603,615,620,624,629,644,648,653,660,671,676,681,686,692],{"__ignoreMap":103},[107,530,531],{"class":109,"line":110},[107,532,533],{"class":117},"\u002F\u002F app.vue\n",[107,535,536,539,543,546],{"class":109,"line":135},[107,537,538],{"class":117},"\u003C",[107,540,542],{"class":541},"szMGX","script",[107,544,545],{"class":164}," setup",[107,547,548],{"class":117},">\n",[107,550,551],{"class":109,"line":148},[107,552,152],{"emptyLinePlaceholder":151},[107,554,555,559,562,565],{"class":109,"line":155},[107,556,558],{"class":557},"s-Tb5","const",[107,560,561],{"class":239}," metaDefaults",[107,563,564],{"class":113}," =",[107,566,181],{"class":117},[107,568,569,572,574,577],{"class":109,"line":171},[107,570,571],{"class":117},"  title",[107,573,178],{"class":177},[107,575,576],{"class":131}," 'Pennock Projects'",[107,578,208],{"class":121},[107,580,581,584,586,589],{"class":109,"line":184},[107,582,583],{"class":117},"  rootUrl",[107,585,178],{"class":177},[107,587,588],{"class":131}," \"https:\u002F\u002Fpennockprojects.com\"",[107,590,208],{"class":121},[107,592,593,596,598,601],{"class":109,"line":197},[107,594,595],{"class":117},"  robots",[107,597,178],{"class":177},[107,599,600],{"class":131}," 'index, follow'",[107,602,208],{"class":121},[107,604,605,608,610,613],{"class":109,"line":211},[107,606,607],{"class":117},"  image2x1",[107,609,178],{"class":177},[107,611,612],{"class":131}," '\u002Fimages\u002FPennockProjectsFB.jpg'",[107,614,208],{"class":121},[107,616,617],{"class":109,"line":224},[107,618,619],{"class":117},"}\n",[107,621,622],{"class":109,"line":231},[107,623,152],{"emptyLinePlaceholder":151},[107,625,626],{"class":109,"line":251},[107,627,628],{"class":227},"\u002F\u002F allow children components readonly access to social defaults.\n",[107,630,631,634,636,639,641],{"class":109,"line":282},[107,632,633],{"class":164},"provide",[107,635,266],{"class":117},[107,637,638],{"class":131},"\"metaDefaults\"",[107,640,122],{"class":121},[107,642,643],{"class":117}," metaDefaults);\n",[107,645,646],{"class":109,"line":301},[107,647,152],{"emptyLinePlaceholder":151},[107,649,650],{"class":109,"line":319},[107,651,652],{"class":227},"\u002F\u002F Setting Global SEO on each page\n",[107,654,655,658],{"class":109,"line":325},[107,656,657],{"class":164},"useSeoMeta",[107,659,168],{"class":117},[107,661,662,664,666,668],{"class":109,"line":331},[107,663,595],{"class":117},[107,665,178],{"class":177},[107,667,561],{"class":239},[107,669,670],{"class":117},".robots\n",[107,672,673],{"class":109,"line":337},[107,674,675],{"class":227},"  \u002F\u002F other global SEO meta tags can go here as well, e.g. copyright\n",[107,677,679],{"class":109,"line":678},18,[107,680,340],{"class":117},[107,682,684],{"class":109,"line":683},19,[107,685,152],{"emptyLinePlaceholder":151},[107,687,689],{"class":109,"line":688},20,[107,690,691],{"class":227},"\u002F\u002F ... rest of app.vue code\n",[107,693,695,698,700],{"class":109,"line":694},21,[107,696,697],{"class":117},"\u003C\u002F",[107,699,542],{"class":541},[107,701,548],{"class":117},[360,703,506,705,707],{"id":704},"original-slugvue-page",[35,706,465],{}," page",[16,709,448,710,712,713,716,717,719,720,723,724,726,727,496,729,79],{},[35,711,465],{}," (and any custom pages) I obtained the defaults with ",[35,714,715],{},"inject(\"metaDefaults\")"," and then queried the front-matter content for the page using ",[35,718,475],{},", flatten the default front matter with the meta front matter.  Then call an imported shared function ",[35,721,722],{},"setSEO(metaPage, metaDefaults, route.path)"," the page data, default data, and the current route.  Finally with the result of the ",[35,725,481],{}," function, I set the SEO metadata for the page with ",[35,728,495],{},[35,730,499],{},[98,732,734],{"className":524,"code":733,"language":526,"meta":103,"style":103},"const route = useRoute()\nconst metaDefaults = inject(\"metaDefaults\");\n\nconst { data: page } = await useAsyncData(route.path, () => {\n  return queryCollection('content').path(route.path).first()\n})\n\nconst doc = page?.value || {}\n\nconst schemaFrontMatter = {\n  title: doc.title,\n  dateCreated: doc.dateCreated\n}\n\n\u002F\u002F all the other front matter fields are in the `.meta` property of the doc, so we spread those in as well to make it easier to access them in the template and for SEO purposes\nconst metaPage = { ...schemaFrontMatter, ...doc?.meta}\n\n\u002F\u002F Call the blending function\nconst seoSettings = setSEO(metaPage, metaDefaults, route.path)\n\n\u002F\u002F Use the results\nuseHead(() => (seoSettings.head))\nuseSeoMeta(seoSettings.seo)\n\n",[35,735,736,741,746,750,755,760,764,768,773,777,782,787,792,796,800,805,810,814,819,824,828,833,839],{"__ignoreMap":103},[107,737,738],{"class":109,"line":110},[107,739,740],{"class":117},"const route = useRoute()\n",[107,742,743],{"class":109,"line":135},[107,744,745],{"class":117},"const metaDefaults = inject(\"metaDefaults\");\n",[107,747,748],{"class":109,"line":148},[107,749,152],{"emptyLinePlaceholder":151},[107,751,752],{"class":109,"line":155},[107,753,754],{"class":117},"const { data: page } = await useAsyncData(route.path, () => {\n",[107,756,757],{"class":109,"line":171},[107,758,759],{"class":117},"  return queryCollection('content').path(route.path).first()\n",[107,761,762],{"class":109,"line":184},[107,763,340],{"class":117},[107,765,766],{"class":109,"line":197},[107,767,152],{"emptyLinePlaceholder":151},[107,769,770],{"class":109,"line":211},[107,771,772],{"class":117},"const doc = page?.value || {}\n",[107,774,775],{"class":109,"line":224},[107,776,152],{"emptyLinePlaceholder":151},[107,778,779],{"class":109,"line":231},[107,780,781],{"class":117},"const schemaFrontMatter = {\n",[107,783,784],{"class":109,"line":251},[107,785,786],{"class":117},"  title: doc.title,\n",[107,788,789],{"class":109,"line":282},[107,790,791],{"class":117},"  dateCreated: doc.dateCreated\n",[107,793,794],{"class":109,"line":301},[107,795,619],{"class":117},[107,797,798],{"class":109,"line":319},[107,799,152],{"emptyLinePlaceholder":151},[107,801,802],{"class":109,"line":325},[107,803,804],{"class":117},"\u002F\u002F all the other front matter fields are in the `.meta` property of the doc, so we spread those in as well to make it easier to access them in the template and for SEO purposes\n",[107,806,807],{"class":109,"line":331},[107,808,809],{"class":117},"const metaPage = { ...schemaFrontMatter, ...doc?.meta}\n",[107,811,812],{"class":109,"line":337},[107,813,152],{"emptyLinePlaceholder":151},[107,815,816],{"class":109,"line":678},[107,817,818],{"class":117},"\u002F\u002F Call the blending function\n",[107,820,821],{"class":109,"line":683},[107,822,823],{"class":117},"const seoSettings = setSEO(metaPage, metaDefaults, route.path)\n",[107,825,826],{"class":109,"line":688},[107,827,152],{"emptyLinePlaceholder":151},[107,829,830],{"class":109,"line":694},[107,831,832],{"class":117},"\u002F\u002F Use the results\n",[107,834,836],{"class":109,"line":835},22,[107,837,838],{"class":117},"useHead(() => (seoSettings.head))\n",[107,840,842],{"class":109,"line":841},23,[107,843,844],{"class":117},"useSeoMeta(seoSettings.seo)\n",[16,846,847,848,850],{},"And here is the ",[35,849,481],{}," function that blended the page content data with the default metadata and returned the SEO metadata and head settings for the page. Of particular note is how the data is untyped and unstructured.",[98,852,856],{"className":853,"code":854,"language":855,"meta":103,"style":103},"language-js shiki shiki-themes min-light min-dark monokai","\u002F\u002F \u002Fshared\u002Futils\u002FsetSEO.js\nexport const setSEO = (metaPage, metaDefaults, routePath) => {\n\n  let doc = metaPage || {}\n  \n  let seo = {\n    ogTitle: (doc.ogTitle || doc.title),\n    ogImage: metaDefaults.rootUrl +  (doc.ogImage || doc.image || metaDefaults.image2x1),\n    ogUrl: metaDefaults.rootUrl + routePath,\n  }\n\n  let head = {\n    link: [\n      {\n        rel: 'canonical',\n        href: metaDefaults.rootUrl + routePath,\n      },\n    ],\n  }\n\n  return {\n    head,\n    seo \n  }\n}\n","js",[35,857,858,863,899,903,923,928,939,964,1003,1020,1024,1028,1039,1049,1054,1066,1083,1090,1097,1101,1105,1112,1119,1124,1129],{"__ignoreMap":103},[107,859,860],{"class":109,"line":110},[107,861,862],{"class":227},"\u002F\u002F \u002Fshared\u002Futils\u002FsetSEO.js\n",[107,864,865,867,870,873,875,878,882,884,886,888,891,894,897],{"class":109,"line":135},[107,866,158],{"class":113},[107,868,869],{"class":557}," const",[107,871,872],{"class":164}," setSEO",[107,874,564],{"class":113},[107,876,877],{"class":117}," (",[107,879,881],{"class":880},"sA0kQ","metaPage",[107,883,122],{"class":121},[107,885,561],{"class":880},[107,887,122],{"class":121},[107,889,890],{"class":880}," routePath",[107,892,893],{"class":117},") ",[107,895,896],{"class":557},"=>",[107,898,181],{"class":117},[107,900,901],{"class":109,"line":148},[107,902,152],{"emptyLinePlaceholder":151},[107,904,905,908,911,914,917,920],{"class":109,"line":155},[107,906,907],{"class":557},"  let",[107,909,910],{"class":117}," doc ",[107,912,913],{"class":113},"=",[107,915,916],{"class":117}," metaPage ",[107,918,919],{"class":113},"||",[107,921,922],{"class":117}," {}\n",[107,924,925],{"class":109,"line":171},[107,926,927],{"class":117},"  \n",[107,929,930,932,935,937],{"class":109,"line":184},[107,931,907],{"class":557},[107,933,934],{"class":117}," seo ",[107,936,913],{"class":113},[107,938,181],{"class":117},[107,940,941,944,946,948,951,954,956,959,962],{"class":109,"line":197},[107,942,943],{"class":117},"    ogTitle",[107,945,178],{"class":177},[107,947,877],{"class":117},[107,949,950],{"class":239},"doc",[107,952,953],{"class":117},".ogTitle ",[107,955,919],{"class":113},[107,957,958],{"class":239}," doc",[107,960,961],{"class":117},".title)",[107,963,208],{"class":121},[107,965,966,969,971,973,976,979,982,984,987,989,991,994,996,998,1001],{"class":109,"line":211},[107,967,968],{"class":117},"    ogImage",[107,970,178],{"class":177},[107,972,561],{"class":239},[107,974,975],{"class":117},".rootUrl ",[107,977,978],{"class":113},"+",[107,980,981],{"class":117},"  (",[107,983,950],{"class":239},[107,985,986],{"class":117},".ogImage ",[107,988,919],{"class":113},[107,990,958],{"class":239},[107,992,993],{"class":117},".image ",[107,995,919],{"class":113},[107,997,561],{"class":239},[107,999,1000],{"class":117},".image2x1)",[107,1002,208],{"class":121},[107,1004,1005,1008,1010,1012,1014,1016,1018],{"class":109,"line":224},[107,1006,1007],{"class":117},"    ogUrl",[107,1009,178],{"class":177},[107,1011,561],{"class":239},[107,1013,975],{"class":117},[107,1015,978],{"class":113},[107,1017,890],{"class":117},[107,1019,208],{"class":121},[107,1021,1022],{"class":109,"line":231},[107,1023,334],{"class":117},[107,1025,1026],{"class":109,"line":251},[107,1027,152],{"emptyLinePlaceholder":151},[107,1029,1030,1032,1035,1037],{"class":109,"line":282},[107,1031,907],{"class":557},[107,1033,1034],{"class":117}," head ",[107,1036,913],{"class":113},[107,1038,181],{"class":117},[107,1040,1041,1044,1046],{"class":109,"line":301},[107,1042,1043],{"class":117},"    link",[107,1045,178],{"class":177},[107,1047,1048],{"class":117}," [\n",[107,1050,1051],{"class":109,"line":319},[107,1052,1053],{"class":117},"      {\n",[107,1055,1056,1059,1061,1064],{"class":109,"line":325},[107,1057,1058],{"class":117},"        rel",[107,1060,178],{"class":177},[107,1062,1063],{"class":131}," 'canonical'",[107,1065,208],{"class":121},[107,1067,1068,1071,1073,1075,1077,1079,1081],{"class":109,"line":331},[107,1069,1070],{"class":117},"        href",[107,1072,178],{"class":177},[107,1074,561],{"class":239},[107,1076,975],{"class":117},[107,1078,978],{"class":113},[107,1080,890],{"class":117},[107,1082,208],{"class":121},[107,1084,1085,1088],{"class":109,"line":337},[107,1086,1087],{"class":117},"      }",[107,1089,208],{"class":121},[107,1091,1092,1095],{"class":109,"line":678},[107,1093,1094],{"class":117},"    ]",[107,1096,208],{"class":121},[107,1098,1099],{"class":109,"line":683},[107,1100,334],{"class":117},[107,1102,1103],{"class":109,"line":688},[107,1104,152],{"emptyLinePlaceholder":151},[107,1106,1107,1110],{"class":109,"line":694},[107,1108,1109],{"class":113},"  return",[107,1111,181],{"class":117},[107,1113,1114,1117],{"class":109,"line":835},[107,1115,1116],{"class":117},"    head",[107,1118,208],{"class":121},[107,1120,1121],{"class":109,"line":841},[107,1122,1123],{"class":117},"    seo \n",[107,1125,1127],{"class":109,"line":1126},24,[107,1128,334],{"class":117},[107,1130,1132],{"class":109,"line":1131},25,[107,1133,619],{"class":117},[345,1135,1137],{"id":1136},"new-system","New System",[16,1139,1140],{},"Here is the pseudocode for the new system.",[40,1142,1143,1150,1157,1164,1193],{},[43,1144,1145,1146,1149],{},"Created a new TypeScript file ",[35,1147,1148],{},"defaultDataSchema.ts"," that contains data for app defaults, and the schema for page collections, and page data using Zod.",[43,1151,448,1152,1154,1155],{},[35,1153,451],{},"  import default data from ",[35,1156,1148],{},[43,1158,448,1159,1161,1162],{},[35,1160,37],{}," import the schema type for collection page ",[35,1163,52],{},[43,1165,448,1166,1168,1169],{},[35,1167,465],{}," - import the app defaults and page data schema type, and then:\n",[468,1170,1171,1178,1185],{},[43,1172,1173,1174,1177],{},"query the front-matter page variables using ",[35,1175,1176],{},"queryCollection()",", and use the page data schema type to type the result of the query.",[43,1179,478,1180,482,1182,486],{},[35,1181,481],{},[35,1183,1184],{},"defaults",[43,1186,489,1187,492,1189,496,1191,79],{},[35,1188,481],{},[35,1190,495],{},[35,1192,499],{},[43,1194,1195,1196,1198],{},"In shared ",[35,1197,481],{}," function, use the defaults and page data types to blend the return data.",[360,1200,1202,1203],{"id":1201},"new-defaultdataschemats","new ",[35,1204,1148],{},[40,1206,1207,1213,1221,1253,1261],{},[43,1208,1209,1212],{},[35,1210,1211],{},"SeoMetaDefaults"," - TypeScript type for the default SEO metadata for the app.",[43,1214,1215,1217,1218,1220],{},[35,1216,1184],{}," - The default data based on the ",[35,1219,1211],{}," type, which is exported for use throughout the app.",[43,1222,1223,1226,1227,1230,1231],{},[35,1224,1225],{},"PageSchemaCustom"," - Zod schema for the ",[30,1228,1229],{},"custom"," front matter variables.\n",[468,1232,1233,1246],{},[43,1234,1235,1236,1238,1239,1238,1242,1245],{},"Note that you should not include the default front matter variables that Nuxt Content provides at the top level, such as ",[35,1237,402],{},", ",[35,1240,1241],{},"description",[35,1243,1244],{},"navigation",", etc. in this schema, because these are already defined at the top level by Nuxt Content and including them in the custom schema will cause issues with the queryCollection and the front matter variables not being available at the top level.",[43,1247,1248,1249,1252],{},"Note that the front matter variable names ",[30,1250,1251],{},"must"," be snake_case in NuxtContent v3 (breaking change from v2) as they store the variables in a SQLite database for queryCollection (snake_case is a limitation of SQLite).",[43,1254,1255,1258,1259,79],{},[35,1256,1257],{},"PageSchema"," - Zod schema for the page collection, which adds back the default front matter variables by extending the ",[35,1260,1225],{},[43,1262,1263,1266,1267,1269],{},[35,1264,1265],{},"PageMatter"," - A TypeScript type for the all top page front matter variables (default and custom), inferred from the ",[35,1268,1257],{}," Zod schema.",[98,1271,1273],{"className":100,"code":1272,"language":102,"meta":103,"style":103},"import { z } from 'zod'\n\n\u002F\u002F Define the type for the default SEO metadata\nexport type SeoMetaDefaults = {\n  title: string\n  image2x1: string\n  robots: string\n  rootUrl: string\n  \u002F\u002F other default SEO metadata fields can go here as well, e.g. copyright, etc.\n}\n\n\u002F\u002F define the singular default data structure for the app, which can be used in the app and blended with page data for SEO and other purposes. This is the single source of truth for default data for the app.\nexport const defaults: SeoMetaDefaults = {\n  title: 'Pennock Projects',\n  image2x1: '\u002Fimages\u002FPennockProjectsFB.jpg',\n  robots: 'index, follow',\n  rootUrl: \"https:\u002F\u002Fpennockprojects.com\",\n}\n\n\n\u002F\u002F Define the schema for page frontmatter using Zod\n\u002F\u002F Note front-matter variable names **must** be snake_case in NuxtContent v3 (breaking change from v2) as they store the variables in a SQLite database for queryCollection (snake_case is a limitation of SQLite).\nexport const PageSchemaCustom = z\n  .object({\n    image: z.string().optional().nullable(),\n    og_title: z.string().optional().nullable(), \u002F\u002F Open Graph title override\n    og_image: z.string().optional().nullable(), \u002F\u002F Open Graph image override\n    date_created: z.string().optional().nullable().default(''),\n  })\n  \n\u002F\u002F Extend the base PageSchemaCustom with additional default fields for our page frontmatter\nexport const PageSchema = PageSchemaCustom.extend({\n  title: z.string(),\n})\n\n\u002F\u002F Infer the Page frontmatter variable object TypeScript type from the Zod schema\nexport type PageMatter = z.infer\u003Ctypeof PageSchema>;\n\n",[35,1274,1275,1285,1289,1294,1309,1319,1327,1335,1343,1348,1352,1356,1361,1378,1388,1398,1408,1418,1422,1426,1430,1435,1440,1454,1463,1494,1527,1560,1602,1608,1613,1619,1640,1657,1662,1667,1673],{"__ignoreMap":103},[107,1276,1277,1279,1281,1283],{"class":109,"line":110},[107,1278,114],{"class":113},[107,1280,140],{"class":117},[107,1282,128],{"class":113},[107,1284,145],{"class":131},[107,1286,1287],{"class":109,"line":135},[107,1288,152],{"emptyLinePlaceholder":151},[107,1290,1291],{"class":109,"line":148},[107,1292,1293],{"class":227},"\u002F\u002F Define the type for the default SEO metadata\n",[107,1295,1296,1298,1301,1305,1307],{"class":109,"line":155},[107,1297,158],{"class":113},[107,1299,1300],{"class":557}," type",[107,1302,1304],{"class":1303},"sz2Vg"," SeoMetaDefaults",[107,1306,564],{"class":113},[107,1308,181],{"class":117},[107,1310,1311,1313,1315],{"class":109,"line":171},[107,1312,571],{"class":117},[107,1314,178],{"class":113},[107,1316,1318],{"class":1317},"sibI6"," string\n",[107,1320,1321,1323,1325],{"class":109,"line":184},[107,1322,607],{"class":117},[107,1324,178],{"class":113},[107,1326,1318],{"class":1317},[107,1328,1329,1331,1333],{"class":109,"line":197},[107,1330,595],{"class":117},[107,1332,178],{"class":113},[107,1334,1318],{"class":1317},[107,1336,1337,1339,1341],{"class":109,"line":211},[107,1338,583],{"class":117},[107,1340,178],{"class":113},[107,1342,1318],{"class":1317},[107,1344,1345],{"class":109,"line":224},[107,1346,1347],{"class":227},"  \u002F\u002F other default SEO metadata fields can go here as well, e.g. copyright, etc.\n",[107,1349,1350],{"class":109,"line":231},[107,1351,619],{"class":117},[107,1353,1354],{"class":109,"line":251},[107,1355,152],{"emptyLinePlaceholder":151},[107,1357,1358],{"class":109,"line":282},[107,1359,1360],{"class":227},"\u002F\u002F define the singular default data structure for the app, which can be used in the app and blended with page data for SEO and other purposes. This is the single source of truth for default data for the app.\n",[107,1362,1363,1365,1367,1370,1372,1374,1376],{"class":109,"line":301},[107,1364,158],{"class":113},[107,1366,869],{"class":557},[107,1368,1369],{"class":239}," defaults",[107,1371,178],{"class":113},[107,1373,1304],{"class":1303},[107,1375,564],{"class":113},[107,1377,181],{"class":117},[107,1379,1380,1382,1384,1386],{"class":109,"line":319},[107,1381,571],{"class":117},[107,1383,178],{"class":177},[107,1385,576],{"class":131},[107,1387,208],{"class":121},[107,1389,1390,1392,1394,1396],{"class":109,"line":325},[107,1391,607],{"class":117},[107,1393,178],{"class":177},[107,1395,612],{"class":131},[107,1397,208],{"class":121},[107,1399,1400,1402,1404,1406],{"class":109,"line":331},[107,1401,595],{"class":117},[107,1403,178],{"class":177},[107,1405,600],{"class":131},[107,1407,208],{"class":121},[107,1409,1410,1412,1414,1416],{"class":109,"line":337},[107,1411,583],{"class":117},[107,1413,178],{"class":177},[107,1415,588],{"class":131},[107,1417,208],{"class":121},[107,1419,1420],{"class":109,"line":678},[107,1421,619],{"class":117},[107,1423,1424],{"class":109,"line":683},[107,1425,152],{"emptyLinePlaceholder":151},[107,1427,1428],{"class":109,"line":688},[107,1429,152],{"emptyLinePlaceholder":151},[107,1431,1432],{"class":109,"line":694},[107,1433,1434],{"class":227},"\u002F\u002F Define the schema for page frontmatter using Zod\n",[107,1436,1437],{"class":109,"line":835},[107,1438,1439],{"class":227},"\u002F\u002F Note front-matter variable names **must** be snake_case in NuxtContent v3 (breaking change from v2) as they store the variables in a SQLite database for queryCollection (snake_case is a limitation of SQLite).\n",[107,1441,1442,1444,1446,1449,1451],{"class":109,"line":841},[107,1443,158],{"class":113},[107,1445,869],{"class":557},[107,1447,1448],{"class":239}," PageSchemaCustom",[107,1450,564],{"class":113},[107,1452,1453],{"class":117}," z\n",[107,1455,1456,1459,1461],{"class":109,"line":1126},[107,1457,1458],{"class":243},"  .",[107,1460,246],{"class":164},[107,1462,168],{"class":117},[107,1464,1465,1468,1470,1472,1474,1476,1478,1480,1483,1485,1487,1490,1492],{"class":109,"line":1131},[107,1466,1467],{"class":117},"    image",[107,1469,178],{"class":177},[107,1471,240],{"class":239},[107,1473,79],{"class":243},[107,1475,274],{"class":164},[107,1477,296],{"class":117},[107,1479,79],{"class":243},[107,1481,1482],{"class":164},"optional",[107,1484,296],{"class":117},[107,1486,79],{"class":243},[107,1488,1489],{"class":164},"nullable",[107,1491,296],{"class":117},[107,1493,208],{"class":121},[107,1495,1497,1500,1502,1504,1506,1508,1510,1512,1514,1516,1518,1520,1522,1524],{"class":109,"line":1496},26,[107,1498,1499],{"class":117},"    og_title",[107,1501,178],{"class":177},[107,1503,240],{"class":239},[107,1505,79],{"class":243},[107,1507,274],{"class":164},[107,1509,296],{"class":117},[107,1511,79],{"class":243},[107,1513,1482],{"class":164},[107,1515,296],{"class":117},[107,1517,79],{"class":243},[107,1519,1489],{"class":164},[107,1521,296],{"class":117},[107,1523,122],{"class":121},[107,1525,1526],{"class":227}," \u002F\u002F Open Graph title override\n",[107,1528,1530,1533,1535,1537,1539,1541,1543,1545,1547,1549,1551,1553,1555,1557],{"class":109,"line":1529},27,[107,1531,1532],{"class":117},"    og_image",[107,1534,178],{"class":177},[107,1536,240],{"class":239},[107,1538,79],{"class":243},[107,1540,274],{"class":164},[107,1542,296],{"class":117},[107,1544,79],{"class":243},[107,1546,1482],{"class":164},[107,1548,296],{"class":117},[107,1550,79],{"class":243},[107,1552,1489],{"class":164},[107,1554,296],{"class":117},[107,1556,122],{"class":121},[107,1558,1559],{"class":227}," \u002F\u002F Open Graph image override\n",[107,1561,1563,1566,1568,1570,1572,1574,1576,1578,1580,1582,1584,1586,1588,1590,1592,1594,1597,1600],{"class":109,"line":1562},28,[107,1564,1565],{"class":117},"    date_created",[107,1567,178],{"class":177},[107,1569,240],{"class":239},[107,1571,79],{"class":243},[107,1573,274],{"class":164},[107,1575,296],{"class":117},[107,1577,79],{"class":243},[107,1579,1482],{"class":164},[107,1581,296],{"class":117},[107,1583,79],{"class":243},[107,1585,1489],{"class":164},[107,1587,296],{"class":117},[107,1589,79],{"class":243},[107,1591,374],{"class":164},[107,1593,266],{"class":117},[107,1595,1596],{"class":131},"''",[107,1598,1599],{"class":117},")",[107,1601,208],{"class":121},[107,1603,1605],{"class":109,"line":1604},29,[107,1606,1607],{"class":117},"  })\n",[107,1609,1611],{"class":109,"line":1610},30,[107,1612,927],{"class":117},[107,1614,1616],{"class":109,"line":1615},31,[107,1617,1618],{"class":227},"\u002F\u002F Extend the base PageSchemaCustom with additional default fields for our page frontmatter\n",[107,1620,1622,1624,1626,1629,1631,1633,1635,1638],{"class":109,"line":1621},32,[107,1623,158],{"class":113},[107,1625,869],{"class":557},[107,1627,1628],{"class":239}," PageSchema",[107,1630,564],{"class":113},[107,1632,1448],{"class":239},[107,1634,79],{"class":243},[107,1636,1637],{"class":164},"extend",[107,1639,168],{"class":117},[107,1641,1643,1645,1647,1649,1651,1653,1655],{"class":109,"line":1642},33,[107,1644,571],{"class":117},[107,1646,178],{"class":177},[107,1648,240],{"class":239},[107,1650,79],{"class":243},[107,1652,274],{"class":164},[107,1654,296],{"class":117},[107,1656,208],{"class":121},[107,1658,1660],{"class":109,"line":1659},34,[107,1661,340],{"class":117},[107,1663,1665],{"class":109,"line":1664},35,[107,1666,152],{"emptyLinePlaceholder":151},[107,1668,1670],{"class":109,"line":1669},36,[107,1671,1672],{"class":227},"\u002F\u002F Infer the Page frontmatter variable object TypeScript type from the Zod schema\n",[107,1674,1676,1678,1680,1683,1685,1687,1689,1692,1694,1697],{"class":109,"line":1675},37,[107,1677,158],{"class":113},[107,1679,1300],{"class":557},[107,1681,1682],{"class":1303}," PageMatter",[107,1684,564],{"class":113},[107,1686,240],{"class":1303},[107,1688,79],{"class":117},[107,1690,1691],{"class":1303},"infer",[107,1693,538],{"class":117},[107,1695,1696],{"class":113},"typeof",[107,1698,1699],{"class":117}," PageSchema>;\n",[360,1701,1202,1703],{"id":1702},"new-contentconfigts",[35,1704,37],{},[16,1706,448,1707,1709,1710,1712,1713,1716,1717,1719],{},[35,1708,37],{}," I imported the ",[35,1711,1225],{}," Zod schema type and used it in the ",[35,1714,1715],{},"schema"," key of the ",[35,1718,52],{}," function for the page collection.  This way, I can ensure that all my page content adheres to the defined schema and I can easily query against the front matter variables defined in the schema.",[98,1721,1723],{"className":100,"code":1722,"language":102,"meta":103,"style":103},"import { defineContentConfig, defineCollection} from '@nuxt\u002Fcontent'\nimport { PageSchemaCustom } from '.\u002Fshared\u002Futils\u002FdefaultDataSchema'\n\n\nexport default defineContentConfig({\n  collections: {\n    content: defineCollection({\n        type: 'page',\n        source: '**\u002F*.md',\n        schema: PageSchemaCustom,\n    }),\n  },\n})\n",[35,1724,1725,1741,1753,1757,1761,1771,1779,1790,1801,1813,1824,1831,1838],{"__ignoreMap":103},[107,1726,1727,1729,1732,1734,1737,1739],{"class":109,"line":110},[107,1728,114],{"class":113},[107,1730,1731],{"class":117}," { defineContentConfig",[107,1733,122],{"class":121},[107,1735,1736],{"class":117}," defineCollection} ",[107,1738,128],{"class":113},[107,1740,132],{"class":131},[107,1742,1743,1745,1748,1750],{"class":109,"line":135},[107,1744,114],{"class":113},[107,1746,1747],{"class":117}," { PageSchemaCustom } ",[107,1749,128],{"class":113},[107,1751,1752],{"class":131}," '.\u002Fshared\u002Futils\u002FdefaultDataSchema'\n",[107,1754,1755],{"class":109,"line":148},[107,1756,152],{"emptyLinePlaceholder":151},[107,1758,1759],{"class":109,"line":155},[107,1760,152],{"emptyLinePlaceholder":151},[107,1762,1763,1765,1767,1769],{"class":109,"line":171},[107,1764,158],{"class":113},[107,1766,161],{"class":113},[107,1768,165],{"class":164},[107,1770,168],{"class":117},[107,1772,1773,1775,1777],{"class":109,"line":184},[107,1774,174],{"class":117},[107,1776,178],{"class":177},[107,1778,181],{"class":117},[107,1780,1781,1784,1786,1788],{"class":109,"line":197},[107,1782,1783],{"class":117},"    content",[107,1785,178],{"class":177},[107,1787,192],{"class":164},[107,1789,168],{"class":117},[107,1791,1792,1795,1797,1799],{"class":109,"line":211},[107,1793,1794],{"class":117},"        type",[107,1796,178],{"class":177},[107,1798,205],{"class":131},[107,1800,208],{"class":121},[107,1802,1803,1806,1808,1811],{"class":109,"line":224},[107,1804,1805],{"class":117},"        source",[107,1807,178],{"class":177},[107,1809,1810],{"class":131}," '**\u002F*.md'",[107,1812,208],{"class":121},[107,1814,1815,1818,1820,1822],{"class":109,"line":231},[107,1816,1817],{"class":117},"        schema",[107,1819,178],{"class":177},[107,1821,1448],{"class":117},[107,1823,208],{"class":121},[107,1825,1826,1829],{"class":109,"line":251},[107,1827,1828],{"class":117},"    })",[107,1830,208],{"class":121},[107,1832,1833,1836],{"class":109,"line":282},[107,1834,1835],{"class":117},"  }",[107,1837,208],{"class":121},[107,1839,1840],{"class":109,"line":301},[107,1841,340],{"class":117},[360,1843,1202,1845,707],{"id":1844},"new-slugvue-page",[35,1846,465],{},[16,1848,448,1849,1851,1852,1854,1855,1857,1858,1860,1861,1863,1864,1867,1868,726,1870,496,1872,1874],{},[35,1850,465],{}," (and any custom pages) I imported the ",[35,1853,1265],{}," types from ",[35,1856,1148],{}," and then queried the front-matter page variables using ",[35,1859,1176],{},", and used the ",[35,1862,1265],{}," type to type the result of the query.  Then I called an imported shared function ",[35,1865,1866],{},"setSEO(page?.value || {}, route.path)"," Finally with the result of the ",[35,1869,481],{},[35,1871,495],{},[35,1873,499],{},".  The data is now typed and structured based on the Zod schema.",[98,1876,1878],{"className":524,"code":1877,"language":526,"meta":103,"style":103},"\u003Cscript setup lang=\"ts\">\nimport { setSEO } from '~\u002Fshared\u002Futils\u002FsetSEO';\nimport type { PageMatter } from '~\u002Fshared\u002Futils\u002FdefaultDataSchema';\n\nconst route = useRoute()\n\nconst { data: page } = await useAsyncData(route.path, () => {\n  return queryCollection('content').path(route.path).first()\n});\n\nconst seoSettings = setSEO(page?.value || {}, route.path)\n\nuseHead(seoSettings.headData)\nuseSeoMeta(seoSettings.seoMetaData)\n\u003C\u002Fscript>\n",[35,1879,1880,1898,1913,1929,1933,1947,1951,1989,2022,2027,2031,2061,2065,2078,2089],{"__ignoreMap":103},[107,1881,1882,1884,1886,1888,1891,1893,1896],{"class":109,"line":110},[107,1883,538],{"class":117},[107,1885,542],{"class":541},[107,1887,545],{"class":164},[107,1889,1890],{"class":164}," lang",[107,1892,913],{"class":177},[107,1894,1895],{"class":131},"\"ts\"",[107,1897,548],{"class":117},[107,1899,1900,1902,1905,1907,1910],{"class":109,"line":135},[107,1901,114],{"class":113},[107,1903,1904],{"class":117}," { setSEO } ",[107,1906,128],{"class":113},[107,1908,1909],{"class":131}," '~\u002Fshared\u002Futils\u002FsetSEO'",[107,1911,1912],{"class":117},";\n",[107,1914,1915,1917,1919,1922,1924,1927],{"class":109,"line":148},[107,1916,114],{"class":113},[107,1918,1300],{"class":113},[107,1920,1921],{"class":117}," { PageMatter } ",[107,1923,128],{"class":113},[107,1925,1926],{"class":131}," '~\u002Fshared\u002Futils\u002FdefaultDataSchema'",[107,1928,1912],{"class":117},[107,1930,1931],{"class":109,"line":155},[107,1932,152],{"emptyLinePlaceholder":151},[107,1934,1935,1937,1940,1942,1945],{"class":109,"line":171},[107,1936,558],{"class":557},[107,1938,1939],{"class":239}," route",[107,1941,564],{"class":113},[107,1943,1944],{"class":164}," useRoute",[107,1946,316],{"class":117},[107,1948,1949],{"class":109,"line":184},[107,1950,152],{"emptyLinePlaceholder":151},[107,1952,1953,1955,1958,1961,1964,1966,1969,1972,1974,1977,1980,1982,1985,1987],{"class":109,"line":197},[107,1954,558],{"class":557},[107,1956,1957],{"class":117}," { data: ",[107,1959,1960],{"class":239},"page",[107,1962,1963],{"class":117}," } ",[107,1965,913],{"class":113},[107,1967,1968],{"class":113}," await",[107,1970,1971],{"class":164}," useAsyncData",[107,1973,266],{"class":117},[107,1975,1976],{"class":239},"route",[107,1978,1979],{"class":117},".path",[107,1981,122],{"class":121},[107,1983,1984],{"class":117}," () ",[107,1986,896],{"class":557},[107,1988,181],{"class":117},[107,1990,1991,1993,1996,1998,2001,2003,2005,2008,2010,2012,2015,2017,2020],{"class":109,"line":211},[107,1992,1109],{"class":113},[107,1994,1995],{"class":164}," queryCollection",[107,1997,266],{"class":117},[107,1999,2000],{"class":131},"'content'",[107,2002,1599],{"class":117},[107,2004,79],{"class":243},[107,2006,2007],{"class":164},"path",[107,2009,266],{"class":117},[107,2011,1976],{"class":239},[107,2013,2014],{"class":117},".path)",[107,2016,79],{"class":243},[107,2018,2019],{"class":164},"first",[107,2021,316],{"class":117},[107,2023,2024],{"class":109,"line":224},[107,2025,2026],{"class":117},"});\n",[107,2028,2029],{"class":109,"line":231},[107,2030,152],{"emptyLinePlaceholder":151},[107,2032,2033,2035,2038,2040,2042,2044,2046,2049,2051,2054,2056,2058],{"class":109,"line":251},[107,2034,558],{"class":557},[107,2036,2037],{"class":239}," seoSettings",[107,2039,564],{"class":113},[107,2041,872],{"class":164},[107,2043,266],{"class":117},[107,2045,1960],{"class":239},[107,2047,2048],{"class":117},"?.value ",[107,2050,919],{"class":113},[107,2052,2053],{"class":117}," {}",[107,2055,122],{"class":121},[107,2057,1939],{"class":239},[107,2059,2060],{"class":117},".path)\n",[107,2062,2063],{"class":109,"line":282},[107,2064,152],{"emptyLinePlaceholder":151},[107,2066,2067,2070,2072,2075],{"class":109,"line":301},[107,2068,2069],{"class":164},"useHead",[107,2071,266],{"class":117},[107,2073,2074],{"class":239},"seoSettings",[107,2076,2077],{"class":117},".headData)\n",[107,2079,2080,2082,2084,2086],{"class":109,"line":319},[107,2081,657],{"class":164},[107,2083,266],{"class":117},[107,2085,2074],{"class":239},[107,2087,2088],{"class":117},".seoMetaData)\n",[107,2090,2091,2093,2095],{"class":109,"line":325},[107,2092,697],{"class":117},[107,2094,542],{"class":541},[107,2096,548],{"class":117},[16,2098,2099,2100,2103],{},"Note, I also use ",[35,2101,2102],{},"lang=\"ts\""," in the script tag to enable TypeScript for this Vue component.",[345,2105,1202,2107,2109],{"id":2106},"new-setseo-function",[35,2108,481],{}," function",[16,2111,2112,2113,2115,2116,2118,2119,2121],{},"In the shared ",[35,2114,481],{}," function, I used the ",[35,2117,1257],{}," schema and ",[35,2120,1265],{}," type.  I also imported the default data.  The function blended the page content data with the default metadata and returned the SEO metadata and head settings for the page. The data is now typed and structured based on the Zod schema.",[98,2123,2125],{"className":100,"code":2124,"language":102,"meta":103,"style":103},"import type { ContentCollectionItem } from '@nuxt\u002Fcontent'\nimport type { UseHeadInput, UseSeoMetaInput } from '@unhead\u002Fvue'\nimport { PageSchema, type PageMatter } from '~\u002Fshared\u002Futils\u002FdefaultDataSchema'\nimport { defaults } from '~\u002Fshared\u002Futils\u002FdefaultDataSchema'\n\ntype UseHeadAndSeoInput = {\n  headData: UseHeadInput\n  seoMetaData: UseSeoMetaInput\n  pageData: PageMatter\n}\n\nexport const setSEO = (\n  pageRaw: ContentCollectionItem | Record\u003Cstring, any>, \u002F\u002F Accept either a ContentCollectionItem or a plain object for page front matter data\n  routePath: string\n): UseHeadAndSeoInput => {\n\n  let pageData: PageMatter  = {} as PageMatter;\n\n  try {\n    pageData = PageSchema.parse(pageRaw);\n\n  } catch (error) {\n    console.error('Error parsing page front matter with schema:', error);\n    pageData = pageData || {} as PageMatter; \u002F\u002F Ensure pageData is at least an empty object if parsing fails\n  }\n\n  const ogTitle = pageData.og_title || pageData.title  \n  const finalOgTitle = ogTitle && ogTitle !== defaults.title\n    ? `${defaults.title} ${ogTitle}`\n    : defaults.title\n  const ogImage = pageData.og_image ?? pageData.image ?? defaults.image2x1\n  const ogUrl = defaults.rootUrl + (pageData.path ?? routePath)\n\n  const headData: UseHeadInput = {\n    link: [\n      {\n        rel: 'canonical',\n        href: defaults.rootUrl + routePath,\n      },\n    ],\n  }\n\n  const seoMetaData: UseSeoMetaInput = {\n    ogTitle: finalOgTitle,\n    ogImage: defaults.rootUrl + ogImage,\n    ogUrl,\n  }\n\n  return {\n    headData,\n    seoMetaData,\n    pageData,\n  }\n}\n",[35,2126,2127,2140,2159,2178,2189,2193,2205,2215,2225,2235,2239,2243,2256,2289,2298,2311,2315,2339,2343,2350,2367,2371,2382,2402,2425,2429,2433,2455,2480,2510,2519,2547,2575,2579,2595,2603,2607,2617,2634,2641,2648,2653,2658,2675,2686,2703,2710,2715,2720,2727,2735,2743,2751,2756],{"__ignoreMap":103},[107,2128,2129,2131,2133,2136,2138],{"class":109,"line":110},[107,2130,114],{"class":113},[107,2132,1300],{"class":113},[107,2134,2135],{"class":117}," { ContentCollectionItem } ",[107,2137,128],{"class":113},[107,2139,132],{"class":131},[107,2141,2142,2144,2146,2149,2151,2154,2156],{"class":109,"line":135},[107,2143,114],{"class":113},[107,2145,1300],{"class":113},[107,2147,2148],{"class":117}," { UseHeadInput",[107,2150,122],{"class":121},[107,2152,2153],{"class":117}," UseSeoMetaInput } ",[107,2155,128],{"class":113},[107,2157,2158],{"class":131}," '@unhead\u002Fvue'\n",[107,2160,2161,2163,2166,2168,2170,2173,2175],{"class":109,"line":148},[107,2162,114],{"class":113},[107,2164,2165],{"class":117}," { PageSchema",[107,2167,122],{"class":121},[107,2169,1300],{"class":113},[107,2171,2172],{"class":117}," PageMatter } ",[107,2174,128],{"class":113},[107,2176,2177],{"class":131}," '~\u002Fshared\u002Futils\u002FdefaultDataSchema'\n",[107,2179,2180,2182,2185,2187],{"class":109,"line":155},[107,2181,114],{"class":113},[107,2183,2184],{"class":117}," { defaults } ",[107,2186,128],{"class":113},[107,2188,2177],{"class":131},[107,2190,2191],{"class":109,"line":171},[107,2192,152],{"emptyLinePlaceholder":151},[107,2194,2195,2198,2201,2203],{"class":109,"line":184},[107,2196,2197],{"class":557},"type",[107,2199,2200],{"class":1303}," UseHeadAndSeoInput",[107,2202,564],{"class":113},[107,2204,181],{"class":117},[107,2206,2207,2210,2212],{"class":109,"line":197},[107,2208,2209],{"class":117},"  headData",[107,2211,178],{"class":113},[107,2213,2214],{"class":1303}," UseHeadInput\n",[107,2216,2217,2220,2222],{"class":109,"line":211},[107,2218,2219],{"class":117},"  seoMetaData",[107,2221,178],{"class":113},[107,2223,2224],{"class":1303}," UseSeoMetaInput\n",[107,2226,2227,2230,2232],{"class":109,"line":224},[107,2228,2229],{"class":117},"  pageData",[107,2231,178],{"class":113},[107,2233,2234],{"class":1303}," PageMatter\n",[107,2236,2237],{"class":109,"line":231},[107,2238,619],{"class":117},[107,2240,2241],{"class":109,"line":251},[107,2242,152],{"emptyLinePlaceholder":151},[107,2244,2245,2247,2249,2251,2253],{"class":109,"line":282},[107,2246,158],{"class":113},[107,2248,869],{"class":557},[107,2250,872],{"class":164},[107,2252,564],{"class":113},[107,2254,2255],{"class":117}," (\n",[107,2257,2258,2261,2263,2266,2269,2272,2274,2276,2278,2281,2284,2286],{"class":109,"line":301},[107,2259,2260],{"class":880},"  pageRaw",[107,2262,178],{"class":113},[107,2264,2265],{"class":1303}," ContentCollectionItem",[107,2267,2268],{"class":113}," |",[107,2270,2271],{"class":1303}," Record",[107,2273,538],{"class":117},[107,2275,274],{"class":1317},[107,2277,122],{"class":121},[107,2279,2280],{"class":1317}," any",[107,2282,2283],{"class":117},">",[107,2285,122],{"class":121},[107,2287,2288],{"class":227}," \u002F\u002F Accept either a ContentCollectionItem or a plain object for page front matter data\n",[107,2290,2291,2294,2296],{"class":109,"line":319},[107,2292,2293],{"class":880},"  routePath",[107,2295,178],{"class":113},[107,2297,1318],{"class":1317},[107,2299,2300,2302,2304,2306,2309],{"class":109,"line":325},[107,2301,1599],{"class":117},[107,2303,178],{"class":113},[107,2305,2200],{"class":1303},[107,2307,2308],{"class":557}," =>",[107,2310,181],{"class":117},[107,2312,2313],{"class":109,"line":331},[107,2314,152],{"emptyLinePlaceholder":151},[107,2316,2317,2319,2322,2324,2326,2329,2332,2335,2337],{"class":109,"line":337},[107,2318,907],{"class":557},[107,2320,2321],{"class":117}," pageData",[107,2323,178],{"class":113},[107,2325,1682],{"class":1303},[107,2327,2328],{"class":113},"  =",[107,2330,2331],{"class":117}," {} ",[107,2333,2334],{"class":113},"as",[107,2336,1682],{"class":1303},[107,2338,1912],{"class":117},[107,2340,2341],{"class":109,"line":678},[107,2342,152],{"emptyLinePlaceholder":151},[107,2344,2345,2348],{"class":109,"line":683},[107,2346,2347],{"class":113},"  try",[107,2349,181],{"class":117},[107,2351,2352,2355,2357,2359,2361,2364],{"class":109,"line":688},[107,2353,2354],{"class":117},"    pageData ",[107,2356,913],{"class":113},[107,2358,1628],{"class":239},[107,2360,79],{"class":243},[107,2362,2363],{"class":164},"parse",[107,2365,2366],{"class":117},"(pageRaw);\n",[107,2368,2369],{"class":109,"line":694},[107,2370,152],{"emptyLinePlaceholder":151},[107,2372,2373,2376,2379],{"class":109,"line":835},[107,2374,2375],{"class":117},"  } ",[107,2377,2378],{"class":113},"catch",[107,2380,2381],{"class":117}," (error) {\n",[107,2383,2384,2387,2389,2392,2394,2397,2399],{"class":109,"line":841},[107,2385,2386],{"class":239},"    console",[107,2388,79],{"class":243},[107,2390,2391],{"class":164},"error",[107,2393,266],{"class":117},[107,2395,2396],{"class":131},"'Error parsing page front matter with schema:'",[107,2398,122],{"class":121},[107,2400,2401],{"class":117}," error);\n",[107,2403,2404,2406,2408,2411,2413,2415,2417,2419,2422],{"class":109,"line":1126},[107,2405,2354],{"class":117},[107,2407,913],{"class":113},[107,2409,2410],{"class":117}," pageData ",[107,2412,919],{"class":113},[107,2414,2331],{"class":117},[107,2416,2334],{"class":113},[107,2418,1682],{"class":1303},[107,2420,2421],{"class":117},"; ",[107,2423,2424],{"class":227},"\u002F\u002F Ensure pageData is at least an empty object if parsing fails\n",[107,2426,2427],{"class":109,"line":1131},[107,2428,334],{"class":117},[107,2430,2431],{"class":109,"line":1496},[107,2432,152],{"emptyLinePlaceholder":151},[107,2434,2435,2438,2441,2443,2445,2448,2450,2452],{"class":109,"line":1529},[107,2436,2437],{"class":557},"  const",[107,2439,2440],{"class":239}," ogTitle",[107,2442,564],{"class":113},[107,2444,2321],{"class":239},[107,2446,2447],{"class":117},".og_title ",[107,2449,919],{"class":113},[107,2451,2321],{"class":239},[107,2453,2454],{"class":117},".title  \n",[107,2456,2457,2459,2462,2464,2467,2470,2472,2475,2477],{"class":109,"line":1562},[107,2458,2437],{"class":557},[107,2460,2461],{"class":239}," finalOgTitle",[107,2463,564],{"class":113},[107,2465,2466],{"class":117}," ogTitle ",[107,2468,2469],{"class":113},"&&",[107,2471,2466],{"class":117},[107,2473,2474],{"class":113},"!==",[107,2476,1369],{"class":239},[107,2478,2479],{"class":117},".title\n",[107,2481,2482,2485,2488,2491,2493,2496,2499,2502,2505,2507],{"class":109,"line":1604},[107,2483,2484],{"class":113},"    ?",[107,2486,2487],{"class":131}," `",[107,2489,2490],{"class":113},"${",[107,2492,1184],{"class":239},[107,2494,2495],{"class":117},".title",[107,2497,2498],{"class":113},"}",[107,2500,2501],{"class":113}," ${",[107,2503,2504],{"class":117},"ogTitle",[107,2506,2498],{"class":113},[107,2508,2509],{"class":131},"`\n",[107,2511,2512,2515,2517],{"class":109,"line":1610},[107,2513,2514],{"class":113},"    :",[107,2516,1369],{"class":239},[107,2518,2479],{"class":117},[107,2520,2521,2523,2526,2528,2530,2533,2536,2538,2540,2542,2544],{"class":109,"line":1615},[107,2522,2437],{"class":557},[107,2524,2525],{"class":239}," ogImage",[107,2527,564],{"class":113},[107,2529,2321],{"class":239},[107,2531,2532],{"class":117},".og_image ",[107,2534,2535],{"class":113},"??",[107,2537,2321],{"class":239},[107,2539,993],{"class":117},[107,2541,2535],{"class":113},[107,2543,1369],{"class":239},[107,2545,2546],{"class":117},".image2x1\n",[107,2548,2549,2551,2554,2556,2558,2560,2562,2564,2567,2570,2572],{"class":109,"line":1621},[107,2550,2437],{"class":557},[107,2552,2553],{"class":239}," ogUrl",[107,2555,564],{"class":113},[107,2557,1369],{"class":239},[107,2559,975],{"class":117},[107,2561,978],{"class":113},[107,2563,877],{"class":117},[107,2565,2566],{"class":239},"pageData",[107,2568,2569],{"class":117},".path ",[107,2571,2535],{"class":113},[107,2573,2574],{"class":117}," routePath)\n",[107,2576,2577],{"class":109,"line":1642},[107,2578,152],{"emptyLinePlaceholder":151},[107,2580,2581,2583,2586,2588,2591,2593],{"class":109,"line":1659},[107,2582,2437],{"class":557},[107,2584,2585],{"class":239}," headData",[107,2587,178],{"class":113},[107,2589,2590],{"class":1303}," UseHeadInput",[107,2592,564],{"class":113},[107,2594,181],{"class":117},[107,2596,2597,2599,2601],{"class":109,"line":1664},[107,2598,1043],{"class":117},[107,2600,178],{"class":177},[107,2602,1048],{"class":117},[107,2604,2605],{"class":109,"line":1669},[107,2606,1053],{"class":117},[107,2608,2609,2611,2613,2615],{"class":109,"line":1675},[107,2610,1058],{"class":117},[107,2612,178],{"class":177},[107,2614,1063],{"class":131},[107,2616,208],{"class":121},[107,2618,2620,2622,2624,2626,2628,2630,2632],{"class":109,"line":2619},38,[107,2621,1070],{"class":117},[107,2623,178],{"class":177},[107,2625,1369],{"class":239},[107,2627,975],{"class":117},[107,2629,978],{"class":113},[107,2631,890],{"class":117},[107,2633,208],{"class":121},[107,2635,2637,2639],{"class":109,"line":2636},39,[107,2638,1087],{"class":117},[107,2640,208],{"class":121},[107,2642,2644,2646],{"class":109,"line":2643},40,[107,2645,1094],{"class":117},[107,2647,208],{"class":121},[107,2649,2651],{"class":109,"line":2650},41,[107,2652,334],{"class":117},[107,2654,2656],{"class":109,"line":2655},42,[107,2657,152],{"emptyLinePlaceholder":151},[107,2659,2661,2663,2666,2668,2671,2673],{"class":109,"line":2660},43,[107,2662,2437],{"class":557},[107,2664,2665],{"class":239}," seoMetaData",[107,2667,178],{"class":113},[107,2669,2670],{"class":1303}," UseSeoMetaInput",[107,2672,564],{"class":113},[107,2674,181],{"class":117},[107,2676,2678,2680,2682,2684],{"class":109,"line":2677},44,[107,2679,943],{"class":117},[107,2681,178],{"class":177},[107,2683,2461],{"class":117},[107,2685,208],{"class":121},[107,2687,2689,2691,2693,2695,2697,2699,2701],{"class":109,"line":2688},45,[107,2690,968],{"class":117},[107,2692,178],{"class":177},[107,2694,1369],{"class":239},[107,2696,975],{"class":117},[107,2698,978],{"class":113},[107,2700,2525],{"class":117},[107,2702,208],{"class":121},[107,2704,2706,2708],{"class":109,"line":2705},46,[107,2707,1007],{"class":117},[107,2709,208],{"class":121},[107,2711,2713],{"class":109,"line":2712},47,[107,2714,334],{"class":117},[107,2716,2718],{"class":109,"line":2717},48,[107,2719,152],{"emptyLinePlaceholder":151},[107,2721,2723,2725],{"class":109,"line":2722},49,[107,2724,1109],{"class":113},[107,2726,181],{"class":117},[107,2728,2730,2733],{"class":109,"line":2729},50,[107,2731,2732],{"class":117},"    headData",[107,2734,208],{"class":121},[107,2736,2738,2741],{"class":109,"line":2737},51,[107,2739,2740],{"class":117},"    seoMetaData",[107,2742,208],{"class":121},[107,2744,2746,2749],{"class":109,"line":2745},52,[107,2747,2748],{"class":117},"    pageData",[107,2750,208],{"class":121},[107,2752,2754],{"class":109,"line":2753},53,[107,2755,334],{"class":117},[107,2757,2759],{"class":109,"line":2758},54,[107,2760,619],{"class":117},[16,2762,2763],{},"Note also that I added error handling for the Zod schema parsing, so if there is an issue with the page front matter data not adhering to the schema, it will log an error and continue with an empty page data object.",[345,2765,2767],{"id":2766},"new-system-benefits","New System Benefits",[16,2769,2770],{},"The new system with Zod schemas provides a more structured and typed approach to handling content data and SEO metadata. The key data objects of the content are defined in one place and then used by the various parts of the system. It allows for better validation of content front matter, easier querying against defined variables, and a more maintainable codebase with clear data structures. The use of TypeScript types also enhances the developer experience by providing type safety and autocompletion in the code editor.",[2772,2773,2774],"style",{},"html pre.shiki code .s-2sM, html code.shiki .s-2sM{--shiki-default:#D32F2F;--shiki-dark:#F97583;--shiki-sepia:#F92672}html pre.shiki code .sxUMQ, html code.shiki .sxUMQ{--shiki-default:#24292EFF;--shiki-dark:#B392F0;--shiki-sepia:#F8F8F2}html pre.shiki code .sizxJ, html code.shiki .sizxJ{--shiki-default:#212121;--shiki-dark:#BBBBBB;--shiki-sepia:#F8F8F2}html pre.shiki code .shHn5, html code.shiki .shHn5{--shiki-default:#22863A;--shiki-dark:#FFAB70;--shiki-sepia:#E6DB74}html pre.shiki code .srTi1, html code.shiki .srTi1{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#A6E22E}html pre.shiki code .skixG, html code.shiki .skixG{--shiki-default:#D32F2F;--shiki-dark:#F97583;--shiki-sepia:#F8F8F2}html pre.shiki code .sNgeA, html code.shiki .sNgeA{--shiki-default:#C2C3C5;--shiki-dark:#6B737C;--shiki-sepia:#88846F}html pre.shiki code .sraLd, html code.shiki .sraLd{--shiki-default:#1976D2;--shiki-dark:#79B8FF;--shiki-sepia:#F8F8F2}html pre.shiki code .s_OQ2, html code.shiki .s_OQ2{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#F8F8F2}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 .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .szMGX, html code.shiki .szMGX{--shiki-default:#22863A;--shiki-dark:#FFAB70;--shiki-sepia:#F92672}html pre.shiki code .s-Tb5, html code.shiki .s-Tb5{--shiki-default:#D32F2F;--shiki-default-font-style:inherit;--shiki-dark:#F97583;--shiki-dark-font-style:inherit;--shiki-sepia:#66D9EF;--shiki-sepia-font-style:italic}html pre.shiki code .sA0kQ, html code.shiki .sA0kQ{--shiki-default:#24292EFF;--shiki-default-font-style:inherit;--shiki-dark:#B392F0;--shiki-dark-font-style:inherit;--shiki-sepia:#FD971F;--shiki-sepia-font-style:italic}html pre.shiki code .sz2Vg, html code.shiki .sz2Vg{--shiki-default:#6F42C1;--shiki-default-text-decoration:inherit;--shiki-dark:#B392F0;--shiki-dark-text-decoration:inherit;--shiki-sepia:#A6E22E;--shiki-sepia-text-decoration:underline}html pre.shiki code .sibI6, html code.shiki .sibI6{--shiki-default:#1976D2;--shiki-default-font-style:inherit;--shiki-dark:#79B8FF;--shiki-dark-font-style:inherit;--shiki-sepia:#66D9EF;--shiki-sepia-font-style:italic}",{"title":103,"searchDepth":135,"depth":135,"links":2776},[2777,2778,2779],{"id":13,"depth":135,"text":14},{"id":24,"depth":135,"text":25},{"id":89,"depth":135,"text":90,"children":2780},[2781,2782,2783,2784,2785,2787],{"id":347,"depth":148,"text":348},{"id":354,"depth":148,"text":355},{"id":439,"depth":148,"text":440},{"id":1136,"depth":148,"text":1137},{"id":2106,"depth":148,"text":2786},"new setSEO() function",{"id":2766,"depth":148,"text":2767},"how-to","2026-05-19",null,"A system for a unified data schema for Nuxt Content v3 using Zod schemas for content collections and page front matter.","md","\u002Fimages\u002Fblog\u002FDataSchemaTrack.webp","A sphere labeled `Data Schema` rolling on a roller-coaster like track with various stopping platforms with signs about what good data schema looks like.",false,[2797,2798,2799,2800,1715,2801,2802],"nuxt","nuxt content","markdown","collections","zod","front matter",{},0,[2806],"JAMStart","draft",{"title":5,"description":2791},{"loc":70},"blog\u002F2026\u002FNuxtContentSchema","w1Q1Obybqb3YxshSOV22lZ0RpuSp-Xc18HhaE3gtQu0",1780531291633]