[{"data":1,"prerenderedAt":2109},["ShallowReactive",2],{"en-post-list":3},[4,523,959],{"id":5,"title":6,"body":7,"description":513,"extension":514,"meta":515,"navigation":517,"path":518,"seo":519,"sitemap":520,"stem":521,"__hash__":522},"blogEn\u002Fen\u002Fnuxt-content-vercel-empty-query-sqlite.md","Nuxt Content v3 queryCollection() Returns Empty on Vercel (But Works Locally)",{"type":8,"value":9,"toc":504},"minimark",[10,14,23,26,29,34,185,212,223,225,229,237,257,265,267,279,301,329,335,337,341,344,381,387,447,449,453,456,488,490,494,500],[11,12,6],"h1",{"id":13},"nuxt-content-v3-querycollection-returns-empty-on-vercel-but-works-locally",[15,16,17,18,22],"p",{},"A Nuxt 3 + Nuxt Content v3 blog suddenly started rendering a completely empty post list in production. No error, no crash — the page just rendered with zero posts. Locally, running ",[19,20,21],"code",{},"npm run build"," and serving that exact same build worked perfectly: every post showed up.",[15,24,25],{},"That gap — identical code, different result depending on where it runs — is the signature of an environment-specific bug, not a logic bug. Here's how it was tracked down.",[27,28],"hr",{},[30,31,33],"h2",{"id":32},"the-symptom","The symptom",[35,36,41],"pre",{"className":37,"code":38,"language":39,"meta":40,"style":40},"language-vue shiki shiki-themes github-light github-dark","\u003Cscript setup>\nconst { data: posts } = await useAsyncData(\"blog-post-list\", async () => {\n  const result = await queryCollection(\"blog\").all();\n  return result;\n}, { default: () => [] });\n\u003C\u002Fscript>\n","vue","",[19,42,43,63,118,149,158,175],{"__ignoreMap":40},[44,45,48,52,56,60],"span",{"class":46,"line":47},"line",1,[44,49,51],{"class":50},"sVt8B","\u003C",[44,53,55],{"class":54},"s9eBZ","script",[44,57,59],{"class":58},"sScJk"," setup",[44,61,62],{"class":50},">\n",[44,64,66,70,73,77,80,84,87,90,93,96,99,103,106,109,112,115],{"class":46,"line":65},2,[44,67,69],{"class":68},"szBVR","const",[44,71,72],{"class":50}," { ",[44,74,76],{"class":75},"s4XuR","data",[44,78,79],{"class":50},": ",[44,81,83],{"class":82},"sj4cs","posts",[44,85,86],{"class":50}," } ",[44,88,89],{"class":68},"=",[44,91,92],{"class":68}," await",[44,94,95],{"class":58}," useAsyncData",[44,97,98],{"class":50},"(",[44,100,102],{"class":101},"sZZnC","\"blog-post-list\"",[44,104,105],{"class":50},", ",[44,107,108],{"class":68},"async",[44,110,111],{"class":50}," () ",[44,113,114],{"class":68},"=>",[44,116,117],{"class":50}," {\n",[44,119,121,124,127,130,132,135,137,140,143,146],{"class":46,"line":120},3,[44,122,123],{"class":68},"  const",[44,125,126],{"class":82}," result",[44,128,129],{"class":68}," =",[44,131,92],{"class":68},[44,133,134],{"class":58}," queryCollection",[44,136,98],{"class":50},[44,138,139],{"class":101},"\"blog\"",[44,141,142],{"class":50},").",[44,144,145],{"class":58},"all",[44,147,148],{"class":50},"();\n",[44,150,152,155],{"class":46,"line":151},4,[44,153,154],{"class":68},"  return",[44,156,157],{"class":50}," result;\n",[44,159,161,164,167,170,172],{"class":46,"line":160},5,[44,162,163],{"class":50},"}, { ",[44,165,166],{"class":58},"default",[44,168,169],{"class":50},": () ",[44,171,114],{"class":68},[44,173,174],{"class":50}," [] });\n",[44,176,178,181,183],{"class":46,"line":177},6,[44,179,180],{"class":50},"\u003C\u002F",[44,182,55],{"class":54},[44,184,62],{"class":50},[186,187,188,202],"ul",{},[189,190,191,192,194,195,198,199,201],"li",{},"Locally (",[19,193,21],{}," + ",[19,196,197],{},"node .output\u002Fserver\u002Findex.mjs","): ",[19,200,83],{}," comes back with every post. Works.",[189,203,204,205,207,208,211],{},"On Vercel, hitting the live URL: ",[19,206,83],{}," comes back as ",[19,209,210],{},"[]",". Every time. No error in the response, no warning in the logs — just an empty array.",[15,213,214,215,218,219,222],{},"Confirmed it wasn't a caching artifact first: the response headers on Vercel showed ",[19,216,217],{},"x-vercel-cache: MISS"," and ",[19,220,221],{},"age: 0"," — a genuinely fresh server execution, still returning nothing.",[27,224],{},[30,226,228],{"id":227},"ruling-things-out","Ruling things out",[15,230,231,232,236],{},"Since the exact same build artifact behaved differently by environment, the cause had to be something about ",[233,234,235],"em",{},"where"," it ran, not the page code:",[186,238,239,246,249],{},[189,240,241,242,245],{},"Not a missing ",[19,243,244],{},"content.config.ts"," — it was present and correctly configured.",[189,247,248],{},"Not a code bug in the page — the identical build rendered all posts correctly when run locally.",[189,250,251,252,105,255,142],{},"Not a stale CDN cache — confirmed via response headers (",[19,253,254],{},"MISS",[19,256,221],{},[15,258,259,260,264],{},"That narrows it down to the one thing that genuinely differs between \"run locally\" and \"run inside a Vercel serverless function\": ",[261,262,263],"strong",{},"filesystem access",".",[27,266],{},[30,268,270,271,274,275,278],{"id":269},"root-cause-nuxtcontent-v3s-sqlite-database-needs-tmp-on-vercel-and-300-didnt-know-that-yet","Root cause: ",[19,272,273],{},"@nuxt\u002Fcontent"," v3's SQLite database needs ",[19,276,277],{},"\u002Ftmp"," on Vercel — and 3.0.0 didn't know that yet",[15,280,281,283,284,287,288,293,294,296,297,300],{},[19,282,273],{}," v3 indexes all your markdown into a SQLite database at build time, read via ",[19,285,286],{},"better-sqlite3"," (a native module) at runtime. On Vercel, the deployed function's filesystem is ",[261,289,290,291],{},"read-only everywhere except ",[19,292,277],{},". If the content module tries to read or write its SQLite file somewhere outside ",[19,295,277],{},", that access silently fails in a way that doesn't throw — ",[19,298,299],{},"queryCollection()"," just comes back with nothing, instead of erroring.",[15,302,303,304,306,307,312,313,320,321,324,325,328],{},"The fix for this exact scenario — making the module use ",[19,305,277],{}," on Vercel — shipped in ",[261,308,309,311],{},[19,310,273],{}," v3.1.1"," (",[314,315,319],"a",{"href":316,"rel":317},"https:\u002F\u002Fgithub.com\u002Fnuxt\u002Fcontent\u002Fissues\u002F3108",[318],"nofollow","nuxt\u002Fcontent#3108","). The project in question was pinned to ",[19,322,323],{},"@nuxt\u002Fcontent: \"^3.0.0\""," — the very first v3 release, published ",[233,326,327],{},"before"," that fix existed.",[15,330,331,332,334],{},"That's the whole bug: a brand-new Nitro\u002FNuxt project scaffold had pinned the first-ever v3 release of the content module, and that specific version never got the Vercel filesystem fix. Locally, there's no ",[19,333,277],{},"-only restriction, so the broken path silently worked anyway — which is exactly why this kind of bug survives so long undetected: it only manifests in the one environment nobody manually tests against on every change.",[27,336],{},[30,338,340],{"id":339},"the-fix","The fix",[15,342,343],{},"Upgrade past the fix, plus whatever else comes along for the ride:",[35,345,349],{"className":346,"code":347,"language":348,"meta":40,"style":40},"language-diff shiki shiki-themes github-light github-dark","- \"@nuxt\u002Fcontent\": \"^3.0.0\",\n+ \"@nuxt\u002Fcontent\": \"^3.16.1\",\n- \"nuxt\": \"^3.15.2\",\n+ \"nuxt\": \"^3.21.11\",\n- \"@nuxtjs\u002Fsitemap\": \"^7.2.9\",\n+ \"@nuxtjs\u002Fsitemap\": \"^8.6.1\",\n","diff",[19,350,351,356,361,366,371,376],{"__ignoreMap":40},[44,352,353],{"class":46,"line":47},[44,354,355],{},"- \"@nuxt\u002Fcontent\": \"^3.0.0\",\n",[44,357,358],{"class":46,"line":65},[44,359,360],{},"+ \"@nuxt\u002Fcontent\": \"^3.16.1\",\n",[44,362,363],{"class":46,"line":120},[44,364,365],{},"- \"nuxt\": \"^3.15.2\",\n",[44,367,368],{"class":46,"line":151},[44,369,370],{},"+ \"nuxt\": \"^3.21.11\",\n",[44,372,373],{"class":46,"line":160},[44,374,375],{},"- \"@nuxtjs\u002Fsitemap\": \"^7.2.9\",\n",[44,377,378],{"class":46,"line":177},[44,379,380],{},"+ \"@nuxtjs\u002Fsitemap\": \"^8.6.1\",\n",[15,382,383,384,386],{},"A few things fall out of bumping ",[19,385,273],{}," that far forward, worth knowing before you hit them:",[186,388,389,403,423],{},[189,390,391,394,395,398,399,402],{},[19,392,393],{},"@nuxt\u002Fcontent@3.16.1"," requires ",[19,396,397],{},"nuxt >=3.19.0"," (or ",[19,400,401],{},"^4.1.0",") — the module disables itself with a warning if your Nuxt version is older, rather than failing loudly.",[189,404,405,406,408,409,411,412,415,416,419,420,264],{},"Newer ",[19,407,273],{}," no longer bundles ",[19,410,286],{}," — it now expects it as an explicit dependency. ",[19,413,414],{},"nuxt prepare"," fails with ",[19,417,418],{},"Nuxt Content requires better-sqlite3 module to operate"," until you ",[19,421,422],{},"npm install better-sqlite3",[189,424,425,428,429,432,433,436,437,439,440,443,444,446],{},[19,426,427],{},"@nuxtjs\u002Fsitemap","'s older content integration (",[19,430,431],{},"asSitemapCollection",") imports ",[19,434,435],{},"queryCollectionWithEvent"," from ",[19,438,273],{},"'s runtime — a function that newer content versions renamed\u002Fremoved. The build fails at the Rollup stage with ",[19,441,442],{},"\"queryCollectionWithEvent\" is not exported"," until ",[19,445,427],{}," is bumped to a version built against the newer content API.",[27,448],{},[30,450,452],{"id":451},"verifying-the-fix","Verifying the fix",[15,454,455],{},"Since this bug only showed up in the deployed environment, \"it builds locally\" isn't enough evidence. After upgrading:",[457,458,459,467,481],"ol",{},[189,460,461,463,464,466],{},[19,462,21],{},", then run the actual build output locally (",[19,465,197],{},") and confirm the post count matches your content directory.",[189,468,469,470,473,474,476,477,480],{},"Deploy, then check the ",[261,471,472],{},"live"," response headers for ",[19,475,217],{}," \u002F fresh ",[19,478,479],{},"age"," to make sure you're not looking at a cached (and still-empty) response.",[189,482,483,484,487],{},"Check ",[19,485,486],{},"\u002Fsitemap.xml"," on the live deployment — it should list every post, not just static routes.",[27,489],{},[30,491,493],{"id":492},"takeaway","Takeaway",[15,495,496,497,499],{},"If ",[19,498,299],{}," (or any SQLite-file-backed Nuxt module) works locally but comes back empty only on Vercel with zero errors, check your filesystem assumptions before your logic — and check whether you're pinned to a version of the module that predates its own Vercel-specific fix. \"Works on my machine\" is a filesystem-permissions story as often as it's a cache one.",[501,502,503],"style",{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":40,"searchDepth":65,"depth":65,"links":505},[506,507,508,510,511,512],{"id":32,"depth":65,"text":33},{"id":227,"depth":65,"text":228},{"id":269,"depth":65,"text":509},"Root cause: @nuxt\u002Fcontent v3's SQLite database needs \u002Ftmp on Vercel — and 3.0.0 didn't know that yet",{"id":339,"depth":65,"text":340},{"id":451,"depth":65,"text":452},{"id":492,"depth":65,"text":493},"A production-only bug where queryCollection(\"blog\").all() silently returns an empty array on Vercel while the exact same build works fine locally — caused by @nuxt\u002Fcontent 3.0.0 predating the Vercel \u002Ftmp SQLite fix.","md",{"date":516},"2026.10.07",true,"\u002Fen\u002Fnuxt-content-vercel-empty-query-sqlite",{"title":6,"description":513},{"loc":518},"en\u002Fnuxt-content-vercel-empty-query-sqlite","jl-tvvA75zhHc-eCBsZ6t-ML2zB-Zct6uwi6xIsxc34",{"id":524,"title":525,"body":526,"description":952,"extension":514,"meta":953,"navigation":517,"path":954,"seo":955,"sitemap":956,"stem":957,"__hash__":958},"blogEn\u002Fen\u002Fnuxt-useasyncdata-cache-error.md","Nuxt 3 useAsyncData Caching Bug: Why Every Post Shows the Same Content",{"type":8,"value":527,"toc":935},[528,531,534,548,551,553,560,566,571,634,638,652,656,678,681,685,695,697,701,708,789,793,821,825,828,830,834,837,843,851,859,865,871,874,893,895,897,912,916,932],[11,529,525],{"id":530},"nuxt-3-useasyncdata-caching-bug-why-every-post-shows-the-same-content",[15,532,533],{},"While building a blog detail page in Nuxt 3, I hit a confusing bug:",[457,535,536,539,545],{},[189,537,538],{},"Open blog post #1.",[189,540,541,542,142],{},"Go back (via ",[19,543,544],{},"NuxtLink",[189,546,547],{},"Click into blog post #2 — and post #1's content is still shown.",[15,549,550],{},"The URL changes correctly. The content doesn't. This turned out to be a caching problem, not a routing one.",[27,552],{},[30,554,270,556,559],{"id":555},"root-cause-useasyncdatas-key-based-cache",[19,557,558],{},"useAsyncData","'s key-based cache",[15,561,562,563,565],{},"Nuxt 3's ",[19,564,558],{}," caches results internally to avoid redundant network requests — and that's exactly what bit me here.",[567,568,570],"h3",{"id":569},"the-original-code","The original code",[35,572,576],{"className":573,"code":574,"language":575,"meta":40,"style":40},"language-javascript shiki shiki-themes github-light github-dark","const { data: post } = await useAsyncData(() => {\n  return queryCollection(\"blog\").path(route.path).first();\n});\n","javascript",[19,577,578,606,629],{"__ignoreMap":40},[44,579,580,582,584,586,588,591,593,595,597,599,602,604],{"class":46,"line":47},[44,581,69],{"class":68},[44,583,72],{"class":50},[44,585,76],{"class":75},[44,587,79],{"class":50},[44,589,590],{"class":82},"post",[44,592,86],{"class":50},[44,594,89],{"class":68},[44,596,92],{"class":68},[44,598,95],{"class":58},[44,600,601],{"class":50},"(() ",[44,603,114],{"class":68},[44,605,117],{"class":50},[44,607,608,610,612,614,616,618,621,624,627],{"class":46,"line":65},[44,609,154],{"class":68},[44,611,134],{"class":58},[44,613,98],{"class":50},[44,615,139],{"class":101},[44,617,142],{"class":50},[44,619,620],{"class":58},"path",[44,622,623],{"class":50},"(route.path).",[44,625,626],{"class":58},"first",[44,628,148],{"class":50},[44,630,631],{"class":46,"line":120},[44,632,633],{"class":50},"});\n",[567,635,637],{"id":636},"the-key-is-fixed","The key is fixed",[15,639,640,641,643,644,647,648,651],{},"When you don't pass an explicit key, ",[19,642,558],{}," derives one ",[261,645,646],{},"from the callback's source code itself"," — not from anything dynamic like the route. So every call site using this exact code shares the ",[233,649,650],{},"same"," cache key, and Nuxt reuses whatever was cached under it.",[567,653,655],{"id":654},"what-actually-happens","What actually happens",[457,657,658,669],{},[189,659,660,661,664,665,668],{},"Visiting ",[19,662,663],{},"\u002Fblog\u002F1"," resolves ",[19,666,667],{},"queryCollection(\"blog\").path(\"\u002Fblog\u002F1\")"," and caches the result under that one fixed key.",[189,670,660,671,674,675,677],{},[19,672,673],{},"\u002Fblog\u002F2"," uses the ",[233,676,650],{}," key — so Nuxt just returns the cached result from step 1 instead of fetching again.",[15,679,680],{},"The URL updates. The rendered post does not.",[567,682,684],{"id":683},"summary-of-the-mechanism","Summary of the mechanism",[186,686,687,692],{},[189,688,689,691],{},[19,690,558],{}," caches by key.",[189,693,694],{},"Same key → no new fetch, cached data returned as-is.",[27,696],{},[30,698,700],{"id":699},"the-fix-make-the-key-dynamic","The fix: make the key dynamic",[15,702,703,704,707],{},"The fix is to derive the key from ",[19,705,706],{},"route.path",", so each route gets its own cache entry.",[35,709,711],{"className":573,"code":710,"language":575,"meta":40,"style":40},"const { data: post, error, refresh } = await useAsyncData(`blog-post-${route.path}`, () => {\n  return queryCollection(\"blog\").path(route.path).first();\n});\n",[19,712,713,765,785],{"__ignoreMap":40},[44,714,715,717,719,721,723,725,727,730,732,735,737,739,741,743,745,748,751,753,755,758,761,763],{"class":46,"line":47},[44,716,69],{"class":68},[44,718,72],{"class":50},[44,720,76],{"class":75},[44,722,79],{"class":50},[44,724,590],{"class":82},[44,726,105],{"class":50},[44,728,729],{"class":82},"error",[44,731,105],{"class":50},[44,733,734],{"class":82},"refresh",[44,736,86],{"class":50},[44,738,89],{"class":68},[44,740,92],{"class":68},[44,742,95],{"class":58},[44,744,98],{"class":50},[44,746,747],{"class":101},"`blog-post-${",[44,749,750],{"class":50},"route",[44,752,264],{"class":101},[44,754,620],{"class":50},[44,756,757],{"class":101},"}`",[44,759,760],{"class":50},", () ",[44,762,114],{"class":68},[44,764,117],{"class":50},[44,766,767,769,771,773,775,777,779,781,783],{"class":46,"line":65},[44,768,154],{"class":68},[44,770,134],{"class":58},[44,772,98],{"class":50},[44,774,139],{"class":101},[44,776,142],{"class":50},[44,778,620],{"class":58},[44,780,623],{"class":50},[44,782,626],{"class":58},[44,784,148],{"class":50},[44,786,787],{"class":46,"line":120},[44,788,633],{"class":50},[567,790,792],{"id":791},"what-changed","What changed",[186,794,795,801],{},[189,796,797,798,264],{},"The key is now explicit and route-specific: ",[19,799,800],{},"blog-post-${route.path}",[189,802,803,804],{},"Every time the path changes, Nuxt sees a new key and fetches fresh data instead of reusing the cache.\n",[186,805,806,814],{},[189,807,808,810,811],{},[19,809,663],{}," → key: ",[19,812,813],{},"blog-post-\u002Fblog\u002F1",[189,815,816,810,818],{},[19,817,673],{},[19,819,820],{},"blog-post-\u002Fblog\u002F2",[567,822,824],{"id":823},"result","Result",[15,826,827],{},"Each route now gets its own cached entry, so navigating between posts always fetches (or correctly reuses) the right data — the stale-content bug disappears.",[27,829],{},[30,831,833],{"id":832},"why-this-matters-more-once-you-deploy","Why this matters more once you deploy",[15,835,836],{},"In production, Nuxt 3's SSR\u002Fprerendering pipeline adds another layer: payload JSON files generated per cache key. A fixed key means every route shares the same payload file; a dynamic key gives each route its own.",[15,838,839,842],{},[261,840,841],{},"Before the fix"," — one shared payload regardless of path:",[35,844,849],{"className":845,"code":847,"language":848},[846],"language-text","\u002F_nuxt\u002Fdata\u002Fblog.json   \u002F\u002F reused for every route\n","text",[19,850,847],{"__ignoreMap":40},[15,852,853,855,856,858],{},[19,854,673],{}," would serve ",[19,857,663],{},"'s cached payload.",[15,860,861,864],{},[261,862,863],{},"After the fix"," — one payload per path:",[35,866,869],{"className":867,"code":868,"language":848},[846],"\u002F_nuxt\u002Fdata\u002Fblog-post-\u002Fblog\u002F1.json\n\u002F_nuxt\u002Fdata\u002Fblog-post-\u002Fblog\u002F2.json\n",[19,870,868],{"__ignoreMap":40},[15,872,873],{},"To confirm this in your own deployment, check:",[457,875,876,883,890],{},[189,877,878,879,882],{},"The generated payload files under ",[19,880,881],{},"_nuxt\u002Fdata\u002F"," — one per path, not one shared file.",[189,884,885,886,889],{},"The prerendered HTML under ",[19,887,888],{},".output\u002Fpublic\u002F"," — each route should have its own file with the correct content baked in.",[189,891,892],{},"Your hosting provider's request logs — each path should be served its own correct payload\u002FHTML, not a stale copy.",[27,894],{},[30,896,493],{"id":492},[15,898,899,901,902,105,904,907,908,911],{},[19,900,558],{},"'s default (code-derived) key is a common trap on any page that's reused across multiple routes. Whenever the fetch itself depends on something route-specific (",[19,903,706],{},[19,905,906],{},"route.params",", a dynamic prop), ",[261,909,910],{},"always pass an explicit, route-aware key"," — it's a one-line fix, but it's the difference between correct data and silently stale content.",[567,913,915],{"id":914},"references","References",[186,917,918,925],{},[189,919,920],{},[314,921,924],{"href":922,"rel":923},"https:\u002F\u002Fnuxt.com\u002Fdocs\u002Fapi\u002Fcomposables\u002Fuse-async-data",[318],"Nuxt 3 Documentation: useAsyncData",[189,926,927],{},[314,928,931],{"href":929,"rel":930},"https:\u002F\u002Fnuxt.com\u002Fdocs\u002Fgetting-started\u002Fintroduction",[318],"Nuxt 3: Caching and Prerendering",[501,933,934],{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":40,"searchDepth":65,"depth":65,"links":936},[937,944,948,949],{"id":555,"depth":65,"text":938,"children":939},"Root cause: useAsyncData's key-based cache",[940,941,942,943],{"id":569,"depth":120,"text":570},{"id":636,"depth":120,"text":637},{"id":654,"depth":120,"text":655},{"id":683,"depth":120,"text":684},{"id":699,"depth":65,"text":700,"children":945},[946,947],{"id":791,"depth":120,"text":792},{"id":823,"depth":120,"text":824},{"id":832,"depth":65,"text":833},{"id":492,"depth":65,"text":493,"children":950},[951],{"id":914,"depth":120,"text":915},"Clicking a second blog post still renders the first one's data — a walkthrough of useAsyncData's default key-based caching and the one-line fix (a dynamic key).",{"date":516},"\u002Fen\u002Fnuxt-useasyncdata-cache-error",{"title":525,"description":952},{"loc":954},"en\u002Fnuxt-useasyncdata-cache-error","VTv8oGo7NafB4mxQAma-r1WGNN9I3D5qqRo1RT-u8to",{"id":960,"title":961,"body":962,"description":2102,"extension":514,"meta":2103,"navigation":517,"path":2104,"seo":2105,"sitemap":2106,"stem":2107,"__hash__":2108},"blogEn\u002Fen\u002Fvue-query-ref-value-trap-nuxt3.md","Why Your Vue Query Data Doesn't Show Up in Nuxt 3 (the ref unwrapping trap)",{"type":8,"value":963,"toc":2090},[964,967,981,992,994,998,1124,1131,1197,1204,1206,1213,1225,1291,1303,1305,1309,1316,1330,1381,1401,1403,1411,1493,1495,1502,1511,1665,1824,1826,1830,2052,2054,2056,2087],[11,965,961],{"id":966},"why-your-vue-query-data-doesnt-show-up-in-nuxt-3-the-ref-unwrapping-trap",[15,968,969,970,973,974],{},"While wiring up ",[19,971,972],{},"@tanstack\u002Fvue-query"," in a Nuxt 3 project to fetch API data, I ran into a frustrating bug: ",[261,975,976,977,980],{},"the data showed up perfectly in ",[19,978,979],{},"console.log",", but nothing rendered on the page.",[15,982,983,984,987,988,991],{},"This post covers why that happens and how Vue 3's ",[19,985,986],{},"ref"," unwrapping actually works inside templates — because the fix is one character, but understanding ",[233,989,990],{},"why"," it's needed is the useful part.",[27,993],{},[30,995,997],{"id":996},"the-symptom-data-exists-but-the-screen-shows-nothing","The symptom: data exists, but the screen shows nothing",[35,999,1001],{"className":37,"code":1000,"language":39,"meta":40,"style":40},"\u003Ctemplate>\n  \u003Cdiv v-if=\"postsQuery.data && postsQuery.data.length > 0\">\n    \u003Cul>\n      \u003Cli v-for=\"post in postsQuery.data\" :key=\"post.id\">\n        {{ post.title }}\n      \u003C\u002Fli>\n    \u003C\u002Ful>\n  \u003C\u002Fdiv>\n  \u003Cp v-else>No data.\u003C\u002Fp>\n\u003C\u002Ftemplate>\n",[19,1002,1003,1012,1030,1039,1064,1069,1078,1088,1098,1115],{"__ignoreMap":40},[44,1004,1005,1007,1010],{"class":46,"line":47},[44,1006,51],{"class":50},[44,1008,1009],{"class":54},"template",[44,1011,62],{"class":50},[44,1013,1014,1017,1020,1023,1025,1028],{"class":46,"line":65},[44,1015,1016],{"class":50},"  \u003C",[44,1018,1019],{"class":54},"div",[44,1021,1022],{"class":58}," v-if",[44,1024,89],{"class":50},[44,1026,1027],{"class":101},"\"postsQuery.data && postsQuery.data.length > 0\"",[44,1029,62],{"class":50},[44,1031,1032,1035,1037],{"class":46,"line":120},[44,1033,1034],{"class":50},"    \u003C",[44,1036,186],{"class":54},[44,1038,62],{"class":50},[44,1040,1041,1044,1046,1049,1051,1054,1057,1059,1062],{"class":46,"line":151},[44,1042,1043],{"class":50},"      \u003C",[44,1045,189],{"class":54},[44,1047,1048],{"class":58}," v-for",[44,1050,89],{"class":50},[44,1052,1053],{"class":101},"\"post in postsQuery.data\"",[44,1055,1056],{"class":58}," :key",[44,1058,89],{"class":50},[44,1060,1061],{"class":101},"\"post.id\"",[44,1063,62],{"class":50},[44,1065,1066],{"class":46,"line":160},[44,1067,1068],{"class":50},"        {{ post.title }}\n",[44,1070,1071,1074,1076],{"class":46,"line":177},[44,1072,1073],{"class":50},"      \u003C\u002F",[44,1075,189],{"class":54},[44,1077,62],{"class":50},[44,1079,1081,1084,1086],{"class":46,"line":1080},7,[44,1082,1083],{"class":50},"    \u003C\u002F",[44,1085,186],{"class":54},[44,1087,62],{"class":50},[44,1089,1091,1094,1096],{"class":46,"line":1090},8,[44,1092,1093],{"class":50},"  \u003C\u002F",[44,1095,1019],{"class":54},[44,1097,62],{"class":50},[44,1099,1101,1103,1105,1108,1111,1113],{"class":46,"line":1100},9,[44,1102,1016],{"class":50},[44,1104,15],{"class":54},[44,1106,1107],{"class":58}," v-else",[44,1109,1110],{"class":50},">No data.\u003C\u002F",[44,1112,15],{"class":54},[44,1114,62],{"class":50},[44,1116,1118,1120,1122],{"class":46,"line":1117},10,[44,1119,180],{"class":50},[44,1121,1009],{"class":54},[44,1123,62],{"class":50},[15,1125,1126,1127,1130],{},"This looks completely reasonable. And ",[19,1128,1129],{},"console.log(postsQuery.data)"," confirms the data is really there:",[35,1132,1136],{"className":1133,"code":1134,"language":1135,"meta":40,"style":40},"language-json shiki shiki-themes github-light github-dark","[\n  { \"id\": 1, \"title\": \"Hello Vue Query\" },\n  { \"id\": 2, \"title\": \"Nuxt 3 is Awesome\" }\n]\n","json",[19,1137,1138,1143,1169,1192],{"__ignoreMap":40},[44,1139,1140],{"class":46,"line":47},[44,1141,1142],{"class":50},"[\n",[44,1144,1145,1148,1151,1153,1156,1158,1161,1163,1166],{"class":46,"line":65},[44,1146,1147],{"class":50},"  { ",[44,1149,1150],{"class":82},"\"id\"",[44,1152,79],{"class":50},[44,1154,1155],{"class":82},"1",[44,1157,105],{"class":50},[44,1159,1160],{"class":82},"\"title\"",[44,1162,79],{"class":50},[44,1164,1165],{"class":101},"\"Hello Vue Query\"",[44,1167,1168],{"class":50}," },\n",[44,1170,1171,1173,1175,1177,1180,1182,1184,1186,1189],{"class":46,"line":120},[44,1172,1147],{"class":50},[44,1174,1150],{"class":82},[44,1176,79],{"class":50},[44,1178,1179],{"class":82},"2",[44,1181,105],{"class":50},[44,1183,1160],{"class":82},[44,1185,79],{"class":50},[44,1187,1188],{"class":101},"\"Nuxt 3 is Awesome\"",[44,1190,1191],{"class":50}," }\n",[44,1193,1194],{"class":46,"line":151},[44,1195,1196],{"class":50},"]\n",[15,1198,1199,1200,1203],{},"But the page only ever shows ",[261,1201,1202],{},"\"No data.\""," Why?",[27,1205],{},[30,1207,270,1209,1212],{"id":1208},"root-cause-usequery-returns-refs-not-plain-values",[19,1210,1211],{},"useQuery()"," returns refs, not plain values",[15,1214,1215,1218,1219,1221,1222,1224],{},[19,1216,1217],{},"vue-query","'s ",[19,1220,1211],{}," is built on Vue's reactivity system, and every property it returns is a ",[19,1223,986],{},":",[1226,1227,1228,1244],"table",{},[1229,1230,1231],"thead",{},[1232,1233,1234,1238,1241],"tr",{},[1235,1236,1237],"th",{},"Property",[1235,1239,1240],{},"Meaning",[1235,1242,1243],{},"Type",[1245,1246,1247,1262,1277],"tbody",{},[1232,1248,1249,1254,1257],{},[1250,1251,1252],"td",{},[19,1253,76],{},[1250,1255,1256],{},"Query result",[1250,1258,1259],{},[19,1260,1261],{},"Ref\u003CT>",[1232,1263,1264,1269,1272],{},[1250,1265,1266],{},[19,1267,1268],{},"isLoading",[1250,1270,1271],{},"Loading state",[1250,1273,1274],{},[19,1275,1276],{},"Ref\u003Cboolean>",[1232,1278,1279,1283,1286],{},[1250,1280,1281],{},[19,1282,729],{},[1250,1284,1285],{},"Error info",[1250,1287,1288],{},[19,1289,1290],{},"Ref\u003CError>",[15,1292,1293,1294,1297,1298,264],{},"So ",[19,1295,1296],{},"postsQuery.data"," is not an array — it's a ",[261,1299,1300],{},[19,1301,1302],{},"Ref\u003CArray>",[27,1304],{},[30,1306,1308],{"id":1307},"the-actual-bug-template-auto-unwrapping-doesnt-apply-inside-directives","The actual bug: template auto-unwrapping doesn't apply inside directives",[15,1310,1311,1312,1315],{},"Vue 3 templates automatically unwrap top-level refs, which is why ",[19,1313,1314],{},"{{ postsQuery.data }}"," \"just works\" in interpolation.",[15,1317,1318,1319,1322,1323,218,1326,1329],{},"But that auto-unwrapping does ",[261,1320,1321],{},"not"," apply inside directive expressions like ",[19,1324,1325],{},"v-if",[19,1327,1328],{},"v-for",". This means the following silently does nothing useful:",[35,1331,1333],{"className":37,"code":1332,"language":39,"meta":40,"style":40},"\u003C!-- Broken -->\n\u003Cdiv v-if=\"postsQuery.data && postsQuery.data.length > 0\"> ... \u003C\u002Fdiv>\n",[19,1334,1335,1341],{"__ignoreMap":40},[44,1336,1337],{"class":46,"line":47},[44,1338,1340],{"class":1339},"sJ8bj","\u003C!-- Broken -->\n",[44,1342,1343,1345,1347,1349,1351,1354,1357,1360,1363,1366,1369,1372,1374,1377,1379],{"class":46,"line":65},[44,1344,51],{"class":50},[44,1346,1019],{"class":54},[44,1348,1022],{"class":68},[44,1350,89],{"class":50},[44,1352,1353],{"class":101},"\"",[44,1355,1356],{"class":50},"postsQuery.data ",[44,1358,1359],{"class":68},"&&",[44,1361,1362],{"class":50}," postsQuery.data.",[44,1364,1365],{"class":82},"length",[44,1367,1368],{"class":68}," >",[44,1370,1371],{"class":82}," 0",[44,1373,1353],{"class":101},[44,1375,1376],{"class":50},"> ... \u003C\u002F",[44,1378,1019],{"class":54},[44,1380,62],{"class":50},[15,1382,1383,1385,1386,1388,1389,1392,1393,1396,1397,1400],{},[19,1384,1296],{}," is still a ",[19,1387,986],{}," object here, so ",[19,1390,1391],{},".length"," is ",[19,1394,1395],{},"undefined",", and the condition evaluates to ",[19,1398,1399],{},"false"," — every time, regardless of whether data actually arrived.",[27,1402],{},[30,1404,1406,1407,1410],{"id":1405},"the-fix-access-value-explicitly","The fix: access ",[19,1408,1409],{},".value"," explicitly",[35,1412,1414],{"className":37,"code":1413,"language":39,"meta":40,"style":40},"\u003C!-- Fixed -->\n\u003Cdiv v-if=\"postsQuery.data?.value?.length > 0\">\n  \u003Cul>\n    \u003Cli v-for=\"post in postsQuery.data.value\" :key=\"post.id\">\n      {{ post.title }}\n    \u003C\u002Fli>\n  \u003C\u002Ful>\n\u003C\u002Fdiv>\n\u003Cp v-else>No data.\u003C\u002Fp>\n",[19,1415,1416,1421,1446,1451,1456,1461,1466,1471,1479],{"__ignoreMap":40},[44,1417,1418],{"class":46,"line":47},[44,1419,1420],{"class":1339},"\u003C!-- Fixed -->\n",[44,1422,1423,1425,1427,1429,1431,1433,1436,1438,1440,1442,1444],{"class":46,"line":65},[44,1424,51],{"class":50},[44,1426,1019],{"class":54},[44,1428,1022],{"class":68},[44,1430,89],{"class":50},[44,1432,1353],{"class":101},[44,1434,1435],{"class":50},"postsQuery.data?.value?.",[44,1437,1365],{"class":82},[44,1439,1368],{"class":68},[44,1441,1371],{"class":82},[44,1443,1353],{"class":101},[44,1445,62],{"class":50},[44,1447,1448],{"class":46,"line":120},[44,1449,1450],{"class":50},"  \u003Cul>\n",[44,1452,1453],{"class":46,"line":151},[44,1454,1455],{"class":50},"    \u003Cli v-for=\"post in postsQuery.data.value\" :key=\"post.id\">\n",[44,1457,1458],{"class":46,"line":160},[44,1459,1460],{"class":50},"      {{ post.title }}\n",[44,1462,1463],{"class":46,"line":177},[44,1464,1465],{"class":50},"    \u003C\u002Fli>\n",[44,1467,1468],{"class":46,"line":1080},[44,1469,1470],{"class":50},"  \u003C\u002Ful>\n",[44,1472,1473,1475,1477],{"class":46,"line":1090},[44,1474,180],{"class":50},[44,1476,1019],{"class":54},[44,1478,62],{"class":50},[44,1480,1481,1483,1485,1487,1489,1491],{"class":46,"line":1100},[44,1482,51],{"class":50},[44,1484,15],{"class":54},[44,1486,1107],{"class":68},[44,1488,1110],{"class":50},[44,1490,15],{"class":54},[44,1492,62],{"class":50},[27,1494],{},[30,1496,1498,1499],{"id":1497},"cleaner-version-pull-it-into-a-computed","Cleaner version: pull it into a ",[19,1500,1501],{},"computed",[15,1503,1504,1505,1507,1508,1510],{},"Repeating ",[19,1506,1409],{}," everywhere in the template gets noisy fast. Extracting it into a ",[19,1509,1501],{}," keeps things readable:",[35,1512,1516],{"className":1513,"code":1514,"language":1515,"meta":40,"style":40},"language-ts shiki shiki-themes github-light github-dark","\u003Cscript setup>\nimport { usePosts } from '~\u002Fcomposables\u002FusePosts'\nimport { computed } from 'vue'\n\nconst { fetchPostsQuery } = usePosts()\nconst postsQuery = fetchPostsQuery()\n\nconst posts = computed(() => postsQuery.data.value ?? [])\nconst isLoading = computed(() => postsQuery.isLoading.value)\nconst error = computed(() => postsQuery.error.value)\n\u003C\u002Fscript>\n","ts",[19,1517,1518,1527,1541,1553,1558,1577,1591,1595,1620,1638,1656],{"__ignoreMap":40},[44,1519,1520,1522,1525],{"class":46,"line":47},[44,1521,51],{"class":68},[44,1523,1524],{"class":50},"script setup",[44,1526,62],{"class":68},[44,1528,1529,1532,1535,1538],{"class":46,"line":65},[44,1530,1531],{"class":68},"import",[44,1533,1534],{"class":50}," { usePosts } ",[44,1536,1537],{"class":68},"from",[44,1539,1540],{"class":101}," '~\u002Fcomposables\u002FusePosts'\n",[44,1542,1543,1545,1548,1550],{"class":46,"line":120},[44,1544,1531],{"class":68},[44,1546,1547],{"class":50}," { computed } ",[44,1549,1537],{"class":68},[44,1551,1552],{"class":101}," 'vue'\n",[44,1554,1555],{"class":46,"line":151},[44,1556,1557],{"emptyLinePlaceholder":517},"\n",[44,1559,1560,1562,1564,1567,1569,1571,1574],{"class":46,"line":160},[44,1561,69],{"class":68},[44,1563,72],{"class":50},[44,1565,1566],{"class":82},"fetchPostsQuery",[44,1568,86],{"class":50},[44,1570,89],{"class":68},[44,1572,1573],{"class":58}," usePosts",[44,1575,1576],{"class":50},"()\n",[44,1578,1579,1581,1584,1586,1589],{"class":46,"line":177},[44,1580,69],{"class":68},[44,1582,1583],{"class":82}," postsQuery",[44,1585,129],{"class":68},[44,1587,1588],{"class":58}," fetchPostsQuery",[44,1590,1576],{"class":50},[44,1592,1593],{"class":46,"line":1080},[44,1594,1557],{"emptyLinePlaceholder":517},[44,1596,1597,1599,1602,1604,1607,1609,1611,1614,1617],{"class":46,"line":1090},[44,1598,69],{"class":68},[44,1600,1601],{"class":82}," posts",[44,1603,129],{"class":68},[44,1605,1606],{"class":58}," computed",[44,1608,601],{"class":50},[44,1610,114],{"class":68},[44,1612,1613],{"class":50}," postsQuery.data.value ",[44,1615,1616],{"class":68},"??",[44,1618,1619],{"class":50}," [])\n",[44,1621,1622,1624,1627,1629,1631,1633,1635],{"class":46,"line":1100},[44,1623,69],{"class":68},[44,1625,1626],{"class":82}," isLoading",[44,1628,129],{"class":68},[44,1630,1606],{"class":58},[44,1632,601],{"class":50},[44,1634,114],{"class":68},[44,1636,1637],{"class":50}," postsQuery.isLoading.value)\n",[44,1639,1640,1642,1645,1647,1649,1651,1653],{"class":46,"line":1117},[44,1641,69],{"class":68},[44,1643,1644],{"class":82}," error",[44,1646,129],{"class":68},[44,1648,1606],{"class":58},[44,1650,601],{"class":50},[44,1652,114],{"class":68},[44,1654,1655],{"class":50}," postsQuery.error.value)\n",[44,1657,1659,1661,1663],{"class":46,"line":1658},11,[44,1660,180],{"class":68},[44,1662,55],{"class":50},[44,1664,62],{"class":68},[35,1666,1668],{"className":37,"code":1667,"language":39,"meta":40,"style":40},"\u003Ctemplate>\n  \u003Cdiv>\n    \u003Cp v-if=\"isLoading\">Loading...\u003C\u002Fp>\n    \u003Cp v-else-if=\"error\">Error: {{ error.message }}\u003C\u002Fp>\n    \u003Cdiv v-else-if=\"posts.length > 0\">\n      \u003Cul>\n        \u003Cli v-for=\"post in posts\" :key=\"post.id\">{{ post.title }}\u003C\u002Fli>\n      \u003C\u002Ful>\n    \u003C\u002Fdiv>\n    \u003Cp v-else>No data.\u003C\u002Fp>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n",[19,1669,1670,1678,1686,1706,1727,1742,1750,1777,1785,1793,1807,1815],{"__ignoreMap":40},[44,1671,1672,1674,1676],{"class":46,"line":47},[44,1673,51],{"class":50},[44,1675,1009],{"class":54},[44,1677,62],{"class":50},[44,1679,1680,1682,1684],{"class":46,"line":65},[44,1681,1016],{"class":50},[44,1683,1019],{"class":54},[44,1685,62],{"class":50},[44,1687,1688,1690,1692,1694,1696,1699,1702,1704],{"class":46,"line":120},[44,1689,1034],{"class":50},[44,1691,15],{"class":54},[44,1693,1022],{"class":58},[44,1695,89],{"class":50},[44,1697,1698],{"class":101},"\"isLoading\"",[44,1700,1701],{"class":50},">Loading...\u003C\u002F",[44,1703,15],{"class":54},[44,1705,62],{"class":50},[44,1707,1708,1710,1712,1715,1717,1720,1723,1725],{"class":46,"line":151},[44,1709,1034],{"class":50},[44,1711,15],{"class":54},[44,1713,1714],{"class":58}," v-else-if",[44,1716,89],{"class":50},[44,1718,1719],{"class":101},"\"error\"",[44,1721,1722],{"class":50},">Error: {{ error.message }}\u003C\u002F",[44,1724,15],{"class":54},[44,1726,62],{"class":50},[44,1728,1729,1731,1733,1735,1737,1740],{"class":46,"line":160},[44,1730,1034],{"class":50},[44,1732,1019],{"class":54},[44,1734,1714],{"class":58},[44,1736,89],{"class":50},[44,1738,1739],{"class":101},"\"posts.length > 0\"",[44,1741,62],{"class":50},[44,1743,1744,1746,1748],{"class":46,"line":177},[44,1745,1043],{"class":50},[44,1747,186],{"class":54},[44,1749,62],{"class":50},[44,1751,1752,1755,1757,1759,1761,1764,1766,1768,1770,1773,1775],{"class":46,"line":1080},[44,1753,1754],{"class":50},"        \u003C",[44,1756,189],{"class":54},[44,1758,1048],{"class":58},[44,1760,89],{"class":50},[44,1762,1763],{"class":101},"\"post in posts\"",[44,1765,1056],{"class":58},[44,1767,89],{"class":50},[44,1769,1061],{"class":101},[44,1771,1772],{"class":50},">{{ post.title }}\u003C\u002F",[44,1774,189],{"class":54},[44,1776,62],{"class":50},[44,1778,1779,1781,1783],{"class":46,"line":1090},[44,1780,1073],{"class":50},[44,1782,186],{"class":54},[44,1784,62],{"class":50},[44,1786,1787,1789,1791],{"class":46,"line":1100},[44,1788,1083],{"class":50},[44,1790,1019],{"class":54},[44,1792,62],{"class":50},[44,1794,1795,1797,1799,1801,1803,1805],{"class":46,"line":1117},[44,1796,1034],{"class":50},[44,1798,15],{"class":54},[44,1800,1107],{"class":58},[44,1802,1110],{"class":50},[44,1804,15],{"class":54},[44,1806,62],{"class":50},[44,1808,1809,1811,1813],{"class":46,"line":1658},[44,1810,1093],{"class":50},[44,1812,1019],{"class":54},[44,1814,62],{"class":50},[44,1816,1818,1820,1822],{"class":46,"line":1817},12,[44,1819,180],{"class":50},[44,1821,1009],{"class":54},[44,1823,62],{"class":50},[27,1825],{},[30,1827,1829],{"id":1828},"the-composable-behind-it","The composable behind it",[35,1831,1833],{"className":1513,"code":1832,"language":1515,"meta":40,"style":40},"\u002F\u002F ~\u002Fcomposables\u002FusePosts.ts\nimport { useNuxtApp } from '#app'\n\nexport const usePostApi = () => {\n  const axios = useNuxtApp().$axios\n  return {\n    fetchPosts: async () => {\n      const response = await axios.get('\u002Fposts')\n      return response.data\n    }\n  }\n}\n\nexport const usePosts = () => {\n  const api = usePostApi()\n  const fetchPostsQuery = () => useQuery({\n    queryKey: ['posts'],\n    queryFn: api.fetchPosts\n  })\n  return { fetchPostsQuery }\n}\n",[19,1834,1835,1840,1852,1856,1875,1890,1896,1911,1937,1945,1950,1955,1960,1965,1982,1996,2015,2027,2033,2039,2047],{"__ignoreMap":40},[44,1836,1837],{"class":46,"line":47},[44,1838,1839],{"class":1339},"\u002F\u002F ~\u002Fcomposables\u002FusePosts.ts\n",[44,1841,1842,1844,1847,1849],{"class":46,"line":65},[44,1843,1531],{"class":68},[44,1845,1846],{"class":50}," { useNuxtApp } ",[44,1848,1537],{"class":68},[44,1850,1851],{"class":101}," '#app'\n",[44,1853,1854],{"class":46,"line":120},[44,1855,1557],{"emptyLinePlaceholder":517},[44,1857,1858,1861,1864,1867,1869,1871,1873],{"class":46,"line":151},[44,1859,1860],{"class":68},"export",[44,1862,1863],{"class":68}," const",[44,1865,1866],{"class":58}," usePostApi",[44,1868,129],{"class":68},[44,1870,111],{"class":50},[44,1872,114],{"class":68},[44,1874,117],{"class":50},[44,1876,1877,1879,1882,1884,1887],{"class":46,"line":160},[44,1878,123],{"class":68},[44,1880,1881],{"class":82}," axios",[44,1883,129],{"class":68},[44,1885,1886],{"class":58}," useNuxtApp",[44,1888,1889],{"class":50},"().$axios\n",[44,1891,1892,1894],{"class":46,"line":177},[44,1893,154],{"class":68},[44,1895,117],{"class":50},[44,1897,1898,1901,1903,1905,1907,1909],{"class":46,"line":1080},[44,1899,1900],{"class":58},"    fetchPosts",[44,1902,79],{"class":50},[44,1904,108],{"class":68},[44,1906,111],{"class":50},[44,1908,114],{"class":68},[44,1910,117],{"class":50},[44,1912,1913,1916,1919,1921,1923,1926,1929,1931,1934],{"class":46,"line":1090},[44,1914,1915],{"class":68},"      const",[44,1917,1918],{"class":82}," response",[44,1920,129],{"class":68},[44,1922,92],{"class":68},[44,1924,1925],{"class":50}," axios.",[44,1927,1928],{"class":58},"get",[44,1930,98],{"class":50},[44,1932,1933],{"class":101},"'\u002Fposts'",[44,1935,1936],{"class":50},")\n",[44,1938,1939,1942],{"class":46,"line":1100},[44,1940,1941],{"class":68},"      return",[44,1943,1944],{"class":50}," response.data\n",[44,1946,1947],{"class":46,"line":1117},[44,1948,1949],{"class":50},"    }\n",[44,1951,1952],{"class":46,"line":1658},[44,1953,1954],{"class":50},"  }\n",[44,1956,1957],{"class":46,"line":1817},[44,1958,1959],{"class":50},"}\n",[44,1961,1963],{"class":46,"line":1962},13,[44,1964,1557],{"emptyLinePlaceholder":517},[44,1966,1968,1970,1972,1974,1976,1978,1980],{"class":46,"line":1967},14,[44,1969,1860],{"class":68},[44,1971,1863],{"class":68},[44,1973,1573],{"class":58},[44,1975,129],{"class":68},[44,1977,111],{"class":50},[44,1979,114],{"class":68},[44,1981,117],{"class":50},[44,1983,1985,1987,1990,1992,1994],{"class":46,"line":1984},15,[44,1986,123],{"class":68},[44,1988,1989],{"class":82}," api",[44,1991,129],{"class":68},[44,1993,1866],{"class":58},[44,1995,1576],{"class":50},[44,1997,1999,2001,2003,2005,2007,2009,2012],{"class":46,"line":1998},16,[44,2000,123],{"class":68},[44,2002,1588],{"class":58},[44,2004,129],{"class":68},[44,2006,111],{"class":50},[44,2008,114],{"class":68},[44,2010,2011],{"class":58}," useQuery",[44,2013,2014],{"class":50},"({\n",[44,2016,2018,2021,2024],{"class":46,"line":2017},17,[44,2019,2020],{"class":50},"    queryKey: [",[44,2022,2023],{"class":101},"'posts'",[44,2025,2026],{"class":50},"],\n",[44,2028,2030],{"class":46,"line":2029},18,[44,2031,2032],{"class":50},"    queryFn: api.fetchPosts\n",[44,2034,2036],{"class":46,"line":2035},19,[44,2037,2038],{"class":50},"  })\n",[44,2040,2042,2044],{"class":46,"line":2041},20,[44,2043,154],{"class":68},[44,2045,2046],{"class":50}," { fetchPostsQuery }\n",[44,2048,2050],{"class":46,"line":2049},21,[44,2051,1959],{"class":50},[27,2053],{},[30,2055,493],{"id":492},[186,2057,2058,2065,2081],{},[189,2059,2060,2062,2063,264],{},[19,2061,1211],{}," returns every piece of state as a ",[19,2064,986],{},[189,2066,2067,2068,2071,2072,2074,2075,2077,2078,2080],{},"Template interpolation (",[19,2069,2070],{},"{{ }}",") auto-unwraps refs, but ",[19,2073,1325],{}," \u002F ",[19,2076,1328],{}," expressions do not — you need ",[19,2079,1409],{}," explicitly.",[189,2082,2083,2084,2086],{},"Pulling the unwrapped values into ",[19,2085,1501],{}," properties keeps the template clean and avoids this trap entirely.",[501,2088,2089],{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}",{"title":40,"searchDepth":65,"depth":65,"links":2091},[2092,2093,2095,2096,2098,2100,2101],{"id":996,"depth":65,"text":997},{"id":1208,"depth":65,"text":2094},"Root cause: useQuery() returns refs, not plain values",{"id":1307,"depth":65,"text":1308},{"id":1405,"depth":65,"text":2097},"The fix: access .value explicitly",{"id":1497,"depth":65,"text":2099},"Cleaner version: pull it into a computed",{"id":1828,"depth":65,"text":1829},{"id":492,"depth":65,"text":493},"Data logs fine in the console but never renders — a walkthrough of why v-if and v-for don't auto-unwrap refs returned by @tanstack\u002Fvue-query, and how to fix it.",{"date":516},"\u002Fen\u002Fvue-query-ref-value-trap-nuxt3",{"title":961,"description":2102},{"loc":2104},"en\u002Fvue-query-ref-value-trap-nuxt3","6trzcsz1qIAtysNFxg1stZY0d0MY_0MmwS7kLkG9wac",1791347386712]