[{"data":1,"prerenderedAt":1324},["ShallowReactive",2],{"\u002Fblog\u002F2026\u002Fnuxtcontentv2to3":3},{"id":4,"title":5,"author":6,"body":7,"content_type":1297,"date_created":1298,"date_modified":1299,"description":1300,"editor":1299,"extension":1301,"guide":1299,"image":1302,"image_alt":1303,"is_list_exclude":1304,"is_manual_image":1304,"is_manual_title":1304,"is_toc":418,"keywords":1305,"meta":1314,"navigation":418,"og_description":1299,"og_image":1299,"og_image_alt":1299,"og_title":1299,"path":1315,"peer_order":1316,"project":1299,"projects":1317,"published":1319,"seo":1320,"sitemap":1321,"stem":1322,"tagline":1299,"version":1299,"x_card":1299,"x_creator_handle":1299,"x_description":1299,"x_image":1299,"x_image_alt":1299,"x_title":1299,"__hash__":1323},"content\u002Fblog\u002F2026\u002FNuxtContentV2to3.md","NuxtContent v2 to v3 Migration","John Pennock",{"type":8,"value":9,"toc":1284},"minimark",[10,15,45,48,91,94,114,118,135,141,432,435,449,455,702,705,758,762,787,817,821,836,840,851,861,892,934,940,994,1020,1033,1055,1064,1095,1098,1168,1171,1260,1267,1271,1280],[11,12,14],"h2",{"id":13},"introduction","Introduction",[16,17,18,19,26,27,31,32,26,36,39,40,44],"p",{},"Nuxt Content is critical for my static site projects. It is the content management system (CMS) that powers my blogs and project pages. In this case I had to upgrade my blog site ",[20,21,25],"a",{"href":22,"rel":23},"https:\u002F\u002Fpennockprojects.com",[24],"nofollow","Pennock Projects"," ",[28,29,30],"em",{},"Nuxt Content"," to v3 ",[33,34,35],"strong",{},"and",[28,37,38],{},"Nuxt"," framework to v4 at the same time. It was necessary to take advantage of the new features, dependencies, security improvements, and other enhancements.  If you are interested in the Nuxt v4 upgrade issues, I have a separate blog post about that ",[20,41,43],{"href":42},"\u002Fblog\u002F2026\u002Fnuxtupgradev4","here",".  In this article, I'll focus on my experience with upgrading Nuxt Content, the challenges I faced, and how I resolved them.",[16,46,47],{},"Issues discussed in this article include:",[49,50,51,59,66,69,72,81],"ol",{},[52,53,54,55],"li",{},"Upgrading Nuxt Content configuration in ",[56,57,58],"code",{},"nuxt.config.ts",[52,60,61,62,65],{},"Querying content with ",[56,63,64],{},"queryCollection()"," and the new collections system",[52,67,68],{},"Frontmatter variables and snake_case requirement",[52,70,71],{},"Zod schemas for defining collections",[52,73,74,75,77,78],{},"Using ",[56,76,64],{}," to query path in a similar way with v2 ",[56,79,80],{},"queryContent()",[52,82,83,84,87,88,90],{},"Naming ",[56,85,86],{},"useAsyncAwait"," calls with ",[56,89,64],{}," to avoid hydration errors",[16,92,93],{},"Related blog posts about Nuxt Content v3 and Nuxt v4 upgrades:",[49,95,96,102,108],{},[52,97,98],{},[20,99,101],{"href":100},"\u002Fblog\u002F2026\u002Fnuxtv3to4","Nuxt v3 to v4 Upgrade",[52,103,104],{},[20,105,107],{"href":106},"\u002Fblog\u002F2026\u002Fnuxtcontentschema","Nuxt Content v3 Schema and Collections",[52,109,110],{},[20,111,113],{"href":112},"\u002Fblog\u002F2025\u002Fnuxtcontentupgrade","Nuxt Content v2 to v3 Upgrade on Nuxt v3",[11,115,117],{"id":116},"_1-configuration","1. Configuration",[16,119,120,121,124,125,127,128,131,132,134],{},"The first issue I encountered was upgrading the Nuxt Content configuration in the ",[56,122,123],{},"content"," key of the ",[56,126,58],{}," file. It took a while to figure out how to migrate them. I initially thought that they would just move over to ",[56,129,130],{},"content.config.ts",", but it actually just changed keys in ",[56,133,58],{},".",[16,136,137,138,140],{},"Here is the original content configuration in ",[56,139,58],{}," for Nuxt Content v2:",[142,143,148],"pre",{"className":144,"code":145,"language":146,"meta":147,"style":147},"language-ts shiki shiki-themes min-light min-dark monokai","export default defineNuxtConfig({\n  \u002F\u002F main Nuxt Content for v2 configuration in nuxt.config.ts\n  content: {\n    markdown: {\n      anchorLinks: false,\n      remarkPlugins: ['remark-unwrap-images']\n    },\n    highlight: {\n      theme: {\n        default: 'min-light',\n        dark: 'min-dark',\n      },\n      langs: [ \n        'json', 'js', 'typescript', 'ts', 'html', 'css', 'vue', 'shell', 'mdc', 'markdown', 'yaml',\n        'asm', 'c', 'cpp', 'python', 'reg', 'terraform']\n    }\n  },\n\n  \u002F\u002F other Nuxt configuration...\n})\n","ts","",[56,149,150,170,177,190,200,216,234,242,252,262,275,288,296,307,366,399,405,413,420,426],{"__ignoreMap":147},[151,152,155,159,162,166],"span",{"class":153,"line":154},"line",1,[151,156,158],{"class":157},"s-2sM","export",[151,160,161],{"class":157}," default",[151,163,165],{"class":164},"srTi1"," defineNuxtConfig",[151,167,169],{"class":168},"sxUMQ","({\n",[151,171,173],{"class":153,"line":172},2,[151,174,176],{"class":175},"sNgeA","  \u002F\u002F main Nuxt Content for v2 configuration in nuxt.config.ts\n",[151,178,180,183,187],{"class":153,"line":179},3,[151,181,182],{"class":168},"  content",[151,184,186],{"class":185},"skixG",":",[151,188,189],{"class":168}," {\n",[151,191,193,196,198],{"class":153,"line":192},4,[151,194,195],{"class":168},"    markdown",[151,197,186],{"class":185},[151,199,189],{"class":168},[151,201,203,206,208,212],{"class":153,"line":202},5,[151,204,205],{"class":168},"      anchorLinks",[151,207,186],{"class":185},[151,209,211],{"class":210},"snb_r"," false",[151,213,215],{"class":214},"sizxJ",",\n",[151,217,219,222,224,227,231],{"class":153,"line":218},6,[151,220,221],{"class":168},"      remarkPlugins",[151,223,186],{"class":185},[151,225,226],{"class":168}," [",[151,228,230],{"class":229},"shHn5","'remark-unwrap-images'",[151,232,233],{"class":168},"]\n",[151,235,237,240],{"class":153,"line":236},7,[151,238,239],{"class":168},"    }",[151,241,215],{"class":214},[151,243,245,248,250],{"class":153,"line":244},8,[151,246,247],{"class":168},"    highlight",[151,249,186],{"class":185},[151,251,189],{"class":168},[151,253,255,258,260],{"class":153,"line":254},9,[151,256,257],{"class":168},"      theme",[151,259,186],{"class":185},[151,261,189],{"class":168},[151,263,265,268,270,273],{"class":153,"line":264},10,[151,266,267],{"class":168},"        default",[151,269,186],{"class":185},[151,271,272],{"class":229}," 'min-light'",[151,274,215],{"class":214},[151,276,278,281,283,286],{"class":153,"line":277},11,[151,279,280],{"class":168},"        dark",[151,282,186],{"class":185},[151,284,285],{"class":229}," 'min-dark'",[151,287,215],{"class":214},[151,289,291,294],{"class":153,"line":290},12,[151,292,293],{"class":168},"      }",[151,295,215],{"class":214},[151,297,299,302,304],{"class":153,"line":298},13,[151,300,301],{"class":168},"      langs",[151,303,186],{"class":185},[151,305,306],{"class":168}," [ \n",[151,308,310,313,316,319,321,324,326,329,331,334,336,339,341,344,346,349,351,354,356,359,361,364],{"class":153,"line":309},14,[151,311,312],{"class":229},"        'json'",[151,314,315],{"class":214},",",[151,317,318],{"class":229}," 'js'",[151,320,315],{"class":214},[151,322,323],{"class":229}," 'typescript'",[151,325,315],{"class":214},[151,327,328],{"class":229}," 'ts'",[151,330,315],{"class":214},[151,332,333],{"class":229}," 'html'",[151,335,315],{"class":214},[151,337,338],{"class":229}," 'css'",[151,340,315],{"class":214},[151,342,343],{"class":229}," 'vue'",[151,345,315],{"class":214},[151,347,348],{"class":229}," 'shell'",[151,350,315],{"class":214},[151,352,353],{"class":229}," 'mdc'",[151,355,315],{"class":214},[151,357,358],{"class":229}," 'markdown'",[151,360,315],{"class":214},[151,362,363],{"class":229}," 'yaml'",[151,365,215],{"class":214},[151,367,369,372,374,377,379,382,384,387,389,392,394,397],{"class":153,"line":368},15,[151,370,371],{"class":229},"        'asm'",[151,373,315],{"class":214},[151,375,376],{"class":229}," 'c'",[151,378,315],{"class":214},[151,380,381],{"class":229}," 'cpp'",[151,383,315],{"class":214},[151,385,386],{"class":229}," 'python'",[151,388,315],{"class":214},[151,390,391],{"class":229}," 'reg'",[151,393,315],{"class":214},[151,395,396],{"class":229}," 'terraform'",[151,398,233],{"class":168},[151,400,402],{"class":153,"line":401},16,[151,403,404],{"class":168},"    }\n",[151,406,408,411],{"class":153,"line":407},17,[151,409,410],{"class":168},"  }",[151,412,215],{"class":214},[151,414,416],{"class":153,"line":415},18,[151,417,419],{"emptyLinePlaceholder":418},true,"\n",[151,421,423],{"class":153,"line":422},19,[151,424,425],{"class":175},"  \u002F\u002F other Nuxt configuration...\n",[151,427,429],{"class":153,"line":428},20,[151,430,431],{"class":168},"})\n",[16,433,434],{},"You can see the main configuration features I needed was:",[49,436,437,440,443,446],{},[52,438,439],{},"'remark-unwrap-images' plugin for markdown to unwrap images from paragraphs",[52,441,442],{},"Custom languages for syntax highlighting in code blocks in markdown files",[52,444,445],{},"Dark Mode and Color Themes",[52,447,448],{},"Remove anchor links from markdown headers",[16,450,451,452,454],{},"Here is the new content configuration in ",[56,453,58],{}," for Nuxt Content v3:",[142,456,458],{"className":144,"code":457,"language":146,"meta":147,"style":147},"export default defineNuxtConfig({\n  content: {\n    build: {\n      markdown: {\n        highlight: {\n          theme: {\n            default: 'min-light',\n            dark: 'min-dark',\n          },\n          langs: [ \n            'asm', 'c', 'cpp', 'python', 'reg', 'terraform', 'diff'\n          ]\n        },\n        remarkPlugins: {\n          'remark-unwrap-images': {} \u002F\u002F No options needed for this plugin\n        },\n        toc: {\n          depth: 2\n        },\n      }\n    },\n    renderer: {\n      anchorLinks: false\n    }\n  },\n  \u002F\u002F other Nuxt configuration...\n})\n",[56,459,460,470,478,487,496,505,514,525,536,543,552,582,587,594,603,616,622,631,642,648,653,660,670,680,685,692,697],{"__ignoreMap":147},[151,461,462,464,466,468],{"class":153,"line":154},[151,463,158],{"class":157},[151,465,161],{"class":157},[151,467,165],{"class":164},[151,469,169],{"class":168},[151,471,472,474,476],{"class":153,"line":172},[151,473,182],{"class":168},[151,475,186],{"class":185},[151,477,189],{"class":168},[151,479,480,483,485],{"class":153,"line":179},[151,481,482],{"class":168},"    build",[151,484,186],{"class":185},[151,486,189],{"class":168},[151,488,489,492,494],{"class":153,"line":192},[151,490,491],{"class":168},"      markdown",[151,493,186],{"class":185},[151,495,189],{"class":168},[151,497,498,501,503],{"class":153,"line":202},[151,499,500],{"class":168},"        highlight",[151,502,186],{"class":185},[151,504,189],{"class":168},[151,506,507,510,512],{"class":153,"line":218},[151,508,509],{"class":168},"          theme",[151,511,186],{"class":185},[151,513,189],{"class":168},[151,515,516,519,521,523],{"class":153,"line":236},[151,517,518],{"class":168},"            default",[151,520,186],{"class":185},[151,522,272],{"class":229},[151,524,215],{"class":214},[151,526,527,530,532,534],{"class":153,"line":244},[151,528,529],{"class":168},"            dark",[151,531,186],{"class":185},[151,533,285],{"class":229},[151,535,215],{"class":214},[151,537,538,541],{"class":153,"line":254},[151,539,540],{"class":168},"          }",[151,542,215],{"class":214},[151,544,545,548,550],{"class":153,"line":264},[151,546,547],{"class":168},"          langs",[151,549,186],{"class":185},[151,551,306],{"class":168},[151,553,554,557,559,561,563,565,567,569,571,573,575,577,579],{"class":153,"line":277},[151,555,556],{"class":229},"            'asm'",[151,558,315],{"class":214},[151,560,376],{"class":229},[151,562,315],{"class":214},[151,564,381],{"class":229},[151,566,315],{"class":214},[151,568,386],{"class":229},[151,570,315],{"class":214},[151,572,391],{"class":229},[151,574,315],{"class":214},[151,576,396],{"class":229},[151,578,315],{"class":214},[151,580,581],{"class":229}," 'diff'\n",[151,583,584],{"class":153,"line":290},[151,585,586],{"class":168},"          ]\n",[151,588,589,592],{"class":153,"line":298},[151,590,591],{"class":168},"        }",[151,593,215],{"class":214},[151,595,596,599,601],{"class":153,"line":309},[151,597,598],{"class":168},"        remarkPlugins",[151,600,186],{"class":185},[151,602,189],{"class":168},[151,604,605,608,610,613],{"class":153,"line":368},[151,606,607],{"class":229},"          'remark-unwrap-images'",[151,609,186],{"class":185},[151,611,612],{"class":168}," {} ",[151,614,615],{"class":175},"\u002F\u002F No options needed for this plugin\n",[151,617,618,620],{"class":153,"line":401},[151,619,591],{"class":168},[151,621,215],{"class":214},[151,623,624,627,629],{"class":153,"line":407},[151,625,626],{"class":168},"        toc",[151,628,186],{"class":185},[151,630,189],{"class":168},[151,632,633,636,638],{"class":153,"line":415},[151,634,635],{"class":168},"          depth",[151,637,186],{"class":185},[151,639,641],{"class":640},"s0Ixy"," 2\n",[151,643,644,646],{"class":153,"line":422},[151,645,591],{"class":168},[151,647,215],{"class":214},[151,649,650],{"class":153,"line":428},[151,651,652],{"class":168},"      }\n",[151,654,656,658],{"class":153,"line":655},21,[151,657,239],{"class":168},[151,659,215],{"class":214},[151,661,663,666,668],{"class":153,"line":662},22,[151,664,665],{"class":168},"    renderer",[151,667,186],{"class":185},[151,669,189],{"class":168},[151,671,673,675,677],{"class":153,"line":672},23,[151,674,205],{"class":168},[151,676,186],{"class":185},[151,678,679],{"class":210}," false\n",[151,681,683],{"class":153,"line":682},24,[151,684,404],{"class":168},[151,686,688,690],{"class":153,"line":687},25,[151,689,410],{"class":168},[151,691,215],{"class":214},[151,693,695],{"class":153,"line":694},26,[151,696,425],{"class":175},[151,698,700],{"class":153,"line":699},27,[151,701,431],{"class":168},[16,703,704],{},"The main changes in the configuration were:",[49,706,707,714,725,738,751],{},[52,708,709,710,713],{},"The markdown configuration is now nested under a ",[56,711,712],{},"build"," key, which is a bit confusing.  I think it is because it is part of the static site generation process?  I'm not actually sure about this, as it is not actually related to the build process as I understand it.",[52,715,716,717,720,721,724],{},"The ",[56,718,719],{},"remark-unwrap-images"," plugin is now configured as a key name in the ",[56,722,723],{},"remark-plugins"," object, you add an empty object as the value.  (Note remark-plugins seem to be discontinued in favor of rehype-plugins, but this one is still supported and works fine for unwrapping images from paragraphs in markdown files and I already had enough upgrades to worry about).",[52,726,716,727,730,731,734,735,737],{},[56,728,729],{},"anchorLinks"," key is now nested under a new ",[56,732,733],{},"renderer"," key instead of the ",[56,736,712],{}," key like everything else.",[52,739,716,740,743,744,747,748,750],{},[56,741,742],{},"langs"," key is really only for additional languages beyond the defaults, so I removed the default languages ",[56,745,746],{},"['json', 'js', 'ts', 'html', 'css', 'vue', 'shell', 'mdc', 'md', 'yaml']"," from the ",[56,749,742],{}," array and just left the custom languages I added for syntax highlighting in code blocks in markdown files.",[52,752,753,754,757],{},"Added a new ",[56,755,756],{},"toc"," depth key to configure the table of contents generation for markdown files, I set the depth to 2 to generate a table of contents for headers up to h3.  Without this configuration all headers, specifically h4, were included in the table of contents, which was too much for my liking.",[11,759,761],{"id":760},"_2-frontmatter-queries","2. Frontmatter Queries",[16,763,764,765,767,768,771,772,775,776,771,779,782,783,786],{},"The second issue I encountered in querying my content data with ",[56,766,64],{}," was related to the additional metadata in the frontmatter variables.  In Nuxt Content v2, you could define any frontmatter variable you wanted and it would be available at the top level of your content queries. In Nuxt Content v3, only a few frontmatter variables are available at the top level, i.e. 'title', 'description', and 'navigation'.  For example, the ",[56,769,770],{},"description"," and ",[56,773,774],{},"navigation"," variables are available at the top level, but other variables like ",[56,777,778],{},"date_created",[56,780,781],{},"date_modified"," are not available at the top level and are instead nested within a ",[56,784,785],{},"meta"," key, which is not queryable.",[16,788,789,790,793,794,796,797,799,800,802,803,806,807,809,810,812,813,134],{},"To resolve this issue, you have to explicitly include your custom frontmatter variables in the ",[56,791,792],{},"schema"," of your collection definition in your ",[56,795,130],{}," file.  For example, to include a ",[56,798,778],{}," date variable you have to add it to the ",[56,801,792],{}," key in the ",[56,804,805],{},"defineCollection"," in your ",[56,808,130],{}," file.  Then you can easily query against ",[56,811,778],{}," variable, and so forth.  I've covered this issue in more detail ",[20,814,43],{"href":815,"rel":816},"https:\u002F\u002Fpennockprojects.com\u002Fblog\u002F2025\u002Fnuxtcontentupgrade#collections",[24],[11,818,820],{"id":819},"_3-frontmatter-snake_case","3. Frontmatter snake_case",[16,822,823,824,827,828,830,831,833,834,134],{},"The third issue I encountered was that all queryable frontmatter variables had to be defined in snake_case (breaking change from v2) as they store the variables in a SQLite database for queryCollection (snake_case is a limitation of SQLite).  So any variables I had previously defined in camelCase or PascalCase had to be changed to snake_case in all of my markdown files and in my code when querying the variables.  For example, ",[56,825,826],{},"dateCreated"," had to be changed to ",[56,829,778],{}," in all of my markdown files and in all of my code where I queried the ",[56,832,826],{}," variable.  This was a bit of a pain, but it was necessary to make the variables queryable with ",[56,835,64],{},[11,837,839],{"id":838},"_4-zod-schemas","4. Zod Schemas",[16,841,842,843,846,847,134],{},"The fourth issue\u002Fopportunity was the inclusion of Zod Schemas in the ",[56,844,845],{},"defineContentConfig()"," and with the renaming of variables to snake_case, it afforded an opportunity for a new system of data defining, typing and shaping, I discuss this in more detail in my blog post about data schema for Nuxt Content v3 ",[20,848,43],{"href":849,"rel":850},"https:\u002F\u002Fpennockprojects.com\u002Fblog\u002F2026\u002FNuxtContentSchema",[24],[11,852,854,855,857,858,860],{"id":853},"_5-querycollection-vs-querycontent-paths","5. ",[56,856,64],{}," vs ",[56,859,80],{}," paths",[16,862,863,864,866,867,869,870,872,873,876,877,880,881,884,885,888,889,891],{},"The fifth issue I encountered was that ",[56,865,64],{}," is that you can't query against paths like with ",[56,868,80],{},".  My previous queries would use a file path to select all content files in subdirectories. With ",[56,871,64],{}," you query collections (which due to problems with nesting collections) meant I had to convert to using a ",[56,874,875],{},".where()"," clause to select subdirectories from the ",[56,878,879],{},"path"," key for each collection item returned.  Note, you can't use ",[56,882,883],{},".path()"," clause for this purpose is that doesn't include subdirectories.  For example, to select all content files in the ",[56,886,887],{},"blog"," subdirectory with ",[56,890,80],{}," I could do:",[142,893,895],{"className":144,"code":894,"language":146,"meta":147,"style":147},"const blogPosts = await queryContent('blog').find()\n",[56,896,897],{"__ignoreMap":147},[151,898,899,903,907,910,913,916,919,922,925,928,931],{"class":153,"line":154},[151,900,902],{"class":901},"s-Tb5","const",[151,904,906],{"class":905},"sraLd"," blogPosts",[151,908,909],{"class":157}," =",[151,911,912],{"class":157}," await",[151,914,915],{"class":164}," queryContent",[151,917,918],{"class":168},"(",[151,920,921],{"class":229},"'blog'",[151,923,924],{"class":168},")",[151,926,134],{"class":927},"s_OQ2",[151,929,930],{"class":164},"find",[151,932,933],{"class":168},"()\n",[16,935,936,937,939],{},"With ",[56,938,64],{}," I had to do:",[142,941,943],{"className":144,"code":942,"language":146,"meta":147,"style":147},"const blogPosts = await queryCollection('content').where('path', 'LIKE', '\u002Fblog%').all()\n",[56,944,945],{"__ignoreMap":147},[151,946,947,949,951,953,955,958,960,963,965,967,970,972,975,977,980,982,985,987,989,992],{"class":153,"line":154},[151,948,902],{"class":901},[151,950,906],{"class":905},[151,952,909],{"class":157},[151,954,912],{"class":157},[151,956,957],{"class":164}," queryCollection",[151,959,918],{"class":168},[151,961,962],{"class":229},"'content'",[151,964,924],{"class":168},[151,966,134],{"class":927},[151,968,969],{"class":164},"where",[151,971,918],{"class":168},[151,973,974],{"class":229},"'path'",[151,976,315],{"class":214},[151,978,979],{"class":229}," 'LIKE'",[151,981,315],{"class":214},[151,983,984],{"class":229}," '\u002Fblog%'",[151,986,924],{"class":168},[151,988,134],{"class":927},[151,990,991],{"class":164},"all",[151,993,933],{"class":168},[16,995,716,996,999,1000,1003,1004,1007,1008,1013,1014,1016,1017,1019],{},[56,997,998],{},"where('path', 'LIKE', '\u002Fblog%')"," clause is a bit more cumbersome and tricky. The end ",[56,1001,1002],{},"%"," is a wildcard to select all content where the path starts with ",[56,1005,1006],{},"\u002Fblog",". The three parameters and what operators are available is ",[20,1009,1012],{"href":1010,"rel":1011},"https:\u002F\u002Fcontent.nuxt.com\u002Fdocs\u002Futils\u002Fquery-collection#wherefield-keyof-collection-string-operator-sqloperator-value-unknown",[24],"not completely documented",". It's not helpful when it says 'Possible' values include.  How about a complete list?  I had to research SQL Lite operators available and included with NuxtContent.  With trial and error to figure out how to select content files in subdirectories with ",[56,1015,64],{},".  This is a bit of a pain and a breaking change from ",[56,1018,80],{},", but it is also a bit more powerful and flexible.",[16,1021,1022,1023,1026,1027,1029,1030,1032],{},"One more note is a breaking change is that the previous ",[56,1024,1025],{},"_path"," key is now just ",[56,1028,879],{}," in the collection items returned from ",[56,1031,64],{},".  This is a bit of a minor change, but it is worth noting.",[16,1034,1035,1036,1039,1040,1042,1043,1045,1046,1049,1050,1052,1053],{},"One final note, is that the ",[56,1037,1038],{},".select()"," method for selecting specific keys from the collection items returned from ",[56,1041,64],{}," does not work with the ",[56,1044,879],{}," key, which is a bit of a pain as it means you have to select all keys and then use the ",[56,1047,1048],{},"where()"," clause to filter by path, you can't just select the keys you want and filter by path.  This is a bit of a limitation of ",[56,1051,64],{}," and the collections system in general, as it is more rigid and structured than ",[56,1054,80],{},[1056,1057,1059,1060,1063],"h3",{"id":1058},"_6-useasyncdata-unique-query-names","6. ",[56,1061,1062],{},"useAsyncData"," Unique Query Names",[16,1065,1066,1067,1069,1070,1072,1073,1076,1077,1079,1080,1083,1084,1087,1088,1090,1091,1094],{},"The sixth issue I encountered was that when using ",[56,1068,64],{}," in my components to fetch content data, I had to wrap the ",[56,1071,64],{}," call in a ",[56,1074,1075],{},"useAsyncData()"," call to avoid hydration errors. Further, the first parameter of ",[56,1078,1075],{}," is a unique key for the query, which helps Nuxt manage the data fetching and caching.  If you query data in a content component like ",[56,1081,1082],{},"BlogList.vue",", you can have that name be the same, especially if you are query for different data sets.  If, for example, you use the ",[56,1085,1086],{},"\u003CBlogList>"," component on different pages, or the component twice on the same page but with different parameters, and you use the same query name in ",[56,1089,1075],{},", you can run into issues with data being cached and shared across different instances of the component, which can lead to unexpected results. To avoid this issue, you can use a unique query name for each instance of the component, which might be based off a parameter rather than just the ",[56,1092,1093],{},"page.route"," or you can use a dynamic query name that includes some unique identifier, such as the page slug or a timestamp.",[16,1096,1097],{},"For example, instead of using a static query name like this:",[142,1099,1101],{"className":144,"code":1100,"language":146,"meta":147,"style":147},"const { data: blogPosts } = useAsyncData('blogPosts', () => queryCollection('content').where('path', 'LIKE', '\u002Fblog%').all())\n",[56,1102,1103],{"__ignoreMap":147},[151,1104,1105,1107,1110,1113,1116,1119,1122,1124,1127,1129,1132,1135,1137,1139,1141,1143,1145,1147,1149,1151,1153,1155,1157,1159,1161,1163,1165],{"class":153,"line":154},[151,1106,902],{"class":901},[151,1108,1109],{"class":168}," { data: ",[151,1111,1112],{"class":905},"blogPosts",[151,1114,1115],{"class":168}," } ",[151,1117,1118],{"class":157},"=",[151,1120,1121],{"class":164}," useAsyncData",[151,1123,918],{"class":168},[151,1125,1126],{"class":229},"'blogPosts'",[151,1128,315],{"class":214},[151,1130,1131],{"class":168}," () ",[151,1133,1134],{"class":901},"=>",[151,1136,957],{"class":164},[151,1138,918],{"class":168},[151,1140,962],{"class":229},[151,1142,924],{"class":168},[151,1144,134],{"class":927},[151,1146,969],{"class":164},[151,1148,918],{"class":168},[151,1150,974],{"class":229},[151,1152,315],{"class":214},[151,1154,979],{"class":229},[151,1156,315],{"class":214},[151,1158,984],{"class":229},[151,1160,924],{"class":168},[151,1162,134],{"class":927},[151,1164,991],{"class":164},[151,1166,1167],{"class":168},"())\n",[16,1169,1170],{},"You could use a dynamic query name like this:",[142,1172,1174],{"className":144,"code":1173,"language":146,"meta":147,"style":147},"const { data: blogPosts } = useAsyncData(`blogPosts-${prop.folder}`, () => queryCollection('content').where('path', 'LIKE', `\u002F${prop.folder}%`).all())\n",[56,1175,1176],{"__ignoreMap":147},[151,1177,1178,1180,1182,1184,1186,1188,1190,1192,1195,1198,1201,1204,1207,1210,1212,1214,1216,1218,1220,1222,1224,1226,1228,1230,1232,1234,1236,1238,1241,1243,1245,1247,1249,1252,1254,1256,1258],{"class":153,"line":154},[151,1179,902],{"class":901},[151,1181,1109],{"class":168},[151,1183,1112],{"class":905},[151,1185,1115],{"class":168},[151,1187,1118],{"class":157},[151,1189,1121],{"class":164},[151,1191,918],{"class":168},[151,1193,1194],{"class":229},"`blogPosts-",[151,1196,1197],{"class":157},"${",[151,1199,1200],{"class":905},"prop",[151,1202,1203],{"class":168},".folder",[151,1205,1206],{"class":157},"}",[151,1208,1209],{"class":229},"`",[151,1211,315],{"class":214},[151,1213,1131],{"class":168},[151,1215,1134],{"class":901},[151,1217,957],{"class":164},[151,1219,918],{"class":168},[151,1221,962],{"class":229},[151,1223,924],{"class":168},[151,1225,134],{"class":927},[151,1227,969],{"class":164},[151,1229,918],{"class":168},[151,1231,974],{"class":229},[151,1233,315],{"class":214},[151,1235,979],{"class":229},[151,1237,315],{"class":214},[151,1239,1240],{"class":229}," `\u002F",[151,1242,1197],{"class":157},[151,1244,1200],{"class":905},[151,1246,1203],{"class":168},[151,1248,1206],{"class":157},[151,1250,1251],{"class":229},"%`",[151,1253,924],{"class":168},[151,1255,134],{"class":927},[151,1257,991],{"class":164},[151,1259,1167],{"class":168},[16,1261,1262,1263,1266],{},"This way, each instance of the component will have a unique query name based on the ",[56,1264,1265],{},"prop.folder"," value, which allows Nuxt to manage the data fetching and caching correctly for each instance of the component.  This is especially important if you are using the same component on different pages or multiple times on the same page with different parameters, as it ensures that each instance of the component fetches and displays the correct data without interference from other instances.",[11,1268,1270],{"id":1269},"conclusion","Conclusion",[16,1272,1273,1274,1276,1277,1279],{},"Upgrading to Nuxt v4 and Nuxt Content v3 was a significant undertaking, but it was necessary to take advantage of the new features and improvements. The upgrade process involved several challenges, including changes to the configuration, querying content with ",[56,1275,64],{},", frontmatter variable handling, and the need to use ",[56,1278,1075],{}," for content queries. However, with careful attention to the documentation and some trial and error, I was able to successfully upgrade my projects and take advantage of the new capabilities offered by Nuxt v4 and Nuxt Content v3.",[1281,1282,1283],"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 .srTi1, html code.shiki .srTi1{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#A6E22E}html pre.shiki code .sxUMQ, html code.shiki .sxUMQ{--shiki-default:#24292EFF;--shiki-dark:#B392F0;--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 .skixG, html code.shiki .skixG{--shiki-default:#D32F2F;--shiki-dark:#F97583;--shiki-sepia:#F8F8F2}html pre.shiki code .snb_r, html code.shiki .snb_r{--shiki-default:#1976D2;--shiki-dark:#79B8FF;--shiki-sepia:#AE81FF}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 .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 .s0Ixy, html code.shiki .s0Ixy{--shiki-default:#1976D2;--shiki-dark:#F8F8F8;--shiki-sepia:#AE81FF}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 .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}",{"title":147,"searchDepth":172,"depth":172,"links":1285},[1286,1287,1288,1289,1290,1291,1296],{"id":13,"depth":172,"text":14},{"id":116,"depth":172,"text":117},{"id":760,"depth":172,"text":761},{"id":819,"depth":172,"text":820},{"id":838,"depth":172,"text":839},{"id":853,"depth":172,"text":1292,"children":1293},"5. queryCollection() vs queryContent() paths",[1294],{"id":1058,"depth":179,"text":1295},"6. useAsyncData Unique Query Names",{"id":1269,"depth":172,"text":1270},"article","2026-05-15",null,"A detailed account of the issues, process and considerations involved in upgrading the Nuxt Content CMS module from version 2 to version 3, while also upgrading the Nuxt framework from version 3 to version 4.","md","\u002Fimages\u002Fblog\u002FNuxtContentUpgrade.jpg","Two documents, one an old school quill, ink, and parchment titled 'Nuxt Content v2' and the other a modern laser printed document titled 'Nuxt Content v3' side by side.",false,[1306,1307,1308,1309,1310,1311,792,1312,1313],"nuxt","nuxt content","upgrade","migration","markdown","collections","zod","frontmatter",{},"\u002Fblog\u002F2026\u002Fnuxtcontentv2to3",0,[1318],"JAMStart","published",{"title":5,"description":1300},{"loc":1315},"blog\u002F2026\u002FNuxtContentV2to3","SVw6KwA3-Zj3334ttG6FdUKyetmobhZsedXLAWlzjgo",1780531288861]