[{"data":1,"prerenderedAt":5709},["ShallowReactive",2],{"en-nuxt-category-articles":3},[4,1848,2436,3877,4560,4733],{"id":5,"title":6,"articleCover":7,"articleCoverEmbed":8,"articleCoverEmbedUploadDate":8,"articleCoverHeight":9,"articleCoverWidth":10,"authorId":11,"body":12,"categoriesIds":1833,"description":1836,"enAlternateSlug":196,"extension":1837,"meta":1838,"navigation":267,"path":1839,"ptBrAlternateSlug":1840,"publishedAt":1841,"seo":1842,"sitemap":1843,"stem":1845,"updatedAt":1844,"wordCount":1846,"__hash__":1847},"blogEn\u002Fen\u002Fblog\u002Fhow-the-routing-system-works-in-nuxt.md","Nuxt Routing: Dynamic, Nested and Catch-All Routes","\u002Fimages\u002Fblog\u002Fnuxt\u002Fnuxt-routing-thumb.webp",null,720,1280,"thiago-nunes-batista",{"type":13,"value":14,"toc":1808},"minimark",[15,24,29,39,83,109,112,116,119,129,144,148,151,187,198,201,573,600,607,612,626,727,732,736,739,742,748,755,896,902,906,917,940,944,951,984,987,991,1004,1083,1086,1090,1100,1106,1129,1252,1355,1362,1366,1369,1447,1458,1462,1471,1650,1670,1674,1700,1704,1708,1721,1725,1734,1738,1751,1755,1767,1771,1790,1794,1804],[16,17,18,19,23],"p",{},"Nuxt turns the files inside the ",[20,21,22],"code",{},"pages"," folder into routes. This article covers how that mapping works in Nuxt 4, including dynamic slugs, nested, optional and catch-all routes and query parameters, and the routing choices that affect SEO on a real website.",[25,26,28],"h2",{"id":27},"how-file-based-routing-works-in-nuxt","How File-Based Routing Works in Nuxt",[16,30,31,32,35,36,38],{},"Every ",[20,33,34],{},".vue"," file inside the ",[20,37,22],{}," folder becomes a page, and its URL comes from the file path:",[40,41,42,55],"table",{},[43,44,45],"thead",{},[46,47,48,52],"tr",{},[49,50,51],"th",{},"File",[49,53,54],{},"Route",[56,57,58,67,75],"tbody",{},[46,59,60,64],{},[61,62,63],"td",{},"app\u002Fpages\u002Findex.vue",[61,65,66],{},"\u002F",[46,68,69,72],{},[61,70,71],{},"app\u002Fpages\u002Flinks.vue",[61,73,74],{},"\u002Flinks",[46,76,77,80],{},[61,78,79],{},"app\u002Fpages\u002Ftools\u002Fcpf-generator.vue",[61,81,82],{},"\u002Ftools\u002Fcpf-generator",[16,84,85,86,89,90,93,94,97,98,101,102,105,106,108],{},"In Nuxt 4, new projects keep application code inside an ",[20,87,88],{},"app\u002F"," directory, so pages live in ",[20,91,92],{},"app\u002Fpages\u002F",", layouts in ",[20,95,96],{},"app\u002Flayouts\u002F"," and route middleware in ",[20,99,100],{},"app\u002Fmiddleware\u002F",". If you migrated from Nuxt 3 and still have a top-level ",[20,103,104],{},"pages\u002F"," folder, it keeps working: Nuxt detects the old structure and does not force the new one. The examples below use the ",[20,107,88],{}," paths.",[16,110,111],{},"This system uses Vue Router under the hood. Nuxt generates the route list from your files, so you rarely need to write a router configuration by hand.",[25,113,115],{"id":114},"static-routes","Static Routes",[16,117,118],{},"A static route is a page whose URL never changes, such as an about page or a tool page:",[120,121,127],"pre",{"className":122,"code":124,"language":125,"meta":126},[123],"language-text","app\u002Fpages\u002F\n├── index.vue              -> \u002F\n├── about.vue              -> \u002Fabout\n├── privacy-policy.vue     -> \u002Fprivacy-policy\n└── tools\u002F\n    └── cpf-generator.vue  -> \u002Ftools\u002Fcpf-generator\n","text","",[20,128,124],{"__ignoreMap":126},[16,130,131,132,135,136,139,140,143],{},"If the base URL is ",[20,133,134],{},"https:\u002F\u002Fonlinestore.com",", Nuxt serves ",[20,137,138],{},"https:\u002F\u002Fonlinestore.com\u002Fabout"," and ",[20,141,142],{},"https:\u002F\u002Fonlinestore.com\u002Fprivacy-policy"," from these files.",[25,145,147],{"id":146},"dynamic-routes","Dynamic Routes",[16,149,150],{},"When many URLs share the same page template, such as the posts of a blog, you create one file with the parameter name inside square brackets. Both layouts below produce the same route:",[40,152,153,164],{},[43,154,155],{},[46,156,157,160,162],{},[49,158,159],{},"Parameter location",[49,161,51],{},[49,163,54],{},[56,165,166,177],{},[46,167,168,171,174],{},[61,169,170],{},"File name",[61,172,173],{},"app\u002Fpages\u002Fblog\u002F[slug].vue",[61,175,176],{},"\u002Fblog\u002Fmy-post",[46,178,179,182,185],{},[61,180,181],{},"Directory name",[61,183,184],{},"app\u002Fpages\u002Fblog\u002F[slug]\u002Findex.vue",[61,186,176],{},[16,188,189,190,193,194,197],{},"The name inside the brackets is the key you read from ",[20,191,192],{},"route.params",". A slug is the readable part of a URL that identifies a resource, usually derived from its title, like ",[20,195,196],{},"how-the-routing-system-works-in-nuxt",". It is not a database id: nothing guarantees it is unique or that it never changes, so your application decides how to resolve it.",[16,199,200],{},"Here is a post page that loads its content from Nuxt Content using the slug:",[120,202,206],{"className":203,"code":204,"language":205,"meta":126,"style":126},"language-vue shiki shiki-themes dracula","\u003Cscript setup lang=\"ts\">\nconst route = useRoute();\n\nconst slug = computed(() => String(route.params.slug));\n\nconst { data: post } = await useAsyncData(\n  () => `post-${slug.value}`,\n  () => queryCollection(\"blog\").path(`\u002Fblog\u002F${slug.value}`).first()\n);\n\nif (!post.value) {\n  throw createError({\n    status: 404,\n    statusText: \"Post not found\",\n    fatal: true\n  });\n}\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Carticle v-if=\"post\">\n    \u003Ch1>{{ post.title }}\u003C\u002Fh1>\n  \u003C\u002Farticle>\n\u003C\u002Ftemplate>\n","vue",[20,207,208,244,262,269,294,299,324,350,397,403,408,423,435,449,467,478,484,490,500,505,515,538,554,564],{"__ignoreMap":126},[209,210,213,217,221,225,228,231,235,239,241],"span",{"class":211,"line":212},"line",1,[209,214,216],{"class":215},"sCdxs","\u003C",[209,218,220],{"class":219},"s0Tla","script",[209,222,224],{"class":223},"sY_PY"," setup",[209,226,227],{"class":223}," lang",[209,229,230],{"class":219},"=",[209,232,234],{"class":233},"seVfx","\"",[209,236,238],{"class":237},"s-mGx","ts",[209,240,234],{"class":233},[209,242,243],{"class":215},">\n",[209,245,247,250,253,255,259],{"class":211,"line":246},2,[209,248,249],{"class":219},"const",[209,251,252],{"class":215}," route ",[209,254,230],{"class":219},[209,256,258],{"class":257},"sAOxA"," useRoute",[209,260,261],{"class":215},"();\n",[209,263,265],{"class":211,"line":264},3,[209,266,268],{"emptyLinePlaceholder":267},true,"\n",[209,270,272,274,277,279,282,285,288,291],{"class":211,"line":271},4,[209,273,249],{"class":219},[209,275,276],{"class":215}," slug ",[209,278,230],{"class":219},[209,280,281],{"class":257}," computed",[209,283,284],{"class":215},"(() ",[209,286,287],{"class":219},"=>",[209,289,290],{"class":257}," String",[209,292,293],{"class":215},"(route.params.slug));\n",[209,295,297],{"class":211,"line":296},5,[209,298,268],{"emptyLinePlaceholder":267},[209,300,302,304,307,310,313,315,318,321],{"class":211,"line":301},6,[209,303,249],{"class":219},[209,305,306],{"class":215}," { data",[209,308,309],{"class":219},":",[209,311,312],{"class":215}," post } ",[209,314,230],{"class":219},[209,316,317],{"class":219}," await",[209,319,320],{"class":257}," useAsyncData",[209,322,323],{"class":215},"(\n",[209,325,327,330,332,335,338,341,344,347],{"class":211,"line":326},7,[209,328,329],{"class":215},"  () ",[209,331,287],{"class":219},[209,333,334],{"class":237}," `post-",[209,336,337],{"class":219},"${",[209,339,340],{"class":215},"slug.value",[209,342,343],{"class":219},"}",[209,345,346],{"class":237},"`",[209,348,349],{"class":215},",\n",[209,351,353,355,357,360,363,365,368,370,373,376,378,381,383,385,387,389,391,394],{"class":211,"line":352},8,[209,354,329],{"class":215},[209,356,287],{"class":219},[209,358,359],{"class":257}," queryCollection",[209,361,362],{"class":215},"(",[209,364,234],{"class":233},[209,366,367],{"class":237},"blog",[209,369,234],{"class":233},[209,371,372],{"class":215},").",[209,374,375],{"class":257},"path",[209,377,362],{"class":215},[209,379,380],{"class":237},"`\u002Fblog\u002F",[209,382,337],{"class":219},[209,384,340],{"class":215},[209,386,343],{"class":219},[209,388,346],{"class":237},[209,390,372],{"class":215},[209,392,393],{"class":257},"first",[209,395,396],{"class":215},"()\n",[209,398,400],{"class":211,"line":399},9,[209,401,402],{"class":215},");\n",[209,404,406],{"class":211,"line":405},10,[209,407,268],{"emptyLinePlaceholder":267},[209,409,411,414,417,420],{"class":211,"line":410},11,[209,412,413],{"class":219},"if",[209,415,416],{"class":215}," (",[209,418,419],{"class":219},"!",[209,421,422],{"class":215},"post.value) {\n",[209,424,426,429,432],{"class":211,"line":425},12,[209,427,428],{"class":219},"  throw",[209,430,431],{"class":257}," createError",[209,433,434],{"class":215},"({\n",[209,436,438,441,443,447],{"class":211,"line":437},13,[209,439,440],{"class":215},"    status",[209,442,309],{"class":219},[209,444,446],{"class":445},"sIQBb"," 404",[209,448,349],{"class":215},[209,450,452,455,457,460,463,465],{"class":211,"line":451},14,[209,453,454],{"class":215},"    statusText",[209,456,309],{"class":219},[209,458,459],{"class":233}," \"",[209,461,462],{"class":237},"Post not found",[209,464,234],{"class":233},[209,466,349],{"class":215},[209,468,470,473,475],{"class":211,"line":469},15,[209,471,472],{"class":215},"    fatal",[209,474,309],{"class":219},[209,476,477],{"class":445}," true\n",[209,479,481],{"class":211,"line":480},16,[209,482,483],{"class":215},"  });\n",[209,485,487],{"class":211,"line":486},17,[209,488,489],{"class":215},"}\n",[209,491,493,496,498],{"class":211,"line":492},18,[209,494,495],{"class":215},"\u003C\u002F",[209,497,220],{"class":219},[209,499,243],{"class":215},[209,501,503],{"class":211,"line":502},19,[209,504,268],{"emptyLinePlaceholder":267},[209,506,508,510,513],{"class":211,"line":507},20,[209,509,216],{"class":215},[209,511,512],{"class":219},"template",[209,514,243],{"class":215},[209,516,518,521,524,527,529,531,534,536],{"class":211,"line":517},21,[209,519,520],{"class":215},"  \u003C",[209,522,523],{"class":219},"article",[209,525,526],{"class":223}," v-if",[209,528,230],{"class":219},[209,530,234],{"class":233},[209,532,533],{"class":237},"post",[209,535,234],{"class":233},[209,537,243],{"class":215},[209,539,541,544,547,550,552],{"class":211,"line":540},22,[209,542,543],{"class":215},"    \u003C",[209,545,546],{"class":219},"h1",[209,548,549],{"class":215},">{{ post.title }}\u003C\u002F",[209,551,546],{"class":219},[209,553,243],{"class":215},[209,555,557,560,562],{"class":211,"line":556},23,[209,558,559],{"class":215},"  \u003C\u002F",[209,561,523],{"class":219},[209,563,243],{"class":215},[209,565,567,569,571],{"class":211,"line":566},24,[209,568,495],{"class":215},[209,570,512],{"class":219},[209,572,243],{"class":215},[16,574,575,576,579,580,583,584,587,588,591,592,595,596,599],{},"The slug is a ",[20,577,578],{},"computed",", and the ",[20,581,582],{},"useAsyncData"," key is a function that depends on it. When the user goes from ",[20,585,586],{},"\u002Fblog\u002Fa"," to ",[20,589,590],{},"\u002Fblog\u002Fb",", Vue Router can keep the same page component mounted, so the code in ",[20,593,594],{},"\u003Cscript setup>"," does not necessarily run again. With a reactive key, Nuxt fetches the new post when the slug changes. If you read ",[20,597,598],{},"route.params.slug"," once and store it in a plain variable, the URL changes but the page keeps showing the previous post.",[16,601,602,603,606],{},"A slug that does not exist also needs a 404. Without the ",[20,604,605],{},"createError"," check, it renders an empty page with a 200 status, and search engines see a valid but empty URL.",[608,609,611],"h3",{"id":610},"validating-the-parameter-before-rendering","Validating the Parameter Before Rendering",[16,613,614,617,618,621,622,625],{},[20,615,616],{},"definePageMeta"," accepts a ",[20,619,620],{},"validate"," function that decides whether the route can be rendered by that page. If it returns ",[20,623,624],{},"false"," and no other route matches, Nuxt responds with a 404:",[120,627,629],{"className":203,"code":628,"language":205,"meta":126,"style":126},"\u003Cscript setup lang=\"ts\">\ndefinePageMeta({\n  validate: (route) => \u002F^[a-z0-9-]+$\u002F.test(String(route.params.slug))\n});\n\u003C\u002Fscript>\n",[20,630,631,651,657,714,719],{"__ignoreMap":126},[209,632,633,635,637,639,641,643,645,647,649],{"class":211,"line":212},[209,634,216],{"class":215},[209,636,220],{"class":219},[209,638,224],{"class":223},[209,640,227],{"class":223},[209,642,230],{"class":219},[209,644,234],{"class":233},[209,646,238],{"class":237},[209,648,234],{"class":233},[209,650,243],{"class":215},[209,652,653,655],{"class":211,"line":246},[209,654,616],{"class":257},[209,656,434],{"class":215},[209,658,659,662,664,666,670,673,675,679,682,686,689,692,695,698,700,703,706,708,711],{"class":211,"line":264},[209,660,661],{"class":257},"  validate",[209,663,309],{"class":219},[209,665,416],{"class":215},[209,667,669],{"class":668},"sGEwX","route",[209,671,672],{"class":215},") ",[209,674,287],{"class":219},[209,676,678],{"class":677},"s-doY"," \u002F",[209,680,681],{"class":219},"^",[209,683,685],{"class":684},"sLL85","[",[209,687,688],{"class":445},"a-z0-9",[209,690,691],{"class":237},"-",[209,693,694],{"class":684},"]",[209,696,697],{"class":219},"+$",[209,699,66],{"class":677},[209,701,702],{"class":215},".",[209,704,705],{"class":257},"test",[209,707,362],{"class":215},[209,709,710],{"class":257},"String",[209,712,713],{"class":215},"(route.params.slug))\n",[209,715,716],{"class":211,"line":271},[209,717,718],{"class":215},"});\n",[209,720,721,723,725],{"class":211,"line":296},[209,722,495],{"class":215},[209,724,220],{"class":219},[209,726,243],{"class":215},[16,728,729,731],{},[20,730,620],{}," only checks the shape of the parameter. Checking that the post exists still needs the data lookup shown above.",[25,733,735],{"id":734},"nested-routes","Nested Routes",[16,737,738],{},"Nested routes are for sections of an interface where a parent area stays on screen while the child content changes. An admin panel with Orders, Products and Settings is a typical case.",[16,740,741],{},"To create one, you need a parent page file and a folder with the same name that holds the children:",[120,743,746],{"className":744,"code":745,"language":125,"meta":126},[123],"app\u002Fpages\u002F\n├── admin.vue\n└── admin\u002F\n    ├── index.vue      -> \u002Fadmin\n    ├── orders.vue     -> \u002Fadmin\u002Forders\n    ├── products.vue   -> \u002Fadmin\u002Fproducts\n    └── settings.vue   -> \u002Fadmin\u002Fsettings\n",[20,747,745],{"__ignoreMap":126},[16,749,750,751,754],{},"The parent file renders the shared interface and uses ",[20,752,753],{},"\u003CNuxtPage \u002F>"," where the child page should appear:",[120,756,758],{"className":203,"code":757,"language":205,"meta":126,"style":126},"\u003Ctemplate>\n  \u003Cdiv>\n    \u003Cnav>\n      \u003CNuxtLink to=\"\u002Fadmin\u002Forders\">Orders\u003C\u002FNuxtLink>\n      \u003CNuxtLink to=\"\u002Fadmin\u002Fproducts\">Products\u003C\u002FNuxtLink>\n      \u003CNuxtLink to=\"\u002Fadmin\u002Fsettings\">Settings\u003C\u002FNuxtLink>\n    \u003C\u002Fnav>\n    \u003CNuxtPage \u002F>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n",[20,759,760,768,777,786,813,837,861,870,880,888],{"__ignoreMap":126},[209,761,762,764,766],{"class":211,"line":212},[209,763,216],{"class":215},[209,765,512],{"class":219},[209,767,243],{"class":215},[209,769,770,772,775],{"class":211,"line":246},[209,771,520],{"class":215},[209,773,774],{"class":219},"div",[209,776,243],{"class":215},[209,778,779,781,784],{"class":211,"line":264},[209,780,543],{"class":215},[209,782,783],{"class":219},"nav",[209,785,243],{"class":215},[209,787,788,791,794,797,799,801,804,806,809,811],{"class":211,"line":271},[209,789,790],{"class":215},"      \u003C",[209,792,793],{"class":219},"NuxtLink",[209,795,796],{"class":223}," to",[209,798,230],{"class":219},[209,800,234],{"class":233},[209,802,803],{"class":237},"\u002Fadmin\u002Forders",[209,805,234],{"class":233},[209,807,808],{"class":215},">Orders\u003C\u002F",[209,810,793],{"class":219},[209,812,243],{"class":215},[209,814,815,817,819,821,823,825,828,830,833,835],{"class":211,"line":296},[209,816,790],{"class":215},[209,818,793],{"class":219},[209,820,796],{"class":223},[209,822,230],{"class":219},[209,824,234],{"class":233},[209,826,827],{"class":237},"\u002Fadmin\u002Fproducts",[209,829,234],{"class":233},[209,831,832],{"class":215},">Products\u003C\u002F",[209,834,793],{"class":219},[209,836,243],{"class":215},[209,838,839,841,843,845,847,849,852,854,857,859],{"class":211,"line":301},[209,840,790],{"class":215},[209,842,793],{"class":219},[209,844,796],{"class":223},[209,846,230],{"class":219},[209,848,234],{"class":233},[209,850,851],{"class":237},"\u002Fadmin\u002Fsettings",[209,853,234],{"class":233},[209,855,856],{"class":215},">Settings\u003C\u002F",[209,858,793],{"class":219},[209,860,243],{"class":215},[209,862,863,866,868],{"class":211,"line":326},[209,864,865],{"class":215},"    \u003C\u002F",[209,867,783],{"class":219},[209,869,243],{"class":215},[209,871,872,874,877],{"class":211,"line":352},[209,873,543],{"class":215},[209,875,876],{"class":219},"NuxtPage",[209,878,879],{"class":215}," \u002F>\n",[209,881,882,884,886],{"class":211,"line":399},[209,883,559],{"class":215},[209,885,774],{"class":219},[209,887,243],{"class":215},[209,889,890,892,894],{"class":211,"line":405},[209,891,495],{"class":215},[209,893,512],{"class":219},[209,895,243],{"class":215},[16,897,898,899,901],{},"Navigating between the children only swaps what ",[20,900,753],{}," renders, so the menu stays in place.",[608,903,905],{"id":904},"nested-routes-are-not-layouts","Nested Routes Are Not Layouts",[16,907,908,909,912,913,916],{},"Nuxt has no ",[20,910,911],{},"layout.vue"," file inside ",[20,914,915],{},"pages\u002Fadmin",". A parent page and a layout are separate features:",[918,919,920,930],"ul",{},[921,922,923,924,927,928,702],"li",{},"A nested parent page (",[20,925,926],{},"admin.vue",") is part of the route tree. It renders child routes through ",[20,929,753],{},[921,931,932,933,935,936,939],{},"A layout lives in ",[20,934,96],{}," and is a reusable shell, like a header and footer. A page picks one with ",[20,937,938],{},"definePageMeta({ layout: \"dashboard\" })",", and the layout renders the page through its slot.",[25,941,943],{"id":942},"optional-and-catch-all-routes","Optional and Catch-All Routes",[16,945,946,947,950],{},"Two more bracket patterns cover cases that a plain ",[20,948,949],{},"[slug]"," does not:",[40,952,953,962],{},[43,954,955],{},[46,956,957,959],{},[49,958,51],{},[49,960,961],{},"Matches",[56,963,964,976],{},[46,965,966,969],{},[61,967,968],{},"app\u002Fpages\u002F[[slug]].vue",[61,970,971,139,973],{},[20,972,66],{},[20,974,975],{},"\u002Fanything",[46,977,978,981],{},[61,979,980],{},"app\u002Fpages\u002F[...slug].vue",[61,982,983],{},"any path, including several segments",[16,985,986],{},"Double brackets make the segment optional. Three dots make it a catch-all that receives the remaining path. Catch-all routes are useful for documentation-style URLs with variable depth and for handling old URLs that you need to redirect.",[25,988,990],{"id":989},"custom-paths-and-aliases","Custom Paths and Aliases",[16,992,993,994,996,997,999,1000,1003],{},"Sometimes the public URL should not follow the file structure. ",[20,995,616],{}," lets you set a custom ",[20,998,375],{}," or add an ",[20,1001,1002],{},"alias"," for a page:",[120,1005,1007],{"className":203,"code":1006,"language":205,"meta":126,"style":126},"\u003Cscript setup lang=\"ts\">\ndefinePageMeta({\n  path: \"\u002Fshirts\u002F:slug\",\n  alias: [\"\u002Ft-shirts\u002F:slug\"]\n});\n\u003C\u002Fscript>\n",[20,1008,1009,1029,1035,1051,1071,1075],{"__ignoreMap":126},[209,1010,1011,1013,1015,1017,1019,1021,1023,1025,1027],{"class":211,"line":212},[209,1012,216],{"class":215},[209,1014,220],{"class":219},[209,1016,224],{"class":223},[209,1018,227],{"class":223},[209,1020,230],{"class":219},[209,1022,234],{"class":233},[209,1024,238],{"class":237},[209,1026,234],{"class":233},[209,1028,243],{"class":215},[209,1030,1031,1033],{"class":211,"line":246},[209,1032,616],{"class":257},[209,1034,434],{"class":215},[209,1036,1037,1040,1042,1044,1047,1049],{"class":211,"line":264},[209,1038,1039],{"class":215},"  path",[209,1041,309],{"class":219},[209,1043,459],{"class":233},[209,1045,1046],{"class":237},"\u002Fshirts\u002F:slug",[209,1048,234],{"class":233},[209,1050,349],{"class":215},[209,1052,1053,1056,1058,1061,1063,1066,1068],{"class":211,"line":271},[209,1054,1055],{"class":215},"  alias",[209,1057,309],{"class":219},[209,1059,1060],{"class":215}," [",[209,1062,234],{"class":233},[209,1064,1065],{"class":237},"\u002Ft-shirts\u002F:slug",[209,1067,234],{"class":233},[209,1069,1070],{"class":215},"]\n",[209,1072,1073],{"class":211,"line":296},[209,1074,718],{"class":215},[209,1076,1077,1079,1081],{"class":211,"line":301},[209,1078,495],{"class":215},[209,1080,220],{"class":219},[209,1082,243],{"class":215},[16,1084,1085],{},"An alias makes the same page answer on more than one URL. That creates duplicate URLs, so only use it when you really need both, and point the canonical to the one you want in the search results.",[25,1087,1089],{"id":1088},"query-parameters","Query Parameters",[16,1091,1092,1093,1096,1097,309],{},"Query parameters carry optional state that does not change which resource the page represents: a filter, the current page of a list or a search term. The format is ",[20,1094,1095],{},"?name=value",", and extra parameters are joined with ",[20,1098,1099],{},"&",[120,1101,1104],{"className":1102,"code":1103,"language":125,"meta":126},[123],"https:\u002F\u002Fonlinestore.com\u002Ftshirts?color=blue&size=m&page=2\n",[20,1105,1103],{"__ignoreMap":126},[16,1107,1108,1109,1112,1113,1116,1117,1120,1121,1124,1125,1128],{},"You read them with ",[20,1110,1111],{},"useRoute().query",". The value of a parameter is not always a single string. It can be a string, ",[20,1114,1115],{},"null"," (for ",[20,1118,1119],{},"?color"," with no value), an array (for ",[20,1122,1123],{},"?tag=seo&tag=nuxt",") or ",[20,1126,1127],{},"undefined"," when the parameter is missing. A small helper avoids bugs when you use the value in a filter or API call:",[120,1130,1133],{"className":1131,"code":1132,"language":238,"meta":126,"style":126},"language-ts shiki shiki-themes dracula","\u002F\u002F app\u002Futils\u002Fquery.ts\nexport function firstQueryValue(\n  value: string | null | (string | null)[] | undefined\n) {\n  const first = Array.isArray(value) ? value[0] : value;\n  return first ?? undefined;\n}\n",[20,1134,1135,1141,1154,1191,1196,1232,1248],{"__ignoreMap":126},[209,1136,1137],{"class":211,"line":212},[209,1138,1140],{"class":1139},"shSDL","\u002F\u002F app\u002Futils\u002Fquery.ts\n",[209,1142,1143,1146,1149,1152],{"class":211,"line":246},[209,1144,1145],{"class":219},"export",[209,1147,1148],{"class":219}," function",[209,1150,1151],{"class":257}," firstQueryValue",[209,1153,323],{"class":215},[209,1155,1156,1159,1161,1165,1168,1171,1173,1175,1178,1180,1182,1185,1188],{"class":211,"line":264},[209,1157,1158],{"class":668},"  value",[209,1160,309],{"class":219},[209,1162,1164],{"class":1163},"sCp4m"," string",[209,1166,1167],{"class":219}," |",[209,1169,1170],{"class":1163}," null",[209,1172,1167],{"class":219},[209,1174,416],{"class":215},[209,1176,1177],{"class":1163},"string",[209,1179,1167],{"class":219},[209,1181,1170],{"class":1163},[209,1183,1184],{"class":215},")[] ",[209,1186,1187],{"class":219},"|",[209,1189,1190],{"class":1163}," undefined\n",[209,1192,1193],{"class":211,"line":271},[209,1194,1195],{"class":215},") {\n",[209,1197,1198,1201,1204,1206,1209,1212,1215,1218,1221,1224,1227,1229],{"class":211,"line":296},[209,1199,1200],{"class":219},"  const",[209,1202,1203],{"class":215}," first ",[209,1205,230],{"class":219},[209,1207,1208],{"class":215}," Array.",[209,1210,1211],{"class":257},"isArray",[209,1213,1214],{"class":215},"(value) ",[209,1216,1217],{"class":219},"?",[209,1219,1220],{"class":215}," value[",[209,1222,1223],{"class":445},"0",[209,1225,1226],{"class":215},"] ",[209,1228,309],{"class":219},[209,1230,1231],{"class":215}," value;\n",[209,1233,1234,1237,1239,1242,1245],{"class":211,"line":301},[209,1235,1236],{"class":219},"  return",[209,1238,1203],{"class":215},[209,1240,1241],{"class":219},"??",[209,1243,1244],{"class":445}," undefined",[209,1246,1247],{"class":215},";\n",[209,1249,1250],{"class":211,"line":326},[209,1251,489],{"class":215},[120,1253,1255],{"className":203,"code":1254,"language":205,"meta":126,"style":126},"\u003Cscript setup lang=\"ts\">\nconst route = useRoute();\n\nconst page = computed(() => Number(firstQueryValue(route.query.page)) || 1);\nconst color = computed(() => firstQueryValue(route.query.color));\n\u003C\u002Fscript>\n",[20,1256,1257,1277,1289,1293,1327,1347],{"__ignoreMap":126},[209,1258,1259,1261,1263,1265,1267,1269,1271,1273,1275],{"class":211,"line":212},[209,1260,216],{"class":215},[209,1262,220],{"class":219},[209,1264,224],{"class":223},[209,1266,227],{"class":223},[209,1268,230],{"class":219},[209,1270,234],{"class":233},[209,1272,238],{"class":237},[209,1274,234],{"class":233},[209,1276,243],{"class":215},[209,1278,1279,1281,1283,1285,1287],{"class":211,"line":246},[209,1280,249],{"class":219},[209,1282,252],{"class":215},[209,1284,230],{"class":219},[209,1286,258],{"class":257},[209,1288,261],{"class":215},[209,1290,1291],{"class":211,"line":264},[209,1292,268],{"emptyLinePlaceholder":267},[209,1294,1295,1297,1300,1302,1304,1306,1308,1311,1313,1316,1319,1322,1325],{"class":211,"line":271},[209,1296,249],{"class":219},[209,1298,1299],{"class":215}," page ",[209,1301,230],{"class":219},[209,1303,281],{"class":257},[209,1305,284],{"class":215},[209,1307,287],{"class":219},[209,1309,1310],{"class":257}," Number",[209,1312,362],{"class":215},[209,1314,1315],{"class":257},"firstQueryValue",[209,1317,1318],{"class":215},"(route.query.page)) ",[209,1320,1321],{"class":219},"||",[209,1323,1324],{"class":445}," 1",[209,1326,402],{"class":215},[209,1328,1329,1331,1334,1336,1338,1340,1342,1344],{"class":211,"line":296},[209,1330,249],{"class":219},[209,1332,1333],{"class":215}," color ",[209,1335,230],{"class":219},[209,1337,281],{"class":257},[209,1339,284],{"class":215},[209,1341,287],{"class":219},[209,1343,1151],{"class":257},[209,1345,1346],{"class":215},"(route.query.color));\n",[209,1348,1349,1351,1353],{"class":211,"line":301},[209,1350,495],{"class":215},[209,1352,220],{"class":219},[209,1354,243],{"class":215},[16,1356,1357,1358,1361],{},"Nuxt auto-imports the helper from ",[20,1359,1360],{},"app\u002Futils",", and both values update when the query changes.",[608,1363,1365],{"id":1364},"query-strings-and-seo","Query Strings and SEO",[16,1367,1368],{},"Every combination of filters creates a different URL, and a listing with a few filters can produce a very large number of them. For URLs that only sort or narrow the same list, set a canonical that points to the unfiltered listing:",[120,1370,1372],{"className":203,"code":1371,"language":205,"meta":126,"style":126},"\u003Cscript setup lang=\"ts\">\nuseHead({\n  link: [{ rel: \"canonical\", href: \"https:\u002F\u002Fonlinestore.com\u002Ftshirts\" }]\n});\n\u003C\u002Fscript>\n",[20,1373,1374,1394,1401,1435,1439],{"__ignoreMap":126},[209,1375,1376,1378,1380,1382,1384,1386,1388,1390,1392],{"class":211,"line":212},[209,1377,216],{"class":215},[209,1379,220],{"class":219},[209,1381,224],{"class":223},[209,1383,227],{"class":223},[209,1385,230],{"class":219},[209,1387,234],{"class":233},[209,1389,238],{"class":237},[209,1391,234],{"class":233},[209,1393,243],{"class":215},[209,1395,1396,1399],{"class":211,"line":246},[209,1397,1398],{"class":257},"useHead",[209,1400,434],{"class":215},[209,1402,1403,1406,1408,1411,1413,1415,1418,1420,1423,1425,1427,1430,1432],{"class":211,"line":264},[209,1404,1405],{"class":215},"  link",[209,1407,309],{"class":219},[209,1409,1410],{"class":215}," [{ rel",[209,1412,309],{"class":219},[209,1414,459],{"class":233},[209,1416,1417],{"class":237},"canonical",[209,1419,234],{"class":233},[209,1421,1422],{"class":215},", href",[209,1424,309],{"class":219},[209,1426,459],{"class":233},[209,1428,1429],{"class":237},"https:\u002F\u002Fonlinestore.com\u002Ftshirts",[209,1431,234],{"class":233},[209,1433,1434],{"class":215}," }]\n",[209,1436,1437],{"class":211,"line":271},[209,1438,718],{"class":215},[209,1440,1441,1443,1445],{"class":211,"line":296},[209,1442,495],{"class":215},[209,1444,220],{"class":219},[209,1446,243],{"class":215},[16,1448,1449,1450,1453,1454,1457],{},"Paginated pages are a different case: each page of a list has its own content, so ",[20,1451,1452],{},"?page=2"," keeps a canonical that points to itself, not to page 1. Content that should rank on its own, like a category, is better as a path (",[20,1455,1456],{},"\u002Ftshirts\u002Fblue",") than as a query string.",[25,1459,1461],{"id":1460},"navigating-between-routes","Navigating Between Routes",[16,1463,1464,1465,1467,1468,309],{},"Use ",[20,1466,793],{}," for links the user sees. It renders a normal anchor element and navigates on the client without a full reload. For redirects and navigation triggered by code, use ",[20,1469,1470],{},"navigateTo",[120,1472,1474],{"className":203,"code":1473,"language":205,"meta":126,"style":126},"\u003Cscript setup lang=\"ts\">\nasync function applyFilter() {\n  await navigateTo({ path: \"\u002Ftshirts\", query: { color: \"blue\", size: \"m\" } });\n}\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003CNuxtLink to=\"\u002Fblog\u002Fhow-the-routing-system-works-in-nuxt\"\n    >Read the post\u003C\u002FNuxtLink\n  >\n  \u003Cbutton @click=\"applyFilter\">Blue, size M\u003C\u002Fbutton>\n\u003C\u002Ftemplate>\n",[20,1475,1476,1496,1509,1561,1565,1573,1577,1585,1603,1611,1616,1642],{"__ignoreMap":126},[209,1477,1478,1480,1482,1484,1486,1488,1490,1492,1494],{"class":211,"line":212},[209,1479,216],{"class":215},[209,1481,220],{"class":219},[209,1483,224],{"class":223},[209,1485,227],{"class":223},[209,1487,230],{"class":219},[209,1489,234],{"class":233},[209,1491,238],{"class":237},[209,1493,234],{"class":233},[209,1495,243],{"class":215},[209,1497,1498,1501,1503,1506],{"class":211,"line":246},[209,1499,1500],{"class":219},"async",[209,1502,1148],{"class":219},[209,1504,1505],{"class":257}," applyFilter",[209,1507,1508],{"class":215},"() {\n",[209,1510,1511,1514,1517,1520,1522,1524,1527,1529,1532,1534,1537,1539,1541,1544,1546,1549,1551,1553,1556,1558],{"class":211,"line":264},[209,1512,1513],{"class":219},"  await",[209,1515,1516],{"class":257}," navigateTo",[209,1518,1519],{"class":215},"({ path",[209,1521,309],{"class":219},[209,1523,459],{"class":233},[209,1525,1526],{"class":237},"\u002Ftshirts",[209,1528,234],{"class":233},[209,1530,1531],{"class":215},", query",[209,1533,309],{"class":219},[209,1535,1536],{"class":215}," { color",[209,1538,309],{"class":219},[209,1540,459],{"class":233},[209,1542,1543],{"class":237},"blue",[209,1545,234],{"class":233},[209,1547,1548],{"class":215},", size",[209,1550,309],{"class":219},[209,1552,459],{"class":233},[209,1554,1555],{"class":237},"m",[209,1557,234],{"class":233},[209,1559,1560],{"class":215}," } });\n",[209,1562,1563],{"class":211,"line":271},[209,1564,489],{"class":215},[209,1566,1567,1569,1571],{"class":211,"line":296},[209,1568,495],{"class":215},[209,1570,220],{"class":219},[209,1572,243],{"class":215},[209,1574,1575],{"class":211,"line":301},[209,1576,268],{"emptyLinePlaceholder":267},[209,1578,1579,1581,1583],{"class":211,"line":326},[209,1580,216],{"class":215},[209,1582,512],{"class":219},[209,1584,243],{"class":215},[209,1586,1587,1589,1591,1593,1595,1597,1600],{"class":211,"line":352},[209,1588,520],{"class":215},[209,1590,793],{"class":219},[209,1592,796],{"class":223},[209,1594,230],{"class":219},[209,1596,234],{"class":233},[209,1598,1599],{"class":237},"\u002Fblog\u002Fhow-the-routing-system-works-in-nuxt",[209,1601,1602],{"class":233},"\"\n",[209,1604,1605,1608],{"class":211,"line":399},[209,1606,1607],{"class":215},"    >Read the post\u003C\u002F",[209,1609,1610],{"class":219},"NuxtLink\n",[209,1612,1613],{"class":211,"line":405},[209,1614,1615],{"class":215},"  >\n",[209,1617,1618,1620,1623,1626,1628,1630,1633,1635,1638,1640],{"class":211,"line":410},[209,1619,520],{"class":215},[209,1621,1622],{"class":219},"button",[209,1624,1625],{"class":223}," @click",[209,1627,230],{"class":219},[209,1629,234],{"class":233},[209,1631,1632],{"class":237},"applyFilter",[209,1634,234],{"class":233},[209,1636,1637],{"class":215},">Blue, size M\u003C\u002F",[209,1639,1622],{"class":219},[209,1641,243],{"class":215},[209,1643,1644,1646,1648],{"class":211,"line":425},[209,1645,495],{"class":215},[209,1647,512],{"class":219},[209,1649,243],{"class":215},[16,1651,1652,1653,1655,1656,139,1659,1662,1663,1666,1667,1669],{},"When a rule applies to many routes, such as requiring a login, use route middleware in ",[20,1654,100],{},". Inside middleware, read the ",[20,1657,1658],{},"to",[20,1660,1661],{},"from"," arguments instead of calling ",[20,1664,1665],{},"useRoute()",". The Nuxt documentation advises against ",[20,1668,1665],{}," there because middleware has no current route yet.",[25,1671,1673],{"id":1672},"common-routing-mistakes","Common Routing Mistakes",[918,1675,1676,1681,1687,1694,1697],{},[921,1677,1678,1679,702],{},"Using a layout as the parent of nested routes instead of a parent page with ",[20,1680,753],{},[921,1682,1683,1684,1686],{},"Reading ",[20,1685,192],{}," once instead of deriving data from it reactively.",[921,1688,1689,1690,1693],{},"Treating ",[20,1691,1692],{},"route.query"," values as plain strings.",[921,1695,1696],{},"Returning an empty page with a 200 status for a slug that does not exist.",[921,1698,1699],{},"Creating filter URLs without deciding which version search engines should index.",[25,1701,1703],{"id":1702},"frequently-asked-questions","Frequently Asked Questions",[608,1705,1707],{"id":1706},"what-is-file-based-routing-in-nuxt","What is file-based routing in Nuxt?",[16,1709,1710,1711,1713,1714,1717,1718,702],{},"Nuxt creates routes from the files and folders inside the ",[20,1712,22],{}," directory. For example, ",[20,1715,1716],{},"app\u002Fpages\u002Fabout.vue"," becomes ",[20,1719,1720],{},"\u002Fabout",[608,1722,1724],{"id":1723},"where-do-pages-go-in-nuxt-4","Where do pages go in Nuxt 4?",[16,1726,1727,1728,1730,1731,1733],{},"New Nuxt 4 projects use ",[20,1729,92],{},". Projects that still have a top-level ",[20,1732,104],{}," folder keep working, because Nuxt detects the old structure.",[608,1735,1737],{"id":1736},"how-do-i-create-a-dynamic-route-in-nuxt","How do I create a dynamic route in Nuxt?",[16,1739,1740,1741,1743,1744,1747,1748,1750],{},"Name the file with a bracketed parameter, such as ",[20,1742,173],{},", and read the value with ",[20,1745,1746],{},"useRoute().params.slug",". Derive anything that depends on it from a ",[20,1749,578],{}," so it updates when the slug changes.",[608,1752,1754],{"id":1753},"what-is-the-difference-between-nested-routes-and-layouts","What is the difference between nested routes and layouts?",[16,1756,1757,1758,1760,1761,1763,1764,702],{},"A nested route uses a parent page with ",[20,1759,753],{}," to render child routes. A layout lives in ",[20,1762,96],{}," and wraps pages with a reusable shell, selected with ",[20,1765,1766],{},"definePageMeta({ layout })",[608,1768,1770],{"id":1769},"how-do-i-show-a-404-for-an-invalid-slug","How do I show a 404 for an invalid slug?",[16,1772,1773,1774,1776,1777,1780,1781,1783,1784,1786,1787,1789],{},"Throw ",[20,1775,605],{}," with ",[20,1778,1779],{},"status: 404"," when the data does not exist, or return ",[20,1782,624],{}," from ",[20,1785,620],{}," in ",[20,1788,616],{}," when the parameter has an invalid format.",[25,1791,1793],{"id":1792},"conclusion","Conclusion",[16,1795,1796,1797,702],{},"Before creating files, list your public URLs: which pages are static, which need a slug and which state belongs in a query string. Then decide which filtered URLs should be indexed. For the full list of options, see the ",[1798,1799,1803],"a",{"href":1800,"rel":1801},"https:\u002F\u002Fnuxt.com\u002Fdocs\u002F4.x\u002Fgetting-started\u002Frouting",[1802],"nofollow","Nuxt routing documentation",[1805,1806,1807],"style",{},"html pre.shiki code .sCdxs, html code.shiki .sCdxs{--shiki-default:#F8F8F2}html pre.shiki code .s0Tla, html code.shiki .s0Tla{--shiki-default:#FF79C6}html pre.shiki code .sY_PY, html code.shiki .sY_PY{--shiki-default:#50FA7B;--shiki-default-font-style:italic}html pre.shiki code .seVfx, html code.shiki .seVfx{--shiki-default:#E9F284}html pre.shiki code .s-mGx, html code.shiki .s-mGx{--shiki-default:#F1FA8C}html pre.shiki code .sAOxA, html code.shiki .sAOxA{--shiki-default:#50FA7B}html pre.shiki code .sIQBb, html code.shiki .sIQBb{--shiki-default:#BD93F9}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 pre.shiki code .sGEwX, html code.shiki .sGEwX{--shiki-default:#FFB86C;--shiki-default-font-style:italic}html pre.shiki code .s-doY, html code.shiki .s-doY{--shiki-default:#FF5555}html pre.shiki code .sLL85, html code.shiki .sLL85{--shiki-default:#8BE9FD}html pre.shiki code .shSDL, html code.shiki .shSDL{--shiki-default:#6272A4}html pre.shiki code .sCp4m, html code.shiki .sCp4m{--shiki-default:#8BE9FD;--shiki-default-font-style:italic}",{"title":126,"searchDepth":246,"depth":246,"links":1809},[1810,1811,1812,1815,1818,1819,1820,1823,1824,1825,1832],{"id":27,"depth":246,"text":28},{"id":114,"depth":246,"text":115},{"id":146,"depth":246,"text":147,"children":1813},[1814],{"id":610,"depth":264,"text":611},{"id":734,"depth":246,"text":735,"children":1816},[1817],{"id":904,"depth":264,"text":905},{"id":942,"depth":246,"text":943},{"id":989,"depth":246,"text":990},{"id":1088,"depth":246,"text":1089,"children":1821},[1822],{"id":1364,"depth":264,"text":1365},{"id":1460,"depth":246,"text":1461},{"id":1672,"depth":246,"text":1673},{"id":1702,"depth":246,"text":1703,"children":1826},[1827,1828,1829,1830,1831],{"id":1706,"depth":264,"text":1707},{"id":1723,"depth":264,"text":1724},{"id":1736,"depth":264,"text":1737},{"id":1753,"depth":264,"text":1754},{"id":1769,"depth":264,"text":1770},{"id":1792,"depth":246,"text":1793},[1834,1835],"nuxt","vuejs","How Nuxt file-based routing works: dynamic, nested and catch-all routes, query params, 404 handling and canonical URLs, with practical Nuxt 4 examples and code.","md",{},"\u002Fen\u002Fblog\u002Fhow-the-routing-system-works-in-nuxt","como-funciona-o-sistema-de-rotas-no-nuxt","2025-03-12",{"title":6,"description":1836},{"loc":1839,"lastmod":1844},"2026-10-06","en\u002Fblog\u002Fhow-the-routing-system-works-in-nuxt",1378,"oB2IGmtiBUJowzUsIjQWjxG2dLcySN1PQ6tQYBgEXNU",{"id":1849,"title":1850,"articleCover":1851,"articleCoverEmbed":8,"articleCoverEmbedUploadDate":8,"articleCoverHeight":9,"articleCoverWidth":10,"authorId":11,"body":1852,"categoriesIds":2422,"description":2423,"enAlternateSlug":2424,"extension":1837,"meta":2425,"navigation":267,"path":2426,"ptBrAlternateSlug":2427,"publishedAt":2428,"seo":2429,"sitemap":2430,"stem":2433,"updatedAt":1844,"wordCount":2434,"__hash__":2435},"blogEn\u002Fen\u002Fblog\u002Fhow-to-add-special-characters-on-i18n.md","How to Add Special Characters in i18N Translations","\u002Fimages\u002Fblog\u002Fi18n\u002Fi18n-special-characters-on-nuxtjs-thumb.webp",{"type":13,"value":1853,"toc":2416},[1854,1860,1879,1883,1908,1931,1934,1941,1944,1950,1960,2027,2217,2221,2234,2237,2403,2405,2413],[16,1855,1856,1859],{},[20,1857,1858],{},"i18n"," is one of the most popular tools for website and application internationalization, and in some scenarios, it can be challenging to resolve specific issues when using i18n with your tool.",[16,1861,1862,1863,1866,1867,1870,1871,1874,1875,1878],{},"If you tried using characters like ",[20,1864,1865],{},"@"," or ",[20,1868,1869],{},"{"," in i18n translation files in ",[20,1872,1873],{},"Vue.js"," and encountered a ",[20,1876,1877],{},"500 error",", this article will help you understand why this happened and teach you how to fix the problem.",[25,1880,1882],{"id":1881},"the-problem-with-certain-characters-in-i18n","The Problem with Certain Characters in I18N",[16,1884,1885,1886,1889,1890,1893,1894,139,1897,1900,1901,139,1904,1907],{},"Just like ",[20,1887,1888],{},"React.JS",", which reserves certain ",[20,1891,1892],{},"HTML"," keywords such as ",[20,1895,1896],{},"class",[20,1898,1899],{},"for","—replacing them with ",[20,1902,1903],{},"className",[20,1905,1906],{},"htmlFor","—i18n also treats some characters in a special way:",[918,1909,1910,1914,1918,1922,1927],{},[921,1911,1912],{},[20,1913,1869],{},[921,1915,1916],{},[20,1917,343],{},[921,1919,1920],{},[20,1921,1865],{},[921,1923,1924],{},[20,1925,1926],{},"$",[921,1928,1929],{},[20,1930,1187],{},[16,1932,1933],{},"If we don't handle these characters correctly, you may face the same error I encountered:",[1935,1936],"img",{"alt":1937,"height":1938,"src":1939,"width":1940},"example of an error when adding special characters in i18n translation files",241,"\u002Fimages\u002Fblog\u002Fi18n\u002Fspecial-character-error.webp",680,[16,1942,1943],{},"To understand the issue, let's consider a project with the following structure:",[120,1945,1948],{"className":1946,"code":1947,"language":125},[123],".\n├── i18n \u003C-- Folder containing i18n configurations\n│   └── locales \u003C--- Folder containing i18n translation files\n│       ├── en.json \u003C--- I18n translation file for English\n│       └── pt-BR.json \u003C--- I18n translation file for Brazilian Portuguese\n",[20,1949,1947],{"__ignoreMap":126},[16,1951,1952,1953,1956,1957,1959],{},"Inside the ",[20,1954,1955],{},"pt-BR.json"," file, there are key-value pairs for the translated texts in Brazilian Portuguese. The problem started when I tried adding the ",[20,1958,1865],{}," character to one of the Footer links:",[40,1961,1962,1975],{},[43,1963,1964],{},[46,1965,1966,1969,1972],{},[49,1967,1968],{},"Special Character",[49,1970,1971],{},"Description",[49,1973,1974],{},"Solution",[56,1976,1977,1987,1997,2007,2017],{},[46,1978,1979,1981,1984],{},[61,1980,1865],{},[61,1982,1983],{},"Used in URLs, emails, etc.",[61,1985,1986],{},"{'@'}",[46,1988,1989,1991,1994],{},[61,1990,1869],{},[61,1992,1993],{},"Start of a block in JSON",[61,1995,1996],{},"{'{'}",[46,1998,1999,2001,2004],{},[61,2000,343],{},[61,2002,2003],{},"End of a block in JSON",[61,2005,2006],{},"{'}'}",[46,2008,2009,2011,2014],{},[61,2010,1926],{},[61,2012,2013],{},"Used in string interpolation",[61,2015,2016],{},"{'$'}",[46,2018,2019,2021,2024],{},[61,2020,1187],{},[61,2022,2023],{},"Pipe character",[61,2025,2026],{},"{'|'}",[120,2028,2032],{"className":2029,"code":2030,"language":2031,"meta":126,"style":126},"language-json shiki shiki-themes dracula","{\n  \"footer\": {\n    \"socialMedia\": [\n      {\n        \"url\": \"https:\u002F\u002Fwww.youtube.com\u002F@CodigoAoPonto\",\n        \"ariaLabel\": \"Go to Código ao Ponto's YouTube channel\",\n        \"icon\": \"mdi:youtube\"\n      },\n      {\n        \"url\": \"https:\u002F\u002Fwww.instagram.com\u002Fcodigo_ponto\",\n        \"ariaLabel\": \"Go to Código ao Ponto's Instagram\",\n        \"icon\": \"mdi:instagram\"\n      }\n    ]\n  }\n}\n","json",[20,2033,2034,2039,2055,2070,2075,2096,2116,2134,2139,2143,2162,2181,2198,2203,2208,2213],{"__ignoreMap":126},[209,2035,2036],{"class":211,"line":212},[209,2037,2038],{"class":215},"{\n",[209,2040,2041,2045,2048,2050,2052],{"class":211,"line":246},[209,2042,2044],{"class":2043},"sY8FZ","  \"",[209,2046,2047],{"class":684},"footer",[209,2049,234],{"class":2043},[209,2051,309],{"class":219},[209,2053,2054],{"class":215}," {\n",[209,2056,2057,2060,2063,2065,2067],{"class":211,"line":264},[209,2058,2059],{"class":2043},"    \"",[209,2061,2062],{"class":684},"socialMedia",[209,2064,234],{"class":2043},[209,2066,309],{"class":219},[209,2068,2069],{"class":215}," [\n",[209,2071,2072],{"class":211,"line":271},[209,2073,2074],{"class":215},"      {\n",[209,2076,2077,2080,2083,2085,2087,2089,2092,2094],{"class":211,"line":296},[209,2078,2079],{"class":2043},"        \"",[209,2081,2082],{"class":684},"url",[209,2084,234],{"class":2043},[209,2086,309],{"class":219},[209,2088,459],{"class":233},[209,2090,2091],{"class":237},"https:\u002F\u002Fwww.youtube.com\u002F@CodigoAoPonto",[209,2093,234],{"class":233},[209,2095,349],{"class":215},[209,2097,2098,2100,2103,2105,2107,2109,2112,2114],{"class":211,"line":301},[209,2099,2079],{"class":2043},[209,2101,2102],{"class":684},"ariaLabel",[209,2104,234],{"class":2043},[209,2106,309],{"class":219},[209,2108,459],{"class":233},[209,2110,2111],{"class":237},"Go to Código ao Ponto's YouTube channel",[209,2113,234],{"class":233},[209,2115,349],{"class":215},[209,2117,2118,2120,2123,2125,2127,2129,2132],{"class":211,"line":326},[209,2119,2079],{"class":2043},[209,2121,2122],{"class":684},"icon",[209,2124,234],{"class":2043},[209,2126,309],{"class":219},[209,2128,459],{"class":233},[209,2130,2131],{"class":237},"mdi:youtube",[209,2133,1602],{"class":233},[209,2135,2136],{"class":211,"line":352},[209,2137,2138],{"class":215},"      },\n",[209,2140,2141],{"class":211,"line":399},[209,2142,2074],{"class":215},[209,2144,2145,2147,2149,2151,2153,2155,2158,2160],{"class":211,"line":405},[209,2146,2079],{"class":2043},[209,2148,2082],{"class":684},[209,2150,234],{"class":2043},[209,2152,309],{"class":219},[209,2154,459],{"class":233},[209,2156,2157],{"class":237},"https:\u002F\u002Fwww.instagram.com\u002Fcodigo_ponto",[209,2159,234],{"class":233},[209,2161,349],{"class":215},[209,2163,2164,2166,2168,2170,2172,2174,2177,2179],{"class":211,"line":410},[209,2165,2079],{"class":2043},[209,2167,2102],{"class":684},[209,2169,234],{"class":2043},[209,2171,309],{"class":219},[209,2173,459],{"class":233},[209,2175,2176],{"class":237},"Go to Código ao Ponto's Instagram",[209,2178,234],{"class":233},[209,2180,349],{"class":215},[209,2182,2183,2185,2187,2189,2191,2193,2196],{"class":211,"line":425},[209,2184,2079],{"class":2043},[209,2186,2122],{"class":684},[209,2188,234],{"class":2043},[209,2190,309],{"class":219},[209,2192,459],{"class":233},[209,2194,2195],{"class":237},"mdi:instagram",[209,2197,1602],{"class":233},[209,2199,2200],{"class":211,"line":437},[209,2201,2202],{"class":215},"      }\n",[209,2204,2205],{"class":211,"line":451},[209,2206,2207],{"class":215},"    ]\n",[209,2209,2210],{"class":211,"line":469},[209,2211,2212],{"class":215},"  }\n",[209,2214,2215],{"class":211,"line":480},[209,2216,489],{"class":215},[25,2218,2220],{"id":2219},"the-solution","The Solution",[16,2222,2223,2224,2227,2228,2230,2231,2233],{},"To fix this issue, we need to use ",[20,2225,2226],{},"Literal Interpolation"," in i18n. To use the ",[20,2229,1865],{}," character in the i18n translation file, we should use the following syntax: ",[20,2232,1986],{},". This means wrapping the special character in curly braces and using single quotes.",[16,2235,2236],{},"Once applied, the corrected file will look like this:",[120,2238,2240],{"className":2029,"code":2239,"language":2031,"meta":126,"style":126},"{\n  \"footer\": {\n    \"socialMedia\": [\n      {\n        \"url\": \"https:\u002F\u002Fwww.youtube.com\u002F{'@'}CodigoAoPonto\",\n        \"ariaLabel\": \"Go to Código ao Ponto's YouTube channel\",\n        \"icon\": \"mdi:youtube\"\n      },\n      {\n        \"url\": \"https:\u002F\u002Fwww.instagram.com\u002Fcodigo_ponto\",\n        \"ariaLabel\": \"Go to Código ao Ponto's Instagram\",\n        \"icon\": \"mdi:instagram\"\n      }\n    ]\n  }\n}\n",[20,2241,2242,2246,2258,2270,2274,2293,2311,2327,2331,2335,2353,2371,2387,2391,2395,2399],{"__ignoreMap":126},[209,2243,2244],{"class":211,"line":212},[209,2245,2038],{"class":215},[209,2247,2248,2250,2252,2254,2256],{"class":211,"line":246},[209,2249,2044],{"class":2043},[209,2251,2047],{"class":684},[209,2253,234],{"class":2043},[209,2255,309],{"class":219},[209,2257,2054],{"class":215},[209,2259,2260,2262,2264,2266,2268],{"class":211,"line":264},[209,2261,2059],{"class":2043},[209,2263,2062],{"class":684},[209,2265,234],{"class":2043},[209,2267,309],{"class":219},[209,2269,2069],{"class":215},[209,2271,2272],{"class":211,"line":271},[209,2273,2074],{"class":215},[209,2275,2276,2278,2280,2282,2284,2286,2289,2291],{"class":211,"line":296},[209,2277,2079],{"class":2043},[209,2279,2082],{"class":684},[209,2281,234],{"class":2043},[209,2283,309],{"class":219},[209,2285,459],{"class":233},[209,2287,2288],{"class":237},"https:\u002F\u002Fwww.youtube.com\u002F{'@'}CodigoAoPonto",[209,2290,234],{"class":233},[209,2292,349],{"class":215},[209,2294,2295,2297,2299,2301,2303,2305,2307,2309],{"class":211,"line":301},[209,2296,2079],{"class":2043},[209,2298,2102],{"class":684},[209,2300,234],{"class":2043},[209,2302,309],{"class":219},[209,2304,459],{"class":233},[209,2306,2111],{"class":237},[209,2308,234],{"class":233},[209,2310,349],{"class":215},[209,2312,2313,2315,2317,2319,2321,2323,2325],{"class":211,"line":326},[209,2314,2079],{"class":2043},[209,2316,2122],{"class":684},[209,2318,234],{"class":2043},[209,2320,309],{"class":219},[209,2322,459],{"class":233},[209,2324,2131],{"class":237},[209,2326,1602],{"class":233},[209,2328,2329],{"class":211,"line":352},[209,2330,2138],{"class":215},[209,2332,2333],{"class":211,"line":399},[209,2334,2074],{"class":215},[209,2336,2337,2339,2341,2343,2345,2347,2349,2351],{"class":211,"line":405},[209,2338,2079],{"class":2043},[209,2340,2082],{"class":684},[209,2342,234],{"class":2043},[209,2344,309],{"class":219},[209,2346,459],{"class":233},[209,2348,2157],{"class":237},[209,2350,234],{"class":233},[209,2352,349],{"class":215},[209,2354,2355,2357,2359,2361,2363,2365,2367,2369],{"class":211,"line":410},[209,2356,2079],{"class":2043},[209,2358,2102],{"class":684},[209,2360,234],{"class":2043},[209,2362,309],{"class":219},[209,2364,459],{"class":233},[209,2366,2176],{"class":237},[209,2368,234],{"class":233},[209,2370,349],{"class":215},[209,2372,2373,2375,2377,2379,2381,2383,2385],{"class":211,"line":425},[209,2374,2079],{"class":2043},[209,2376,2122],{"class":684},[209,2378,234],{"class":2043},[209,2380,309],{"class":219},[209,2382,459],{"class":233},[209,2384,2195],{"class":237},[209,2386,1602],{"class":233},[209,2388,2389],{"class":211,"line":437},[209,2390,2202],{"class":215},[209,2392,2393],{"class":211,"line":451},[209,2394,2207],{"class":215},[209,2396,2397],{"class":211,"line":469},[209,2398,2212],{"class":215},[209,2400,2401],{"class":211,"line":480},[209,2402,489],{"class":215},[608,2404,1793],{"id":1792},[16,2406,2407,2408,702],{},"I hope this helps you overcome the challenge of using special characters in i18n with Vue.js. If you still have questions, you can check the ",[1798,2409,2412],{"href":2410,"rel":2411},"https:\u002F\u002Fvue-i18n.intlify.dev\u002Fguide\u002Fessentials\u002Fsyntax#literal-interpolation",[1802],"Official VueI18n Documentation",[1805,2414,2415],{},"html pre.shiki code .sCdxs, html code.shiki .sCdxs{--shiki-default:#F8F8F2}html pre.shiki code .sY8FZ, html code.shiki .sY8FZ{--shiki-default:#8BE9FE}html pre.shiki code .sLL85, html code.shiki .sLL85{--shiki-default:#8BE9FD}html pre.shiki code .s0Tla, html code.shiki .s0Tla{--shiki-default:#FF79C6}html pre.shiki code .seVfx, html code.shiki .seVfx{--shiki-default:#E9F284}html pre.shiki code .s-mGx, html code.shiki .s-mGx{--shiki-default:#F1FA8C}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);}",{"title":126,"searchDepth":246,"depth":246,"links":2417},[2418,2419],{"id":1881,"depth":246,"text":1882},{"id":2219,"depth":246,"text":2220,"children":2420},[2421],{"id":1792,"depth":264,"text":1793},[1834,1835],"Learn how to add special characters like @ and {} in i18n translations in Vue.js without causing errors, with a step-by-step solution and code examples.","how-to-add-special-characters-on-i18n",{},"\u002Fen\u002Fblog\u002Fhow-to-add-special-characters-on-i18n","como-adicionar-caracteres-especiais-no-i18n","2025-02-02",{"title":1850,"description":2423},{"loc":2426,"images":2431,"lastmod":1844},[2432],{"loc":1939},"en\u002Fblog\u002Fhow-to-add-special-characters-on-i18n",313,"-kLLM0rTpGWSafW4wyyRwc9AbKLTuQgP3fe48DD9SIw",{"id":2437,"title":2438,"articleCover":2439,"articleCoverEmbed":8,"articleCoverEmbedUploadDate":8,"articleCoverHeight":9,"articleCoverWidth":10,"authorId":11,"body":2440,"categoriesIds":3861,"description":3863,"enAlternateSlug":3864,"extension":1837,"meta":3865,"navigation":267,"path":3866,"ptBrAlternateSlug":3867,"publishedAt":3868,"seo":3869,"sitemap":3870,"stem":3874,"updatedAt":1844,"wordCount":3875,"__hash__":3876},"blogEn\u002Fen\u002Fblog\u002Fhow-to-create-structured-data-with-nuxtjs.md","What is Structured Data and How to Add it in Nuxt.JS","\u002Fimages\u002Fen\u002Fblog\u002Fseo\u002Fstructured-data\u002Fstructured-data-thumb-1.webp",{"type":13,"value":2441,"toc":3833},[2442,2449,2453,2456,2463,2467,2475,2482,2488,2492,2495,2505,2616,2620,2623,2634,2637,2641,2644,2658,2662,2674,2677,2686,2693,2717,2723,2738,2749,2808,2811,2815,2825,2869,2879,3163,3175,3179,3187,3309,3327,3331,3373,3377,3383,3528,3531,3537,3541,3551,3555,3558,3581,3584,3612,3616,3619,3638,3641,3644,3648,3651,3659,3663,3666,3681,3691,3694,3742,3745,3749,3761,3763,3767,3773,3777,3780,3784,3787,3791,3794,3798,3801,3803,3812,3830],[16,2443,2444,2445,2448],{},"Here you will see what Structured Data is, when to use it, when not to, and how to mark up your blog articles in Nuxt.JS with ",[20,2446,2447],{},"nuxt-schema-org",". After that come validating the markup and what to do so Google discovers and indexes your content and, when possible, recommends it in Discover.",[25,2450,2452],{"id":2451},"what-is-structured-data","What is Structured Data?",[16,2454,2455],{},"Structured data is information from a website that is organized in a standardized format such as JSON-LD, Microdata, or RDFa. Google recommends JSON-LD, which is kept separate from the visible HTML and is the easiest to maintain, and it is the format we use here.",[16,2457,2458,2459,2462],{},"Through structured data, search engines like Google receive more information about what your page is about. Based on this information, along with other factors, search engines can display ",[20,2460,2461],{},"Rich Results"," of your site's pages in search results.",[25,2464,2466],{"id":2465},"what-are-rich-snippets","What are Rich Snippets?",[16,2468,2469,1866,2472,2474],{},[20,2470,2471],{},"Rich Snippets",[20,2473,2461],{}," are visual enhancements in search results such as images, prices, breadcrumbs, review stars, etc.",[16,2476,2477,2478,2481],{},"Below, you can see an example of a Rich Result on Google from the ",[20,2479,2480],{},"Yahoo Finance"," website, where an image, title, and publication date are displayed. These visual elements increase the chances of attracting the user's attention and driving them to your site.",[1935,2483],{"alt":2484,"height":2485,"src":2486,"width":2487},"example of a rich search result on Google from Yahoo Finance",358,"\u002Fimages\u002Fblog\u002Fseo\u002Fstructured-data\u002Fyahoo-finances-schema-org-rich-result.webp",688,[25,2489,2491],{"id":2490},"example-of-structured-data-code","Example of Structured Data Code",[16,2493,2494],{},"Below you can see an example of structured data in JSON-LD. Don't worry, you won't need to write this manually, we'll use a Nuxt tool to generate the code.",[2496,2497,2498],"blockquote",{},[16,2499,2500,2501,2504],{},"This example uses ",[20,2502,2503],{},"FAQPage",", which was the most common rich result type for years. Google stopped showing the FAQ rich result on May 7, 2026, for every kind of site, and removed the feature's documentation in June. The markup is still valid in schema.org, but today it only describes the content and does not change how your result looks in search.",[120,2506,2510],{"className":2507,"code":2508,"language":2509,"meta":126,"style":126},"language-html shiki shiki-themes dracula","\u003Cscript type=\"application\u002Fld+json\">\n  {\n    \"@type\": \"FAQPage\",\n    \"@context\": \"https:\u002F\u002Fschema.org\",\n    \"@graph\": [\n      {\n        \"@id\": \"https:\u002F\u002Fcodigoaoponto.com\u002Ftools\u002Fcpf-generator\u002F#\u002Fschema\u002Fquestion\u002Fc6e1aab\",\n        \"@type\": \"Question\",\n        \"inLanguage\": \"pt-BR\",\n        \"name\": \"What is a CPF Generator?\",\n        \"acceptedAnswer\": {\n          \"@type\": \"Answer\",\n          \"text\": \"A CPF Generator is a tool that generates valid CPF numbers, useful for testing and system development. It applies the official CPF validation algorithm to ensure that the generated numbers are correctly formatted.\"\n        }\n      }\n    ]\n  }\n\u003C\u002Fscript>\n","html",[20,2511,2512,2532,2537,2542,2547,2552,2556,2561,2566,2571,2576,2581,2586,2591,2596,2600,2604,2608],{"__ignoreMap":126},[209,2513,2514,2516,2518,2521,2523,2525,2528,2530],{"class":211,"line":212},[209,2515,216],{"class":215},[209,2517,220],{"class":219},[209,2519,2520],{"class":223}," type",[209,2522,230],{"class":219},[209,2524,234],{"class":233},[209,2526,2527],{"class":237},"application\u002Fld+json",[209,2529,234],{"class":233},[209,2531,243],{"class":215},[209,2533,2534],{"class":211,"line":246},[209,2535,2536],{"class":215},"  {\n",[209,2538,2539],{"class":211,"line":264},[209,2540,2541],{"class":215},"    \"@type\": \"FAQPage\",\n",[209,2543,2544],{"class":211,"line":271},[209,2545,2546],{"class":215},"    \"@context\": \"https:\u002F\u002Fschema.org\",\n",[209,2548,2549],{"class":211,"line":296},[209,2550,2551],{"class":215},"    \"@graph\": [\n",[209,2553,2554],{"class":211,"line":301},[209,2555,2074],{"class":215},[209,2557,2558],{"class":211,"line":326},[209,2559,2560],{"class":215},"        \"@id\": \"https:\u002F\u002Fcodigoaoponto.com\u002Ftools\u002Fcpf-generator\u002F#\u002Fschema\u002Fquestion\u002Fc6e1aab\",\n",[209,2562,2563],{"class":211,"line":352},[209,2564,2565],{"class":215},"        \"@type\": \"Question\",\n",[209,2567,2568],{"class":211,"line":399},[209,2569,2570],{"class":215},"        \"inLanguage\": \"pt-BR\",\n",[209,2572,2573],{"class":211,"line":405},[209,2574,2575],{"class":215},"        \"name\": \"What is a CPF Generator?\",\n",[209,2577,2578],{"class":211,"line":410},[209,2579,2580],{"class":215},"        \"acceptedAnswer\": {\n",[209,2582,2583],{"class":211,"line":425},[209,2584,2585],{"class":215},"          \"@type\": \"Answer\",\n",[209,2587,2588],{"class":211,"line":437},[209,2589,2590],{"class":215},"          \"text\": \"A CPF Generator is a tool that generates valid CPF numbers, useful for testing and system development. It applies the official CPF validation algorithm to ensure that the generated numbers are correctly formatted.\"\n",[209,2592,2593],{"class":211,"line":451},[209,2594,2595],{"class":215},"        }\n",[209,2597,2598],{"class":211,"line":469},[209,2599,2202],{"class":215},[209,2601,2602],{"class":211,"line":480},[209,2603,2207],{"class":215},[209,2605,2606],{"class":211,"line":486},[209,2607,2212],{"class":215},[209,2609,2610,2612,2614],{"class":211,"line":492},[209,2611,495],{"class":215},[209,2613,220],{"class":219},[209,2615,243],{"class":215},[25,2617,2619],{"id":2618},"when-to-use-structured-data","When to Use Structured Data?",[16,2621,2622],{},"Use Structured Data when:",[918,2624,2625,2628,2631],{},[921,2626,2627],{},"The page content fits into one of the supported categories, such as articles, products, recipes, events, job postings, etc.",[921,2629,2630],{},"You want to increase your page visibility and click-through rate (CTR).",[921,2632,2633],{},"You want to help search engines better understand your website's content.",[16,2635,2636],{},"In any case, what you mark up has to be visible to the user on the page, and the properties have to be kept up to date.",[25,2638,2640],{"id":2639},"when-not-to-use-structured-data","When Not to Use Structured Data?",[16,2642,2643],{},"Avoid using Structured Data when:",[918,2645,2646,2649,2652,2655],{},[921,2647,2648],{},"The page content does not fit into the supported categories.",[921,2650,2651],{},"The content is of low quality; structured data won't improve it.",[921,2653,2654],{},"The content changes frequently, making it costly to update this information regularly.",[921,2656,2657],{},"The marked-up content does not appear on the page. Google treats that as misuse of structured data and can apply a manual action to your site.",[25,2659,2661],{"id":2660},"installing-the-schemaorg-package-in-nuxt","Installing the Schema.org Package in Nuxt",[16,2663,2664,2669,2670,2673],{},[1798,2665,2668],{"href":2666,"rel":2667},"https:\u002F\u002Fnuxtseo.com",[1802],"Nuxt SEO"," is a collection of SEO modules that make it easier to develop ",[20,2671,2672],{},"technical SEO"," for your website. In this tutorial, we will use the module that simplifies the implementation of structured data.",[16,2675,2676],{},"You can install this module in several ways; here, I'll show the two main ones:",[2678,2679,2680,2683],"ol",{},[921,2681,2682],{},"nuxi (Nuxt's official command-line interface)",[921,2684,2685],{},"npm (Package manager)",[16,2687,2688,2689,2692],{},"If you are using ",[20,2690,2691],{},"nuxi",", simply run the following command:",[120,2694,2698],{"className":2695,"code":2696,"language":2697,"meta":126,"style":126},"language-bash shiki shiki-themes dracula","npx nuxi module add schema-org\n","bash",[20,2699,2700],{"__ignoreMap":126},[209,2701,2702,2705,2708,2711,2714],{"class":211,"line":212},[209,2703,2704],{"class":257},"npx",[209,2706,2707],{"class":237}," nuxi",[209,2709,2710],{"class":237}," module",[209,2712,2713],{"class":237}," add",[209,2715,2716],{"class":237}," schema-org\n",[16,2718,2688,2719,2722],{},[20,2720,2721],{},"npm",", you'll need an extra step. Start by installing the package:",[120,2724,2726],{"className":2695,"code":2725,"language":2697,"meta":126,"style":126},"npm i nuxt-schema-org\n",[20,2727,2728],{"__ignoreMap":126},[209,2729,2730,2732,2735],{"class":211,"line":212},[209,2731,2721],{"class":257},[209,2733,2734],{"class":237}," i",[209,2736,2737],{"class":237}," nuxt-schema-org\n",[16,2739,2740,2741,2744,2745,2748],{},"Then add the installed package to the ",[20,2742,2743],{},"modules"," option in the ",[20,2746,2747],{},"nuxt.config.ts"," file:",[120,2750,2754],{"className":2751,"code":2752,"language":2753,"meta":126,"style":126},"language-js shiki shiki-themes dracula","export default defineNuxtConfig({\n  \u002F\u002F other configurations...\n  modules: [\n    \u002F\u002F other modules\n    \"nuxt-schema-org\"\n  ]\n  \u002F\u002F other configurations...\n});\n","js",[20,2755,2756,2768,2773,2782,2787,2795,2800,2804],{"__ignoreMap":126},[209,2757,2758,2760,2763,2766],{"class":211,"line":212},[209,2759,1145],{"class":219},[209,2761,2762],{"class":219}," default",[209,2764,2765],{"class":257}," defineNuxtConfig",[209,2767,434],{"class":215},[209,2769,2770],{"class":211,"line":246},[209,2771,2772],{"class":1139},"  \u002F\u002F other configurations...\n",[209,2774,2775,2778,2780],{"class":211,"line":264},[209,2776,2777],{"class":215},"  modules",[209,2779,309],{"class":219},[209,2781,2069],{"class":215},[209,2783,2784],{"class":211,"line":271},[209,2785,2786],{"class":1139},"    \u002F\u002F other modules\n",[209,2788,2789,2791,2793],{"class":211,"line":296},[209,2790,2059],{"class":233},[209,2792,2447],{"class":237},[209,2794,1602],{"class":233},[209,2796,2797],{"class":211,"line":301},[209,2798,2799],{"class":215},"  ]\n",[209,2801,2802],{"class":211,"line":326},[209,2803,2772],{"class":1139},[209,2805,2806],{"class":211,"line":352},[209,2807,718],{"class":215},[16,2809,2810],{},"Once this is done, we can start using the tool.",[25,2812,2814],{"id":2813},"structured-data-for-articles-in-nuxtjs","Structured Data for Articles in Nuxt.JS",[16,2816,2817,2818,2821,2822,2824],{},"For a blog, the most useful type is ",[20,2819,2820],{},"Article",". It tells Google that the page is an article, who wrote it, when it was published and updated, and which image represents it. Google does not require any property for ",[20,2823,2820],{},", but it recommends these:",[918,2826,2827,2833,2839,2848,2863],{},[921,2828,2829,2832],{},[20,2830,2831],{},"headline",": the article title.",[921,2834,2835,2838],{},[20,2836,2837],{},"image",": a representative image. Google recommends several high-resolution images in 16x9, 4x3 and 1x1 aspect ratios.",[921,2840,2841,139,2844,2847],{},[20,2842,2843],{},"datePublished",[20,2845,2846],{},"dateModified",": the date of first publication and of the last modification, in ISO 8601 format.",[921,2849,2850,2853,2854,1776,2857,139,2860,2862],{},[20,2851,2852],{},"author",": preferably a ",[20,2855,2856],{},"Person",[20,2858,2859],{},"name",[20,2861,2082],{},", pointing to a page that identifies the author.",[921,2864,2865,2868],{},[20,2866,2867],{},"publisher",": the organization behind the site, with a name and logo.",[16,2870,2871,2872,2875,2876,2878],{},"In Nuxt, the ",[20,2873,2874],{},"useSchemaOrg"," composable is globally available, and you can use it inside the ",[20,2877,220],{}," tag of the page. In a blog page that loads the post with Nuxt Content, it looks like this:",[120,2880,2882],{"className":203,"code":2881,"language":205,"meta":126,"style":126},"\u003Cscript setup lang=\"ts\">\nconst route = useRoute();\n\nconst { data: post } = await useAsyncData(`post-${route.path}`, () =>\n  queryCollection(\"blog\").path(route.path).first()\n);\n\nuseSchemaOrg([\n  defineArticle({\n    headline: post.value?.title,\n    description: post.value?.description,\n    image: post.value?.articleCover,\n    datePublished: post.value?.publishedAt,\n    dateModified: post.value?.updatedAt ?? post.value?.publishedAt\n  }),\n  defineBreadcrumb({\n    itemListElement: [\n      { name: \"Home\", item: \"\u002F\" },\n      { name: \"Blog\", item: \"\u002Fblog\" },\n      { name: post.value?.title }\n    ]\n  })\n]);\n\u003C\u002Fscript>\n",[20,2883,2884,2904,2916,2920,2956,2980,2984,2988,2995,3002,3012,3022,3032,3042,3057,3062,3069,3078,3106,3132,3141,3145,3150,3155],{"__ignoreMap":126},[209,2885,2886,2888,2890,2892,2894,2896,2898,2900,2902],{"class":211,"line":212},[209,2887,216],{"class":215},[209,2889,220],{"class":219},[209,2891,224],{"class":223},[209,2893,227],{"class":223},[209,2895,230],{"class":219},[209,2897,234],{"class":233},[209,2899,238],{"class":237},[209,2901,234],{"class":233},[209,2903,243],{"class":215},[209,2905,2906,2908,2910,2912,2914],{"class":211,"line":246},[209,2907,249],{"class":219},[209,2909,252],{"class":215},[209,2911,230],{"class":219},[209,2913,258],{"class":257},[209,2915,261],{"class":215},[209,2917,2918],{"class":211,"line":264},[209,2919,268],{"emptyLinePlaceholder":267},[209,2921,2922,2924,2926,2928,2930,2932,2934,2936,2938,2941,2943,2946,2948,2950,2953],{"class":211,"line":271},[209,2923,249],{"class":219},[209,2925,306],{"class":215},[209,2927,309],{"class":219},[209,2929,312],{"class":215},[209,2931,230],{"class":219},[209,2933,317],{"class":219},[209,2935,320],{"class":257},[209,2937,362],{"class":215},[209,2939,2940],{"class":237},"`post-",[209,2942,337],{"class":219},[209,2944,2945],{"class":215},"route.path",[209,2947,343],{"class":219},[209,2949,346],{"class":237},[209,2951,2952],{"class":215},", () ",[209,2954,2955],{"class":219},"=>\n",[209,2957,2958,2961,2963,2965,2967,2969,2971,2973,2976,2978],{"class":211,"line":296},[209,2959,2960],{"class":257},"  queryCollection",[209,2962,362],{"class":215},[209,2964,234],{"class":233},[209,2966,367],{"class":237},[209,2968,234],{"class":233},[209,2970,372],{"class":215},[209,2972,375],{"class":257},[209,2974,2975],{"class":215},"(route.path).",[209,2977,393],{"class":257},[209,2979,396],{"class":215},[209,2981,2982],{"class":211,"line":301},[209,2983,402],{"class":215},[209,2985,2986],{"class":211,"line":326},[209,2987,268],{"emptyLinePlaceholder":267},[209,2989,2990,2992],{"class":211,"line":352},[209,2991,2874],{"class":257},[209,2993,2994],{"class":215},"([\n",[209,2996,2997,3000],{"class":211,"line":399},[209,2998,2999],{"class":257},"  defineArticle",[209,3001,434],{"class":215},[209,3003,3004,3007,3009],{"class":211,"line":405},[209,3005,3006],{"class":215},"    headline",[209,3008,309],{"class":219},[209,3010,3011],{"class":215}," post.value?.title,\n",[209,3013,3014,3017,3019],{"class":211,"line":410},[209,3015,3016],{"class":215},"    description",[209,3018,309],{"class":219},[209,3020,3021],{"class":215}," post.value?.description,\n",[209,3023,3024,3027,3029],{"class":211,"line":425},[209,3025,3026],{"class":215},"    image",[209,3028,309],{"class":219},[209,3030,3031],{"class":215}," post.value?.articleCover,\n",[209,3033,3034,3037,3039],{"class":211,"line":437},[209,3035,3036],{"class":215},"    datePublished",[209,3038,309],{"class":219},[209,3040,3041],{"class":215}," post.value?.publishedAt,\n",[209,3043,3044,3047,3049,3052,3054],{"class":211,"line":451},[209,3045,3046],{"class":215},"    dateModified",[209,3048,309],{"class":219},[209,3050,3051],{"class":215}," post.value?.updatedAt ",[209,3053,1241],{"class":219},[209,3055,3056],{"class":215}," post.value?.publishedAt\n",[209,3058,3059],{"class":211,"line":469},[209,3060,3061],{"class":215},"  }),\n",[209,3063,3064,3067],{"class":211,"line":480},[209,3065,3066],{"class":257},"  defineBreadcrumb",[209,3068,434],{"class":215},[209,3070,3071,3074,3076],{"class":211,"line":486},[209,3072,3073],{"class":215},"    itemListElement",[209,3075,309],{"class":219},[209,3077,2069],{"class":215},[209,3079,3080,3083,3085,3087,3090,3092,3095,3097,3099,3101,3103],{"class":211,"line":492},[209,3081,3082],{"class":215},"      { name",[209,3084,309],{"class":219},[209,3086,459],{"class":233},[209,3088,3089],{"class":237},"Home",[209,3091,234],{"class":233},[209,3093,3094],{"class":215},", item",[209,3096,309],{"class":219},[209,3098,459],{"class":233},[209,3100,66],{"class":237},[209,3102,234],{"class":233},[209,3104,3105],{"class":215}," },\n",[209,3107,3108,3110,3112,3114,3117,3119,3121,3123,3125,3128,3130],{"class":211,"line":502},[209,3109,3082],{"class":215},[209,3111,309],{"class":219},[209,3113,459],{"class":233},[209,3115,3116],{"class":237},"Blog",[209,3118,234],{"class":233},[209,3120,3094],{"class":215},[209,3122,309],{"class":219},[209,3124,459],{"class":233},[209,3126,3127],{"class":237},"\u002Fblog",[209,3129,234],{"class":233},[209,3131,3105],{"class":215},[209,3133,3134,3136,3138],{"class":211,"line":507},[209,3135,3082],{"class":215},[209,3137,309],{"class":219},[209,3139,3140],{"class":215}," post.value?.title }\n",[209,3142,3143],{"class":211,"line":517},[209,3144,2207],{"class":215},[209,3146,3147],{"class":211,"line":540},[209,3148,3149],{"class":215},"  })\n",[209,3151,3152],{"class":211,"line":556},[209,3153,3154],{"class":215},"]);\n",[209,3156,3157,3159,3161],{"class":211,"line":566},[209,3158,495],{"class":215},[209,3160,220],{"class":219},[209,3162,243],{"class":215},[16,3164,3165,3166,3168,3169,3172,3173,702],{},"The fields come from the article's front matter, so ",[20,3167,2846],{}," only reflects reality if you update ",[20,3170,3171],{},"updatedAt"," when the content actually changes. The module converts the dates to ISO 8601 and turns the image path into an absolute URL using the site ",[20,3174,2082],{},[608,3176,3178],{"id":3177},"global-site-configuration","Global Site Configuration",[16,3180,3181,3182,3184,3185,309],{},"Author and publisher do not need to be repeated on every page. ",[20,3183,2447],{}," uses the site configuration and the organization identity, which you define once in ",[20,3186,2747],{},[120,3188,3190],{"className":1131,"code":3189,"language":238,"meta":126,"style":126},"export default defineNuxtConfig({\n  site: {\n    url: \"https:\u002F\u002Fyoursite.com\",\n    name: \"Site Name\"\n  },\n  schemaOrg: {\n    identity: defineOrganization({\n      name: \"Site Name\",\n      logo: \"https:\u002F\u002Fyoursite.com\u002Flogo-512.png\"\n    })\n  }\n});\n",[20,3191,3192,3202,3211,3227,3241,3246,3255,3267,3282,3296,3301,3305],{"__ignoreMap":126},[209,3193,3194,3196,3198,3200],{"class":211,"line":212},[209,3195,1145],{"class":219},[209,3197,2762],{"class":219},[209,3199,2765],{"class":257},[209,3201,434],{"class":215},[209,3203,3204,3207,3209],{"class":211,"line":246},[209,3205,3206],{"class":215},"  site",[209,3208,309],{"class":219},[209,3210,2054],{"class":215},[209,3212,3213,3216,3218,3220,3223,3225],{"class":211,"line":264},[209,3214,3215],{"class":215},"    url",[209,3217,309],{"class":219},[209,3219,459],{"class":233},[209,3221,3222],{"class":237},"https:\u002F\u002Fyoursite.com",[209,3224,234],{"class":233},[209,3226,349],{"class":215},[209,3228,3229,3232,3234,3236,3239],{"class":211,"line":271},[209,3230,3231],{"class":215},"    name",[209,3233,309],{"class":219},[209,3235,459],{"class":233},[209,3237,3238],{"class":237},"Site Name",[209,3240,1602],{"class":233},[209,3242,3243],{"class":211,"line":296},[209,3244,3245],{"class":215},"  },\n",[209,3247,3248,3251,3253],{"class":211,"line":301},[209,3249,3250],{"class":215},"  schemaOrg",[209,3252,309],{"class":219},[209,3254,2054],{"class":215},[209,3256,3257,3260,3262,3265],{"class":211,"line":326},[209,3258,3259],{"class":215},"    identity",[209,3261,309],{"class":219},[209,3263,3264],{"class":257}," defineOrganization",[209,3266,434],{"class":215},[209,3268,3269,3272,3274,3276,3278,3280],{"class":211,"line":352},[209,3270,3271],{"class":215},"      name",[209,3273,309],{"class":219},[209,3275,459],{"class":233},[209,3277,3238],{"class":237},[209,3279,234],{"class":233},[209,3281,349],{"class":215},[209,3283,3284,3287,3289,3291,3294],{"class":211,"line":399},[209,3285,3286],{"class":215},"      logo",[209,3288,309],{"class":219},[209,3290,459],{"class":233},[209,3292,3293],{"class":237},"https:\u002F\u002Fyoursite.com\u002Flogo-512.png",[209,3295,1602],{"class":233},[209,3297,3298],{"class":211,"line":405},[209,3299,3300],{"class":215},"    })\n",[209,3302,3303],{"class":211,"line":410},[209,3304,2212],{"class":215},[209,3306,3307],{"class":211,"line":425},[209,3308,718],{"class":215},[16,3310,3311,3312,3314,3315,3317,3318,1776,3320,139,3322,587,3324,702],{},"If the ",[20,3313,2820],{}," has no ",[20,3316,2867],{},", the module uses this identity. For the author, pass a ",[20,3319,2856],{},[20,3321,2859],{},[20,3323,2082],{},[20,3325,3326],{},"defineArticle",[608,3328,3330],{"id":3329},"how-the-entities-connect","How the Entities Connect",[16,3332,3333,3334,3337,3338,3341,3342,3341,3345,3341,3347,139,3349,3352,3353,3356,3357,3359,3360,3362,3363,139,3365,3367,3368,139,3370,3372],{},"The module builds a single ",[20,3335,3336],{},"@graph"," in which ",[20,3339,3340],{},"WebSite",", ",[20,3343,3344],{},"WebPage",[20,3346,2820],{},[20,3348,2856],{},[20,3350,3351],{},"Organization"," reference each other by ",[20,3354,3355],{},"@id",". The ",[20,3358,2820],{}," points to the current ",[20,3361,3344],{},", and ",[20,3364,2852],{},[20,3366,2867],{}," point to the ",[20,3369,2856],{},[20,3371,3351],{}," nodes. That link tells Google the article belongs to that author and that brand.",[608,3374,3376],{"id":3375},"what-about-faqpage","What About FAQPage?",[16,3378,3379,3380,3382],{},"If the page has a visible question and answer section, marking it up with ",[20,3381,2503],{}," is still worth it. Just don't expect the dropdown in search. The example below generates the same JSON-LD we saw earlier:",[120,3384,3386],{"className":203,"code":3385,"language":205,"meta":126,"style":126},"\u003Cscript setup>\nuseSchemaOrg([\n  defineWebPage({\n    \"@type\": \"FAQPage\"\n  }),\n  defineQuestion({\n    name: \"What is a CPF Generator?\",\n    acceptedAnswer:\n      \"A CPF Generator is a tool that generates valid CPF numbers, useful for testing and system development. It applies the official CPF validation algorithm to ensure that the generated numbers are correctly formatted.\"\n  }),\n  defineQuestion({\n    name: \"How does the CPF Generator work?\",\n    acceptedAnswer:\n      \"The CPF Generator randomly creates the first nine digits of the CPF and then calculates the two check digits using the official validation algorithm, ensuring the generated number is valid.\"\n  })\n]);\n\u003C\u002Fscript>\n",[20,3387,3388,3398,3404,3411,3428,3432,3439,3454,3462,3472,3476,3482,3497,3503,3512,3516,3520],{"__ignoreMap":126},[209,3389,3390,3392,3394,3396],{"class":211,"line":212},[209,3391,216],{"class":215},[209,3393,220],{"class":219},[209,3395,224],{"class":223},[209,3397,243],{"class":215},[209,3399,3400,3402],{"class":211,"line":246},[209,3401,2874],{"class":257},[209,3403,2994],{"class":215},[209,3405,3406,3409],{"class":211,"line":264},[209,3407,3408],{"class":257},"  defineWebPage",[209,3410,434],{"class":215},[209,3412,3413,3415,3418,3420,3422,3424,3426],{"class":211,"line":271},[209,3414,2059],{"class":233},[209,3416,3417],{"class":237},"@type",[209,3419,234],{"class":233},[209,3421,309],{"class":219},[209,3423,459],{"class":233},[209,3425,2503],{"class":237},[209,3427,1602],{"class":233},[209,3429,3430],{"class":211,"line":296},[209,3431,3061],{"class":215},[209,3433,3434,3437],{"class":211,"line":301},[209,3435,3436],{"class":257},"  defineQuestion",[209,3438,434],{"class":215},[209,3440,3441,3443,3445,3447,3450,3452],{"class":211,"line":326},[209,3442,3231],{"class":215},[209,3444,309],{"class":219},[209,3446,459],{"class":233},[209,3448,3449],{"class":237},"What is a CPF Generator?",[209,3451,234],{"class":233},[209,3453,349],{"class":215},[209,3455,3456,3459],{"class":211,"line":352},[209,3457,3458],{"class":215},"    acceptedAnswer",[209,3460,3461],{"class":219},":\n",[209,3463,3464,3467,3470],{"class":211,"line":399},[209,3465,3466],{"class":233},"      \"",[209,3468,3469],{"class":237},"A CPF Generator is a tool that generates valid CPF numbers, useful for testing and system development. It applies the official CPF validation algorithm to ensure that the generated numbers are correctly formatted.",[209,3471,1602],{"class":233},[209,3473,3474],{"class":211,"line":405},[209,3475,3061],{"class":215},[209,3477,3478,3480],{"class":211,"line":410},[209,3479,3436],{"class":257},[209,3481,434],{"class":215},[209,3483,3484,3486,3488,3490,3493,3495],{"class":211,"line":425},[209,3485,3231],{"class":215},[209,3487,309],{"class":219},[209,3489,459],{"class":233},[209,3491,3492],{"class":237},"How does the CPF Generator work?",[209,3494,234],{"class":233},[209,3496,349],{"class":215},[209,3498,3499,3501],{"class":211,"line":437},[209,3500,3458],{"class":215},[209,3502,3461],{"class":219},[209,3504,3505,3507,3510],{"class":211,"line":451},[209,3506,3466],{"class":233},[209,3508,3509],{"class":237},"The CPF Generator randomly creates the first nine digits of the CPF and then calculates the two check digits using the official validation algorithm, ensuring the generated number is valid.",[209,3511,1602],{"class":233},[209,3513,3514],{"class":211,"line":469},[209,3515,3149],{"class":215},[209,3517,3518],{"class":211,"line":480},[209,3519,3154],{"class":215},[209,3521,3522,3524,3526],{"class":211,"line":486},[209,3523,495],{"class":215},[209,3525,220],{"class":219},[209,3527,243],{"class":215},[16,3529,3530],{},"Until May 2026, this markup could produce a rich result like the one in the image below:",[1935,3532],{"alt":3533,"height":3534,"src":3535,"width":3536},"example of a rich FAQ result on Google for the question about what Nuxt.JS is, shown before Google ended the feature",351,"\u002Fimages\u002Fblog\u002Fseo\u002Fstructured-data\u002Ffaq-rich-result-example-1.webp",681,[25,3538,3540],{"id":3539},"how-to-check-that-the-json-ld-is-in-the-html","How to Check That the JSON-LD Is in the HTML",[16,3542,3543,3544,3547,3548,3550],{},"Open the page in the browser and use \"View page source\", not \"Inspect element\". Look for a ",[20,3545,3546],{},"\u003Cscript type=\"application\u002Fld+json\">"," block. ",[20,3549,2447],{}," injects the JSON-LD during server rendering, so it has to be in the raw HTML. If it only shows up after hydration, review the rendering configuration of the page.",[25,3552,3554],{"id":3553},"how-to-validate-structured-data","How to Validate Structured Data",[16,3556,3557],{},"After publishing, validate the page with these tools:",[2678,3559,3560,3569,3578],{},[921,3561,3562,3563,3568],{},"Google's ",[1798,3564,3567],{"href":3565,"rel":3566},"https:\u002F\u002Fsearch.google.com\u002Ftest\u002Frich-results",[1802],"Rich Results Test",": shows the items detected on the URL and lists errors and warnings.",[921,3570,3571,3572,3577],{},"The ",[1798,3573,3576],{"href":3574,"rel":3575},"https:\u002F\u002Fvalidator.schema.org",[1802],"Schema Markup Validator",": checks the JSON-LD syntax against schema.org, regardless of whether Google shows a rich result.",[921,3579,3580],{},"The structured data report in Search Console: groups the errors and warnings of the whole site.",[16,3582,3583],{},"The most common problems on blogs are these:",[918,3585,3586,3597,3600,3609],{},[921,3587,3588,3589,1866,3591,3593,3594,3596],{},"A missing recommended property, such as ",[20,3590,2837],{},[20,3592,2852],{},". It is not a critical error in ",[20,3595,2820],{},", but it limits what Google can use.",[921,3598,3599],{},"A small or inaccessible image. For Discover, the image needs to be at least 1200 px wide and the URL cannot be blocked for the crawler.",[921,3601,3602,3603,1866,3606,702],{},"An invalid date format. Use ISO 8601, like ",[20,3604,3605],{},"2026-10-03",[20,3607,3608],{},"2026-10-03T14:30:00-03:00",[921,3610,3611],{},"Markup that differs from the visible content. The title, author, date and image in the schema must match what the user sees on the page.",[25,3613,3615],{"id":3614},"how-to-get-google-to-index-your-article","How to Get Google to Index Your Article",[16,3617,3618],{},"Correct structured data does not help if Google does not index the page. The flow for a new article is this:",[2678,3620,3621,3632,3635],{},[921,3622,3623,3624,3627,3628,3631],{},"Confirm the URL is in ",[20,3625,3626],{},"sitemap.xml"," and that the sitemap is submitted in Search Console. In Nuxt, the ",[20,3629,3630],{},"@nuxtjs\u002Fsitemap"," module generates this file.",[921,3633,3634],{},"Paste the URL in the \"Inspect any URL\" field of Search Console.",[921,3636,3637],{},"Click \"Request indexing\".",[16,3639,3640],{},"The indexing request puts the URL in a crawl queue, but it does not guarantee indexing, and Google does not give a timeframe. Google also does not publish the daily request limit. When you reach it, the button becomes unavailable until the next day, so use it only for important URLs and do not resubmit the same page several times.",[16,3642,3643],{},"For the other pages, what helps Google find them is the sitemap and internal links. A page with no links from other pages on the site tends to be discovered more slowly. Link to new articles from the blog listing and from related posts.",[608,3645,3647],{"id":3646},"discovered-currently-not-indexed","Discovered, Currently Not Indexed",[16,3649,3650],{},"In the page indexing report, two statuses confuse many people:",[918,3652,3653,3656],{},[921,3654,3655],{},"Discovered, currently not indexed: Google knows the URL but has not crawled it yet. Strengthen internal links, check that the URL is in the sitemap and verify that the server is not returning 5xx errors or responding slowly.",[921,3657,3658],{},"Crawled, currently not indexed: Google already read the page and decided not to index it for now. It is usually a quality issue: content too similar to something already indexed, shallow, or off the search intent. Improve the content and only then request indexing again.",[25,3660,3662],{"id":3661},"how-to-appear-in-google-discover","How to Appear in Google Discover",[16,3664,3665],{},"Discover is the content feed in the Google app and on mobile browsers. It shows content based on each user's interests, without depending on a search. According to Google's documentation, to be eligible the content needs to:",[918,3667,3668,3671,3678],{},[921,3669,3670],{},"Be indexed and follow Discover's content policies, with no clickbait or sensationalism.",[921,3672,3673,3674,3677],{},"Have an image at least 1200 px wide and more than 300,000 pixels in total, in a 16x9 aspect ratio, with the ",[20,3675,3676],{},"max-image-preview:large"," robots meta enabled.",[921,3679,3680],{},"Be original, helpful and made for people.",[16,3682,3683,3684,3686,3687,3690],{},"No structured data is required for Discover. But Google uses the schema.org ",[20,3685,2837],{}," and the ",[20,3688,3689],{},"og:image"," meta tag to choose the card image, so keep both pointing to a good image and do not use the site logo instead.",[16,3692,3693],{},"In Nuxt, the robots meta can be set on the page itself:",[120,3695,3697],{"className":203,"code":3696,"language":205,"meta":126,"style":126},"\u003Cscript setup>\nuseSeoMeta({\n  robots: \"index, follow, max-image-preview:large\"\n});\n\u003C\u002Fscript>\n",[20,3698,3699,3709,3716,3730,3734],{"__ignoreMap":126},[209,3700,3701,3703,3705,3707],{"class":211,"line":212},[209,3702,216],{"class":215},[209,3704,220],{"class":219},[209,3706,224],{"class":223},[209,3708,243],{"class":215},[209,3710,3711,3714],{"class":211,"line":246},[209,3712,3713],{"class":257},"useSeoMeta",[209,3715,434],{"class":215},[209,3717,3718,3721,3723,3725,3728],{"class":211,"line":264},[209,3719,3720],{"class":215},"  robots",[209,3722,309],{"class":219},[209,3724,459],{"class":233},[209,3726,3727],{"class":237},"index, follow, max-image-preview:large",[209,3729,1602],{"class":233},[209,3731,3732],{"class":211,"line":271},[209,3733,718],{"class":215},[209,3735,3736,3738,3740],{"class":211,"line":296},[209,3737,495],{"class":215},[209,3739,220],{"class":219},[209,3741,243],{"class":215},[16,3743,3744],{},"Discover performance shows up in its own report in Search Console. If your site has no data there, the content is not being shown in the feed yet.",[25,3746,3748],{"id":3747},"notifying-bing-with-indexnow","Notifying Bing with IndexNow",[16,3750,3751,3752,3755,3756,702],{},"IndexNow is a protocol adopted by Bing and other search engines to signal when a URL is created, updated or removed. You generate a key, publish a ",[20,3753,3754],{},".txt"," file with it at the root of the domain and make a request to the IndexNow endpoint whenever you publish or update an article. This tells the search engine something new exists, but it does not guarantee immediate indexing. The official guide is at ",[1798,3757,3760],{"href":3758,"rel":3759},"https:\u002F\u002Fwww.bing.com\u002Findexnow\u002Fgetstarted",[1802],"bing.com\u002Findexnow\u002Fgetstarted",[25,3762,1703],{"id":1702},[608,3764,3766],{"id":3765},"do-i-need-structured-data-for-my-blog-to-rank","Do I need structured data for my blog to rank?",[16,3768,3769,3770,3772],{},"No. It is not required to rank, but it helps Google understand your articles better. For a blog, the starting point is ",[20,3771,2820],{}," with an author and an image, plus the breadcrumb.",[608,3774,3776],{"id":3775},"is-it-worth-keeping-faqpage-if-it-no-longer-produces-a-rich-result","Is it worth keeping FAQPage if it no longer produces a rich result?",[16,3778,3779],{},"It is, if the page has a question and answer section that is visible to the user. In that case the markup serves to describe the content.",[608,3781,3783],{"id":3782},"does-structured-data-guarantee-my-post-shows-up-in-discover","Does structured data guarantee my post shows up in Discover?",[16,3785,3786],{},"No. Discover does not require structured data. What matters is that the content is indexed, has a large image and follows the content policies.",[608,3788,3790],{"id":3789},"my-article-shows-as-crawled-currently-not-indexed-what-should-i-do","My article shows as \"Crawled, currently not indexed\". What should I do?",[16,3792,3793],{},"That status usually points to the quality or originality of the page. Improve the content, strengthen internal links and request indexing again afterward.",[608,3795,3797],{"id":3796},"how-do-i-know-google-is-reading-my-json-ld","How do I know Google is reading my JSON-LD?",[16,3799,3800],{},"Check the raw HTML in \"View page source\", test the URL in the Rich Results Test and follow the structured data report in Search Console.",[25,3802,1793],{"id":1792},[16,3804,3805,3806,3808,3809,3811],{},"For each new article, mark up ",[20,3807,2820],{}," with author, image and dates, check the JSON-LD in the source, validate it in the Rich Results Test and follow the indexing report in Search Console. Update ",[20,3810,3171],{}," only when the content really changes and, if Bing matters to you, set up IndexNow.",[16,3813,3814,3815,3341,3820,139,3825,702],{},"To go deeper, read the ",[1798,3816,3819],{"href":3817,"rel":3818},"https:\u002F\u002Fnuxtseo.com\u002Fdocs\u002Fschema-org\u002Fgetting-started\u002Fintroduction",[1802],"complete documentation of the module",[1798,3821,3824],{"href":3822,"rel":3823},"https:\u002F\u002Fdevelopers.google.com\u002Fsearch\u002Fdocs\u002Fappearance\u002Fstructured-data\u002Farticle",[1802],"Google's documentation on Article structured data",[1798,3826,3829],{"href":3827,"rel":3828},"https:\u002F\u002Fdevelopers.google.com\u002Fsearch\u002Fdocs\u002Fappearance\u002Fgoogle-discover",[1802],"Google's Discover documentation",[1805,3831,3832],{},"html pre.shiki code .sCdxs, html code.shiki .sCdxs{--shiki-default:#F8F8F2}html pre.shiki code .s0Tla, html code.shiki .s0Tla{--shiki-default:#FF79C6}html pre.shiki code .sY_PY, html code.shiki .sY_PY{--shiki-default:#50FA7B;--shiki-default-font-style:italic}html pre.shiki code .seVfx, html code.shiki .seVfx{--shiki-default:#E9F284}html pre.shiki code .s-mGx, html code.shiki .s-mGx{--shiki-default:#F1FA8C}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 pre.shiki code .sAOxA, html code.shiki .sAOxA{--shiki-default:#50FA7B}html pre.shiki code .shSDL, html code.shiki .shSDL{--shiki-default:#6272A4}",{"title":126,"searchDepth":246,"depth":246,"links":3834},[3835,3836,3837,3838,3839,3840,3841,3846,3847,3848,3851,3852,3853,3860],{"id":2451,"depth":246,"text":2452},{"id":2465,"depth":246,"text":2466},{"id":2490,"depth":246,"text":2491},{"id":2618,"depth":246,"text":2619},{"id":2639,"depth":246,"text":2640},{"id":2660,"depth":246,"text":2661},{"id":2813,"depth":246,"text":2814,"children":3842},[3843,3844,3845],{"id":3177,"depth":264,"text":3178},{"id":3329,"depth":264,"text":3330},{"id":3375,"depth":264,"text":3376},{"id":3539,"depth":246,"text":3540},{"id":3553,"depth":246,"text":3554},{"id":3614,"depth":246,"text":3615,"children":3849},[3850],{"id":3646,"depth":264,"text":3647},{"id":3661,"depth":246,"text":3662},{"id":3747,"depth":246,"text":3748},{"id":1702,"depth":246,"text":1703,"children":3854},[3855,3856,3857,3858,3859],{"id":3765,"depth":264,"text":3766},{"id":3775,"depth":264,"text":3776},{"id":3782,"depth":264,"text":3783},{"id":3789,"depth":264,"text":3790},{"id":3796,"depth":264,"text":3797},{"id":1792,"depth":246,"text":1793},[1834,1835,3862],"seo","How to add structured data in Nuxt.js with nuxt-schema-org: Article, breadcrumb, validation, indexing in Search Console and Google Discover, with code examples.","how-to-create-structured-data-with-nuxtjs",{},"\u002Fen\u002Fblog\u002Fhow-to-create-structured-data-with-nuxtjs","como-criar-dados-estruturados-com-nuxtjs","2025-01-22",{"title":2438,"description":3863},{"loc":3866,"images":3871,"lastmod":1844},[3872,3873],{"loc":2486},{"loc":3535},"en\u002Fblog\u002Fhow-to-create-structured-data-with-nuxtjs",2024,"u3LF7VbQ71jTkhZ6Mxdt0r_KmRugMzYtUkzwze8nyGA",{"id":3878,"title":3879,"articleCover":3880,"articleCoverEmbed":8,"articleCoverEmbedUploadDate":8,"articleCoverHeight":9,"articleCoverWidth":10,"authorId":11,"body":3881,"categoriesIds":4546,"description":4547,"enAlternateSlug":4548,"extension":1837,"meta":4549,"navigation":267,"path":4550,"ptBrAlternateSlug":4551,"publishedAt":4552,"seo":4553,"sitemap":4554,"stem":4557,"updatedAt":1844,"wordCount":4558,"__hash__":4559},"blogEn\u002Fen\u002Fblog\u002Fhow-to-use-middlewares-nuxtjs.md","Middlewares: What They Are and How to Use Them in Nuxt.JS","\u002Fimages\u002Fblog\u002Fnuxt\u002Ffront-end-middlewares-thumb.webp",{"type":13,"value":3882,"toc":4539},[3883,3887,3899,3919,3922,3935,3938,3942,3949,3952,3955,3966,3969,3973,3976,3987,4003,4144,4152,4329,4339,4460,4463,4510,4523,4525,4528,4536],[25,3884,3886],{"id":3885},"what-are-middlewares","What Are Middlewares?",[16,3888,3889,3890,3894,3895,3898],{},"In Front-end development, ",[3891,3892,3893],"strong",{},"Middlewares"," are blocks of code that execute ",[3891,3896,3897],{},"before navigation to a new page occurs",", allowing various controls in our application, such as:",[918,3900,3901,3907,3913],{},[921,3902,3903,3906],{},[3891,3904,3905],{},"Authentication",": Checks if the user is logged in; otherwise, they are redirected to the login screen.",[921,3908,3909,3912],{},[3891,3910,3911],{},"Permission",": Verifies if the user has access to the page; if not, they are redirected to an allowed page.",[921,3914,3915,3918],{},[3891,3916,3917],{},"Logs",": Saves information about page activities.",[16,3920,3921],{},"Middlewares act as an intermediate layer, working from the start of the page load until its complete rendering.",[16,3923,3924,3925,139,3930,702],{},"This middleware functionality is present in modern Front-end frameworks such as ",[1798,3926,3929],{"href":3927,"rel":3928},"https:\u002F\u002Fnextjs.org",[1802],"Next.js",[1798,3931,3934],{"href":3932,"rel":3933},"https:\u002F\u002Fnuxt.com",[1802],"Nuxt.JS",[16,3936,3937],{},"In this article, I will teach you how to implement route middlewares in Nuxt.JS.",[25,3939,3941],{"id":3940},"middlewares-in-a-less-technical-way","Middlewares in a Less Technical Way",[1935,3943],{":copyright":3944,"alt":3945,"height":3946,"src":3947,"width":3948},"{\"title\": \"People at a party\", \"licenseId\": \"pexels\", \"author\": {\"name\": \"Cottonbro Studio\", \"url\": \"https:\u002F\u002Fwww.pexels.com\u002Fphoto\u002Fpeople-toasting-wine-glasses-3171837\" }}","people at a party",428,"\u002Fimages\u002Fblog\u002Fpeople\u002Fpexels-cottonbro-3171837.webp",640,[16,3950,3951],{},"To explain it more simply, let’s use an analogy:",[16,3953,3954],{},"Imagine you want to enter a party, and in this process, you go through three security staff members, who act as \"middlewares,\" each responsible for a specific action.",[2678,3956,3957,3960,3963],{},[921,3958,3959],{},"The first security guard checks if you have a valid ticket, similar to an authentication middleware. If you don’t have one, you are redirected to the ticket office or login screen.",[921,3961,3962],{},"The second guard ensures you are of legal age, a crucial requirement for entering the party, similar to a permission middleware. If you don’t meet the criteria, you are sent away.",[921,3964,3965],{},"The third and final guard gives you a wristband indicating whether you have VIP access, much like middlewares that add data to requests.",[16,3967,3968],{},"Finally, after passing all these checks, you are free to enter the party hall.",[25,3970,3972],{"id":3971},"types-of-middlewares","Types of Middlewares",[16,3974,3975],{},"In Nuxt.js, we have the following types of Middlewares:",[918,3977,3978,3981,3984],{},[921,3979,3980],{},"Global",[921,3982,3983],{},"Inline or Anonymous",[921,3985,3986],{},"Named",[2678,3988,3989],{},[921,3990,3991,3994,3995,3998,3999,4002],{},[3891,3992,3993],{},"Global Middlewares"," are created inside the ",[20,3996,3997],{},"\u002Fmiddleware"," folder and execute on all pages. The ",[20,4000,4001],{},"global"," suffix is required.",[120,4004,4008],{"className":4005,"code":4006,"language":4007,"meta":126,"style":126},"language-typescript shiki shiki-themes dracula","\u002F*\n  Example of a Global Middleware:\n  This middleware checks if the requested URL\u002Froute exists.\n  If it doesn’t exist, the user is redirected to the links page.\n\n  File Name and Directory:\n  \u002Fmiddleware\u002FnotFound.global.js\n*\u002F\n\nexport default defineNuxtRouteMiddleware((to) => {\n  const hasFoundRoute = to.matched.length > 0;\n\n  if (!hasFoundRoute) {\n    return navigateTo({\n      path: \"\u002Flinks\"\n    });\n  }\n});\n","typescript",[20,4009,4010,4015,4020,4025,4030,4034,4039,4044,4049,4053,4073,4093,4097,4109,4118,4131,4136,4140],{"__ignoreMap":126},[209,4011,4012],{"class":211,"line":212},[209,4013,4014],{"class":1139},"\u002F*\n",[209,4016,4017],{"class":211,"line":246},[209,4018,4019],{"class":1139},"  Example of a Global Middleware:\n",[209,4021,4022],{"class":211,"line":264},[209,4023,4024],{"class":1139},"  This middleware checks if the requested URL\u002Froute exists.\n",[209,4026,4027],{"class":211,"line":271},[209,4028,4029],{"class":1139},"  If it doesn’t exist, the user is redirected to the links page.\n",[209,4031,4032],{"class":211,"line":296},[209,4033,268],{"emptyLinePlaceholder":267},[209,4035,4036],{"class":211,"line":301},[209,4037,4038],{"class":1139},"  File Name and Directory:\n",[209,4040,4041],{"class":211,"line":326},[209,4042,4043],{"class":1139},"  \u002Fmiddleware\u002FnotFound.global.js\n",[209,4045,4046],{"class":211,"line":352},[209,4047,4048],{"class":1139},"*\u002F\n",[209,4050,4051],{"class":211,"line":399},[209,4052,268],{"emptyLinePlaceholder":267},[209,4054,4055,4057,4059,4062,4065,4067,4069,4071],{"class":211,"line":405},[209,4056,1145],{"class":219},[209,4058,2762],{"class":219},[209,4060,4061],{"class":257}," defineNuxtRouteMiddleware",[209,4063,4064],{"class":215},"((",[209,4066,1658],{"class":668},[209,4068,672],{"class":215},[209,4070,287],{"class":219},[209,4072,2054],{"class":215},[209,4074,4075,4077,4080,4082,4085,4088,4091],{"class":211,"line":410},[209,4076,1200],{"class":219},[209,4078,4079],{"class":215}," hasFoundRoute ",[209,4081,230],{"class":219},[209,4083,4084],{"class":215}," to.matched.length ",[209,4086,4087],{"class":219},">",[209,4089,4090],{"class":445}," 0",[209,4092,1247],{"class":215},[209,4094,4095],{"class":211,"line":425},[209,4096,268],{"emptyLinePlaceholder":267},[209,4098,4099,4102,4104,4106],{"class":211,"line":437},[209,4100,4101],{"class":219},"  if",[209,4103,416],{"class":215},[209,4105,419],{"class":219},[209,4107,4108],{"class":215},"hasFoundRoute) {\n",[209,4110,4111,4114,4116],{"class":211,"line":451},[209,4112,4113],{"class":219},"    return",[209,4115,1516],{"class":257},[209,4117,434],{"class":215},[209,4119,4120,4123,4125,4127,4129],{"class":211,"line":469},[209,4121,4122],{"class":215},"      path",[209,4124,309],{"class":219},[209,4126,459],{"class":233},[209,4128,74],{"class":237},[209,4130,1602],{"class":233},[209,4132,4133],{"class":211,"line":480},[209,4134,4135],{"class":215},"    });\n",[209,4137,4138],{"class":211,"line":486},[209,4139,2212],{"class":215},[209,4141,4142],{"class":211,"line":492},[209,4143,718],{"class":215},[2678,4145,4146],{"start":246},[921,4147,4148,4151],{},[3891,4149,4150],{},"Inline or Anonymous Middlewares"," are specific to a page, defined directly within the Nuxt page file. This limits code reuse since it cannot be easily shared with other pages.",[120,4153,4155],{"className":2751,"code":4154,"language":2753,"meta":126,"style":126},"\u002F*\n  Example of an Inline Middleware:\n  This middleware checks if data is present in the store.\n  If not, the data is requested.\n*\u002F\n\n\u003Cscript setup lang=\"ts\">\nimport { useStore } from 'vuex';\n\ndefinePageMeta({\n  middleware: [\n    async function (to, from) {\n      const store = useStore();\n      if (!store.state.user) {\n        await store.dispatch(\"fetchUser\");\n      }\n    },\n  ],\n});\n\u003C\u002Fscript>\n",[20,4156,4157,4161,4166,4171,4176,4180,4184,4204,4219,4223,4230,4235,4252,4267,4279,4301,4305,4310,4315,4321],{"__ignoreMap":126},[209,4158,4159],{"class":211,"line":212},[209,4160,4014],{"class":1139},[209,4162,4163],{"class":211,"line":246},[209,4164,4165],{"class":1139},"  Example of an Inline Middleware:\n",[209,4167,4168],{"class":211,"line":264},[209,4169,4170],{"class":1139},"  This middleware checks if data is present in the store.\n",[209,4172,4173],{"class":211,"line":271},[209,4174,4175],{"class":1139},"  If not, the data is requested.\n",[209,4177,4178],{"class":211,"line":296},[209,4179,4048],{"class":1139},[209,4181,4182],{"class":211,"line":301},[209,4183,268],{"emptyLinePlaceholder":267},[209,4185,4186,4188,4190,4192,4194,4196,4198,4200,4202],{"class":211,"line":326},[209,4187,216],{"class":215},[209,4189,220],{"class":219},[209,4191,224],{"class":223},[209,4193,227],{"class":223},[209,4195,230],{"class":219},[209,4197,234],{"class":233},[209,4199,238],{"class":237},[209,4201,234],{"class":233},[209,4203,243],{"class":215},[209,4205,4206,4209,4211,4214,4216],{"class":211,"line":352},[209,4207,4208],{"class":215},"import ",[209,4210,1869],{"class":219},[209,4212,4213],{"class":215}," useStore ",[209,4215,343],{"class":219},[209,4217,4218],{"class":215}," from 'vuex';\n",[209,4220,4221],{"class":211,"line":399},[209,4222,268],{"emptyLinePlaceholder":267},[209,4224,4225,4228],{"class":211,"line":405},[209,4226,4227],{"class":215},"definePageMeta(",[209,4229,2038],{"class":219},[209,4231,4232],{"class":211,"line":410},[209,4233,4234],{"class":215},"  middleware: [\n",[209,4236,4237,4240,4242,4244,4246,4248,4250],{"class":211,"line":425},[209,4238,4239],{"class":219},"    async",[209,4241,1148],{"class":219},[209,4243,416],{"class":215},[209,4245,1658],{"class":668},[209,4247,3341],{"class":215},[209,4249,1661],{"class":668},[209,4251,1195],{"class":215},[209,4253,4254,4257,4260,4262,4265],{"class":211,"line":437},[209,4255,4256],{"class":219},"      const",[209,4258,4259],{"class":215}," store ",[209,4261,230],{"class":219},[209,4263,4264],{"class":257}," useStore",[209,4266,261],{"class":215},[209,4268,4269,4272,4274,4276],{"class":211,"line":451},[209,4270,4271],{"class":219},"      if",[209,4273,416],{"class":215},[209,4275,419],{"class":219},[209,4277,4278],{"class":215},"store.state.user) {\n",[209,4280,4281,4284,4287,4290,4292,4294,4297,4299],{"class":211,"line":469},[209,4282,4283],{"class":219},"        await",[209,4285,4286],{"class":215}," store.",[209,4288,4289],{"class":257},"dispatch",[209,4291,362],{"class":215},[209,4293,234],{"class":233},[209,4295,4296],{"class":237},"fetchUser",[209,4298,234],{"class":233},[209,4300,402],{"class":215},[209,4302,4303],{"class":211,"line":480},[209,4304,2202],{"class":215},[209,4306,4307],{"class":211,"line":486},[209,4308,4309],{"class":215},"    },\n",[209,4311,4312],{"class":211,"line":492},[209,4313,4314],{"class":215},"  ],\n",[209,4316,4317,4319],{"class":211,"line":502},[209,4318,343],{"class":219},[209,4320,402],{"class":215},[209,4322,4323,4325,4327],{"class":211,"line":507},[209,4324,495],{"class":215},[209,4326,220],{"class":219},[209,4328,243],{"class":215},[2678,4330,4331],{"start":264},[921,4332,4333,3994,4336,4338],{},[3891,4334,4335],{},"Named Middlewares",[20,4337,3997],{}," folder.",[120,4340,4342],{"className":2751,"code":4341,"language":2753,"meta":126,"style":126},"\u002F*\n  Example of a Named Middleware:\n  This middleware checks if the user is logged in.\n  If not, they are redirected to the login page.\n\n  File Name and Directory:\n  \u002Fmiddleware\u002Fauth.js\n*\u002F\n\nexport default defineNuxtRouteMiddleware((to, from) => {\n  const { $store } = useNuxtApp();\n\n  if (!$store.auth?.loggedIn) {\n    return navigateTo(\"\u002Flogin\");\n  }\n});\n",[20,4343,4344,4348,4353,4358,4363,4367,4371,4376,4380,4384,4406,4420,4424,4435,4452,4456],{"__ignoreMap":126},[209,4345,4346],{"class":211,"line":212},[209,4347,4014],{"class":1139},[209,4349,4350],{"class":211,"line":246},[209,4351,4352],{"class":1139},"  Example of a Named Middleware:\n",[209,4354,4355],{"class":211,"line":264},[209,4356,4357],{"class":1139},"  This middleware checks if the user is logged in.\n",[209,4359,4360],{"class":211,"line":271},[209,4361,4362],{"class":1139},"  If not, they are redirected to the login page.\n",[209,4364,4365],{"class":211,"line":296},[209,4366,268],{"emptyLinePlaceholder":267},[209,4368,4369],{"class":211,"line":301},[209,4370,4038],{"class":1139},[209,4372,4373],{"class":211,"line":326},[209,4374,4375],{"class":1139},"  \u002Fmiddleware\u002Fauth.js\n",[209,4377,4378],{"class":211,"line":352},[209,4379,4048],{"class":1139},[209,4381,4382],{"class":211,"line":399},[209,4383,268],{"emptyLinePlaceholder":267},[209,4385,4386,4388,4390,4392,4394,4396,4398,4400,4402,4404],{"class":211,"line":405},[209,4387,1145],{"class":219},[209,4389,2762],{"class":219},[209,4391,4061],{"class":257},[209,4393,4064],{"class":215},[209,4395,1658],{"class":668},[209,4397,3341],{"class":215},[209,4399,1661],{"class":668},[209,4401,672],{"class":215},[209,4403,287],{"class":219},[209,4405,2054],{"class":215},[209,4407,4408,4410,4413,4415,4418],{"class":211,"line":410},[209,4409,1200],{"class":219},[209,4411,4412],{"class":215}," { $store } ",[209,4414,230],{"class":219},[209,4416,4417],{"class":257}," useNuxtApp",[209,4419,261],{"class":215},[209,4421,4422],{"class":211,"line":425},[209,4423,268],{"emptyLinePlaceholder":267},[209,4425,4426,4428,4430,4432],{"class":211,"line":437},[209,4427,4101],{"class":219},[209,4429,416],{"class":215},[209,4431,419],{"class":219},[209,4433,4434],{"class":215},"$store.auth?.loggedIn) {\n",[209,4436,4437,4439,4441,4443,4445,4448,4450],{"class":211,"line":451},[209,4438,4113],{"class":219},[209,4440,1516],{"class":257},[209,4442,362],{"class":215},[209,4444,234],{"class":233},[209,4446,4447],{"class":237},"\u002Flogin",[209,4449,234],{"class":233},[209,4451,402],{"class":215},[209,4453,4454],{"class":211,"line":469},[209,4455,2212],{"class":215},[209,4457,4458],{"class":211,"line":480},[209,4459,718],{"class":215},[16,4461,4462],{},"For Named Middlewares, you need to specify which pages use a particular middleware. One way to do this is by modifying the page files that will utilize the middleware:",[120,4464,4466],{"className":2751,"code":4465,"language":2753,"meta":126,"style":126},"\u003Cscript setup>\ndefinePageMeta({\n  middleware: \"auth-admin\"\n});\n\u003C\u002Fscript>\n",[20,4467,4468,4478,4484,4496,4502],{"__ignoreMap":126},[209,4469,4470,4472,4474,4476],{"class":211,"line":212},[209,4471,216],{"class":215},[209,4473,220],{"class":219},[209,4475,224],{"class":223},[209,4477,243],{"class":215},[209,4479,4480,4482],{"class":211,"line":246},[209,4481,4227],{"class":215},[209,4483,2038],{"class":219},[209,4485,4486,4489,4491,4494],{"class":211,"line":264},[209,4487,4488],{"class":215},"  middleware: ",[209,4490,234],{"class":233},[209,4492,4493],{"class":237},"auth-admin",[209,4495,1602],{"class":233},[209,4497,4498,4500],{"class":211,"line":271},[209,4499,343],{"class":219},[209,4501,402],{"class":215},[209,4503,4504,4506,4508],{"class":211,"line":296},[209,4505,495],{"class":215},[209,4507,220],{"class":219},[209,4509,243],{"class":215},[16,4511,4512,4513,1866,4516,4519,4520,702],{},"It’s also important to note that even if you name middleware files using ",[20,4514,4515],{},"PascalCase",[20,4517,4518],{},"camelCase",", file names are normalized to ",[20,4521,4522],{},"kebab-case",[608,4524,1793],{"id":1792},[16,4526,4527],{},"I hope this article has helped you understand the key points of creating and using Middlewares in Nuxt.JS in a simple and quick way 🙋🏻‍♂️.",[16,4529,4530,4531,702],{},"If you need more information about Middlewares in Nuxt.JS, you can check the ",[1798,4532,4535],{"href":4533,"rel":4534},"https:\u002F\u002Fnuxt.com\u002Fdocs\u002Fguide\u002Fdirectory-structure\u002Fmiddleware",[1802],"official documentation",[1805,4537,4538],{},"html pre.shiki code .shSDL, html code.shiki .shSDL{--shiki-default:#6272A4}html pre.shiki code .s0Tla, html code.shiki .s0Tla{--shiki-default:#FF79C6}html pre.shiki code .sAOxA, html code.shiki .sAOxA{--shiki-default:#50FA7B}html pre.shiki code .sCdxs, html code.shiki .sCdxs{--shiki-default:#F8F8F2}html pre.shiki code .sGEwX, html code.shiki .sGEwX{--shiki-default:#FFB86C;--shiki-default-font-style:italic}html pre.shiki code .sIQBb, html code.shiki .sIQBb{--shiki-default:#BD93F9}html pre.shiki code .seVfx, html code.shiki .seVfx{--shiki-default:#E9F284}html pre.shiki code .s-mGx, html code.shiki .s-mGx{--shiki-default:#F1FA8C}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 pre.shiki code .sY_PY, html code.shiki .sY_PY{--shiki-default:#50FA7B;--shiki-default-font-style:italic}",{"title":126,"searchDepth":246,"depth":246,"links":4540},[4541,4542,4543],{"id":3885,"depth":246,"text":3886},{"id":3940,"depth":246,"text":3941},{"id":3971,"depth":246,"text":3972,"children":4544},[4545],{"id":1792,"depth":264,"text":1793},[1834,1835],"Learn what middlewares are and how to use them in Nuxt.js to control navigation, authentication, and permissions on your pages, with examples of each type.","how-to-use-middlewares-nuxtjs",{},"\u002Fen\u002Fblog\u002Fhow-to-use-middlewares-nuxtjs","como-usar-middlewares-no-nuxtjs","2025-02-03",{"title":3879,"description":4547},{"loc":4550,"images":4555,"lastmod":1844},[4556],{"loc":3947},"en\u002Fblog\u002Fhow-to-use-middlewares-nuxtjs",470,"2zdrkBknELzfzccjTJU6S8fSLCUeRF9bZPegbB7p50s",{"id":4561,"title":4562,"articleCover":4563,"articleCoverEmbed":8,"articleCoverEmbedUploadDate":8,"articleCoverHeight":9,"articleCoverWidth":10,"authorId":11,"body":4564,"categoriesIds":4721,"description":4722,"enAlternateSlug":4723,"extension":1837,"meta":4724,"navigation":267,"path":4725,"ptBrAlternateSlug":4726,"publishedAt":4727,"seo":4728,"sitemap":4729,"stem":4730,"updatedAt":1844,"wordCount":4731,"__hash__":4732},"blogEn\u002Fen\u002Fblog\u002Fvercel-buys-nuxt-labs.md","Vercel Acquired Nuxt Labs! What Does This Mean for Nuxt?","\u002Fimages\u002Fen\u002Fblog\u002Fnuxt\u002Fvercel-buys-nuxt-labs.webp",{"type":13,"value":4565,"toc":4714},[4566,4577,4580,4584,4593,4596,4610,4613,4617,4624,4630,4638,4643,4647,4654,4674,4677,4681,4684,4690,4695,4697,4704,4711],[16,4567,4568,4569,4572,4573,4576],{},"Last week, ",[3891,4570,4571],{},"Vercel","—the company known for developing Next.js—announced the acquisition of ",[3891,4574,4575],{},"Nuxt Labs",", a startup created by the core maintainers of the Nuxt.js framework. The news took the community by surprise, raising questions about Nuxt's future and the impact of this move on Vue ecosystem developers.",[16,4578,4579],{},"In this article, let's break down what was acquired, why it happened, and what changes in practice.",[25,4581,4583],{"id":4582},"what-is-nuxt-labs","What is Nuxt Labs?",[16,4585,4586,4588,4589,4592],{},[3891,4587,4575],{}," is a company founded by ",[3891,4590,4591],{},"Sébastien Chopin"," and part of the Nuxt core team to support the sustainable development of the framework. Since Nuxt is open source and free to use, Nuxt Labs was created to offer paid products that help fund core development.",[16,4594,4595],{},"Some of Nuxt Labs' main products include:",[918,4597,4598,4604],{},[921,4599,4600,4603],{},[3891,4601,4602],{},"Nuxt Studio"," — A visual interface for creating content with Nuxt Content",[921,4605,4606,4609],{},[3891,4607,4608],{},"Nuxt UI Pro"," — A premium component library built with Tailwind CSS",[16,4611,4612],{},"These tools allowed the core team to work more consistently on Nuxt, supporting major releases like Nuxt 3 and new official modules.",[25,4614,4616],{"id":4615},"why-did-vercel-acquire-nuxt-labs","Why did Vercel acquire Nuxt Labs?",[16,4618,4619,4620,4623],{},"According to the official announcement on Vercel's blog and Nuxt Labs' statement, the motivation is to ",[3891,4621,4622],{},"invest in the Vue ecosystem"," and ensure that Nuxt has the support it needs to continue evolving.",[16,4625,4626,4627,702],{},"This move is also part of Vercel's broader strategy to expand its support for multiple meta-frameworks—just like it has done with Next.js, SvelteKit, and Astro. The goal is to make ",[3891,4628,4629],{},"Vercel the best platform to host any modern frontend project",[16,4631,4632,4633,309],{},"In their words from the ",[1798,4634,4637],{"href":4635,"rel":4636},"https:\u002F\u002Fvercel.com\u002Fblog\u002Fnuxtlabs-joins-vercel",[1802],"official announcement",[2496,4639,4640],{},[16,4641,4642],{},"“We want to ensure Nuxt continues to innovate and remains a powerful option for Vue developers. We're excited to welcome Sébastien Chopin and part of the Nuxt Labs team to Vercel.”",[25,4644,4646],{"id":4645},"what-changes-in-practice","What changes in practice?",[16,4648,4649,4650,4653],{},"For now, ",[3891,4651,4652],{},"Nuxt will remain open source and independent",". The framework itself was not bought—rather, the company that maintains and builds products around it was. Still, the impact is meaningful:",[918,4655,4656,4662,4668],{},[921,4657,4658,4661],{},[3891,4659,4660],{},"More resources"," for Nuxt's ongoing development",[921,4663,4664,4667],{},[3891,4665,4666],{},"Core team now employed by Vercel",", gaining more financial stability",[921,4669,4670,4673],{},[3891,4671,4672],{},"Tighter integration with the Vercel platform"," (while still supporting alternatives like Netlify or self-hosting)",[16,4675,4676],{},"Paid products such as Nuxt Studio and Nuxt UI Pro are expected to remain available, potentially with improvements backed by Vercel's investment.",[25,4678,4680],{"id":4679},"what-about-the-community","What about the community?",[16,4682,4683],{},"Sébastien Chopin emphasized that the community will continue to play a central role in Nuxt's future. Project governance remains open, and development will continue to happen in public on GitHub.",[16,4685,4686,4687,702],{},"There's also a strong commitment to Vue's ecosystem — Vercel even mentioned plans to ",[3891,4688,4689],{},"invest more in frameworks beyond React",[2496,4691,4692],{},[16,4693,4694],{},"“Vue is one of the most used frameworks on Vercel. We want to support all modern frameworks, and Nuxt is a key part of that.”",[25,4696,1793],{"id":1792},[16,4698,4699,4700,4703],{},"It's important to note that ",[3891,4701,4702],{},"the Nuxt project itself wasn't bought","—just its main supporting company. With the acquisition of Nuxt Labs, Vercel is showing a real willingness to invest in a broader range of frameworks and not limit itself to React.",[16,4705,4706,4707,4710],{},"In the short term, the expectation is ",[3891,4708,4709],{},"more investment, more stability, and faster evolution for Nuxt","—great news for developers working with Vue every day.",[16,4712,4713],{},"Let's keep an eye on what's next in this exciting new chapter.",{"title":126,"searchDepth":246,"depth":246,"links":4715},[4716,4717,4718,4719,4720],{"id":4582,"depth":246,"text":4583},{"id":4615,"depth":246,"text":4616},{"id":4645,"depth":246,"text":4646},{"id":4679,"depth":246,"text":4680},{"id":1792,"depth":246,"text":1793},[1834],"Vercel has announced the acquisition of Nuxt Labs, the company behind the Nuxt ecosystem. Find out what this means for the framework and the community.","vercel-buys-nuxt-labs",{},"\u002Fen\u002Fblog\u002Fvercel-buys-nuxt-labs","vercel-compra-nuxt-labs","2025-07-17",{"title":4562,"description":4722},{"loc":4725,"lastmod":1844},"en\u002Fblog\u002Fvercel-buys-nuxt-labs",540,"ocw7cKaZP5YMNC433fS1UZuinvM1rzeIW2dEe9zAoUQ",{"id":4734,"title":4735,"articleCover":4736,"articleCoverEmbed":8,"articleCoverEmbedUploadDate":8,"articleCoverHeight":9,"articleCoverWidth":10,"authorId":11,"body":4737,"categoriesIds":5698,"description":5699,"enAlternateSlug":5700,"extension":1837,"meta":5701,"navigation":267,"path":5702,"ptBrAlternateSlug":5703,"publishedAt":1844,"seo":5704,"sitemap":5705,"stem":5706,"updatedAt":1844,"wordCount":5707,"__hash__":5708},"blogEn\u002Fen\u002Fblog\u002Fvue-vapor-mode-what-it-is-and-how-to-use-it.md","Vue Vapor Mode: what it is and what changes in Vue 3.6","\u002Fimages\u002Fen\u002Fblog\u002Fvue\u002Fvue-vapor-mode-what-it-is-and-how-to-use-it.webp",{"type":13,"value":4738,"toc":5672},[4739,4754,4760,4764,4767,4770,4773,4777,4802,4806,4816,4945,4952,4967,4971,4982,5051,5054,5058,5064,5121,5130,5134,5139,5184,5197,5200,5264,5275,5278,5282,5285,5346,5355,5362,5365,5372,5386,5419,5425,5429,5432,5539,5554,5558,5561,5568,5572,5575,5578,5595,5598,5602,5605,5609,5617,5621,5626,5630,5639,5643,5646,5650,5662,5666,5669],[16,4740,4741,4742,4745,4746,4749,4750,4753],{},"Vue 3.6 is not stable yet. As of October 6, 2026, the latest version is ",[20,4743,4744],{},"3.6.0-rc.10"," (September 30), and the stable release is still ",[20,4747,4748],{},"3.5.43"," (September 17). I checked both on npm and in the ",[20,4751,4752],{},"vuejs\u002Fcore"," releases on that date.",[16,4755,4756,4759],{},[3891,4757,4758],{},"Vue Vapor Mode"," is the headline feature of 3.6: a compilation mode that skips the Virtual DOM and generates code that updates the DOM directly. It is opt-in, so you pick which components use it and you don't rewrite your app. This article covers how Vapor works, how to enable it in Vue and in Nuxt 4.6, what it doesn't support yet, and when it's worth a try.",[25,4761,4763],{"id":4762},"how-vapor-works","How Vapor works",[16,4765,4766],{},"In the traditional mode, when reactive state changes, Vue runs the render function, builds a new tree of VNodes (plain JavaScript objects that describe the UI), compares it with the previous tree, and only then touches the real DOM.",[16,4768,4769],{},"In Vapor Mode, the compiler analyzes the template and generates code that manipulates the DOM directly. Vapor components don't create VNodes or diff trees at runtime. When a reactive value changes, only the part of the DOM bound to it is updated.",[16,4771,4772],{},"According to the official Vue 3.6 release notes, the goal is a smaller baseline bundle and better performance. The same notes say Vapor reached the same level of performance as Solid and Svelte 5 in third-party benchmarks (js-framework-benchmark). As with any benchmark, the result depends on the scenario measured.",[608,4774,4776],{"id":4775},"what-about-alien-signals","What about Alien Signals?",[16,4778,4779,4780,4783,4784,4789,4790,3341,4793,3341,4796,139,4798,4801],{},"Vue 3.6 also ships a major refactor of ",[20,4781,4782],{},"@vue\u002Freactivity"," based on the ",[1798,4785,4788],{"href":4786,"rel":4787},"https:\u002F\u002Fgithub.com\u002Fstackblitz\u002Falien-signals",[1802],"alien-signals"," library. The rc.1 notes say it improves the reactivity system's performance and memory usage. It's an internal change: ",[20,4791,4792],{},"ref",[20,4794,4795],{},"reactive",[20,4797,578],{},[20,4799,4800],{},"watch"," keep the same API. Since it's part of Vue 3.6, you get it whether or not you use Vapor.",[25,4803,4805],{"id":4804},"how-to-enable-vapor-mode","How to enable Vapor Mode",[16,4807,4808,4809,4811,4812,4815],{},"Vapor works with SFCs that use ",[20,4810,594],{}," or only a template. You mark the component with the ",[20,4813,4814],{},"vapor"," attribute:",[120,4817,4819],{"className":203,"code":4818,"language":205,"meta":126,"style":126},"\u003Cscript setup vapor lang=\"ts\">\nimport { ref } from 'vue'\n\nconst count = ref(0)\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Cbutton @click=\"count++\">\n    count is {{ count }}\n  \u003C\u002Fbutton>\n\u003C\u002Ftemplate>\n",[20,4820,4821,4844,4862,4866,4885,4893,4897,4905,4924,4929,4937],{"__ignoreMap":126},[209,4822,4823,4825,4827,4829,4832,4834,4836,4838,4840,4842],{"class":211,"line":212},[209,4824,216],{"class":215},[209,4826,220],{"class":219},[209,4828,224],{"class":223},[209,4830,4831],{"class":223}," vapor",[209,4833,227],{"class":223},[209,4835,230],{"class":219},[209,4837,234],{"class":233},[209,4839,238],{"class":237},[209,4841,234],{"class":233},[209,4843,243],{"class":215},[209,4845,4846,4849,4852,4854,4857,4859],{"class":211,"line":246},[209,4847,4848],{"class":219},"import",[209,4850,4851],{"class":215}," { ref } ",[209,4853,1661],{"class":219},[209,4855,4856],{"class":233}," '",[209,4858,205],{"class":237},[209,4860,4861],{"class":233},"'\n",[209,4863,4864],{"class":211,"line":264},[209,4865,268],{"emptyLinePlaceholder":267},[209,4867,4868,4870,4873,4875,4878,4880,4882],{"class":211,"line":271},[209,4869,249],{"class":219},[209,4871,4872],{"class":215}," count ",[209,4874,230],{"class":219},[209,4876,4877],{"class":257}," ref",[209,4879,362],{"class":215},[209,4881,1223],{"class":445},[209,4883,4884],{"class":215},")\n",[209,4886,4887,4889,4891],{"class":211,"line":296},[209,4888,495],{"class":215},[209,4890,220],{"class":219},[209,4892,243],{"class":215},[209,4894,4895],{"class":211,"line":301},[209,4896,268],{"emptyLinePlaceholder":267},[209,4898,4899,4901,4903],{"class":211,"line":326},[209,4900,216],{"class":215},[209,4902,512],{"class":219},[209,4904,243],{"class":215},[209,4906,4907,4909,4911,4913,4915,4917,4920,4922],{"class":211,"line":352},[209,4908,520],{"class":215},[209,4910,1622],{"class":219},[209,4912,1625],{"class":223},[209,4914,230],{"class":219},[209,4916,234],{"class":233},[209,4918,4919],{"class":237},"count++",[209,4921,234],{"class":233},[209,4923,243],{"class":215},[209,4925,4926],{"class":211,"line":399},[209,4927,4928],{"class":215},"    count is {{ count }}\n",[209,4930,4931,4933,4935],{"class":211,"line":405},[209,4932,559],{"class":215},[209,4934,1622],{"class":219},[209,4936,243],{"class":215},[209,4938,4939,4941,4943],{"class":211,"line":410},[209,4940,495],{"class":215},[209,4942,512],{"class":219},[209,4944,243],{"class":215},[16,4946,4947,4948,4951],{},"Outside Nuxt, ",[20,4949,4950],{},"@vitejs\u002Fplugin-vue"," compiles Vapor SFCs (the plugin's changelog records Vapor component support, including template-only ones), so use a recent version.",[16,4953,4954,4955,4958,4959,4962,4963,4966],{},"Vue also accepts ",[20,4956,4957],{},"\u003Cscript vapor>"," as shorthand for ",[20,4960,4961],{},"\u003Cscript setup vapor>",". You can instead put the marker on the template with ",[20,4964,4965],{},"\u003Ctemplate vapor>"," to compile the whole SFC in Vapor mode.",[608,4968,4970],{"id":4969},"vapor-components-in-an-existing-app","Vapor components in an existing app",[16,4972,4973,4974,4977,4978,4981],{},"In an app created with ",[20,4975,4976],{},"createApp()",", you need to install ",[20,4979,4980],{},"vaporInteropPlugin"," to use Vapor components:",[120,4983,4985],{"className":1131,"code":4984,"language":238,"meta":126,"style":126},"import { createApp, vaporInteropPlugin } from 'vue'\nimport App from '.\u002FApp.vue'\n\ncreateApp(App).use(vaporInteropPlugin).mount('#app')\n",[20,4986,4987,5002,5018,5022],{"__ignoreMap":126},[209,4988,4989,4991,4994,4996,4998,5000],{"class":211,"line":212},[209,4990,4848],{"class":219},[209,4992,4993],{"class":215}," { createApp, vaporInteropPlugin } ",[209,4995,1661],{"class":219},[209,4997,4856],{"class":233},[209,4999,205],{"class":237},[209,5001,4861],{"class":233},[209,5003,5004,5006,5009,5011,5013,5016],{"class":211,"line":246},[209,5005,4848],{"class":219},[209,5007,5008],{"class":215}," App ",[209,5010,1661],{"class":219},[209,5012,4856],{"class":233},[209,5014,5015],{"class":237},".\u002FApp.vue",[209,5017,4861],{"class":233},[209,5019,5020],{"class":211,"line":264},[209,5021,268],{"emptyLinePlaceholder":267},[209,5023,5024,5027,5030,5033,5036,5039,5041,5044,5047,5049],{"class":211,"line":271},[209,5025,5026],{"class":257},"createApp",[209,5028,5029],{"class":215},"(App).",[209,5031,5032],{"class":257},"use",[209,5034,5035],{"class":215},"(vaporInteropPlugin).",[209,5037,5038],{"class":257},"mount",[209,5040,362],{"class":215},[209,5042,5043],{"class":233},"'",[209,5045,5046],{"class":237},"#app",[209,5048,5043],{"class":233},[209,5050,4884],{"class":215},[16,5052,5053],{},"With the plugin installed, Vapor and regular components can be nested inside each other. The Vue notes warn that this covers props, events and slots in standard usage, but not every edge case yet. Their advice is to keep distinct regions in the app, each in one mode, and to avoid mixed nesting where you can.",[608,5055,5057],{"id":5056},"a-fully-vapor-app","A fully Vapor app",[16,5059,5060,5061,309],{},"If every component is Vapor, use ",[20,5062,5063],{},"createVaporApp()",[120,5065,5067],{"className":1131,"code":5066,"language":238,"meta":126,"style":126},"import { createVaporApp } from 'vue'\nimport App from '.\u002FApp.vue'\n\ncreateVaporApp(App).mount('#app')\n",[20,5068,5069,5084,5098,5102],{"__ignoreMap":126},[209,5070,5071,5073,5076,5078,5080,5082],{"class":211,"line":212},[209,5072,4848],{"class":219},[209,5074,5075],{"class":215}," { createVaporApp } ",[209,5077,1661],{"class":219},[209,5079,4856],{"class":233},[209,5081,205],{"class":237},[209,5083,4861],{"class":233},[209,5085,5086,5088,5090,5092,5094,5096],{"class":211,"line":246},[209,5087,4848],{"class":219},[209,5089,5008],{"class":215},[209,5091,1661],{"class":219},[209,5093,4856],{"class":233},[209,5095,5015],{"class":237},[209,5097,4861],{"class":233},[209,5099,5100],{"class":211,"line":264},[209,5101,268],{"emptyLinePlaceholder":267},[209,5103,5104,5107,5109,5111,5113,5115,5117,5119],{"class":211,"line":271},[209,5105,5106],{"class":257},"createVaporApp",[209,5108,5029],{"class":215},[209,5110,5038],{"class":257},[209,5112,362],{"class":215},[209,5114,5043],{"class":233},[209,5116,5046],{"class":237},[209,5118,5043],{"class":233},[209,5120,4884],{"class":215},[16,5122,5123,5124,5126,5127,5129],{},"The Virtual DOM runtime then stays out of the bundle. Each component still needs the ",[20,5125,4814],{}," marker. Components written with render functions or JSX remain Virtual DOM components and need interop. A Vapor app can also install ",[20,5128,4980],{}," to accept Virtual DOM components, but that pulls the runtime back in and cuts into the bundle savings.",[25,5131,5133],{"id":5132},"vapor-in-nuxt-46","Vapor in Nuxt 4.6",[16,5135,5136,5137,309],{},"Nuxt 4.6, released on October 5, 2026, supports Vapor in interop mode. The app root stays on the Virtual DOM, and you choose which components or pages use Vapor. To enable it, turn the option on in ",[20,5138,2747],{},[120,5140,5142],{"className":1131,"code":5141,"language":238,"meta":126,"style":126},"export default defineNuxtConfig({\n  vue: {\n    vapor: true,\n  },\n})\n",[20,5143,5144,5154,5163,5175,5179],{"__ignoreMap":126},[209,5145,5146,5148,5150,5152],{"class":211,"line":212},[209,5147,1145],{"class":219},[209,5149,2762],{"class":219},[209,5151,2765],{"class":257},[209,5153,434],{"class":215},[209,5155,5156,5159,5161],{"class":211,"line":246},[209,5157,5158],{"class":215},"  vue",[209,5160,309],{"class":219},[209,5162,2054],{"class":215},[209,5164,5165,5168,5170,5173],{"class":211,"line":264},[209,5166,5167],{"class":215},"    vapor",[209,5169,309],{"class":219},[209,5171,5172],{"class":445}," true",[209,5174,349],{"class":215},[209,5176,5177],{"class":211,"line":271},[209,5178,3245],{"class":215},[209,5180,5181],{"class":211,"line":296},[209,5182,5183],{"class":215},"})\n",[16,5185,5186,5187,5189,5190,5193,5194,5196],{},"Then mark components with ",[20,5188,4961],{},", as in the example above. Nuxt requires Vue ",[20,5191,5192],{},"^3.6.0-rc.2"," or newer. According to the release post, routing, ",[20,5195,582],{},", layouts and most built-in components keep working unchanged. The post recommends trying Vapor in a fresh project first.",[16,5198,5199],{},"The Nuxt docs list a few limitations of interop mode:",[918,5201,5202,5205,5212,5232,5239,5257],{},[921,5203,5204],{},"the app root can't be Vapor, because fully Vapor apps aren't available in Nuxt;",[921,5206,5207,5208,5211],{},"Vapor components don't expose ",[20,5209,5210],{},"$el"," on template refs;",[921,5213,5214,5217,5218,5221,5222,3341,5225,139,5228,5231],{},[20,5215,5216],{},"\u003CClientOnly>"," skips its attribute forwarding, and ",[20,5219,5220],{},"\u003CNuxtIsland>",", server components and the head components that read text children (",[20,5223,5224],{},"\u003CTitle>",[20,5226,5227],{},"\u003CStyle>",[20,5229,5230],{},"\u003CNoscript>",") warn if you pass them a Vapor slot;",[921,5233,5234,5235,5238],{},"keyed ",[20,5236,5237],{},"onPrehydrate"," falls back to the unkeyed form (and warns in development), because there is no component instance to attach the key to;",[921,5240,5241,139,5244,5247,5248,5251,5252,1786,5254,5256],{},[20,5242,5243],{},"asyncData",[20,5245,5246],{},"fetchKey"," through ",[20,5249,5250],{},"defineNuxtComponent"," don't work (use ",[20,5253,582],{},[20,5255,594],{},");",[921,5258,5259,5260,5263],{},"Nuxt composables called after an ",[20,5261,5262],{},"await"," may lose their context.",[16,5265,5266,5267,5270,5271,5274],{},"The same release post has performance numbers for Nuxt 4.6: ",[20,5268,5269],{},"nuxt build"," on a starter project went from 4.4 s to 3.6 s (18% faster), and a page with 300 ",[20,5272,5273],{},"\u003CNuxtLink>","s went from 110 to 140 req\u002Fs in SSR (+26%). Nuxt credits those gains to general optimizations in the release, not to Vapor.",[16,5276,5277],{},"If you're still on Nuxt 3, it reached end of life on July 31, 2026.",[25,5279,5281],{"id":5280},"what-vapor-doesnt-support-yet","What Vapor doesn't support yet",[16,5283,5284],{},"Vapor supports a subset of Vue's APIs. The official notes list what is unavailable or doesn't apply in Vapor components:",[918,5286,5287,5305,5310,5318,5324,5329],{},[921,5288,5289,5290,3341,5293,3341,5296,5299,5300,5304],{},"the Options API (",[20,5291,5292],{},"data",[20,5294,5295],{},"methods",[20,5297,5298],{},"mounted"," and so on). If you want a refresher on the difference, see ",[1798,5301,5303],{"href":5302},"\u002Fen\u002Fblog\u002Fwhat-is-the-difference-between-composition-api-and-options-api-in-vuejs","Composition API vs Options API",";",[921,5306,5307,5304],{},[20,5308,5309],{},"app.config.globalProperties",[921,5311,5312,5315,5316,5304],{},[20,5313,5314],{},"getCurrentInstance()",", which returns ",[20,5317,1115],{},[921,5319,5320,5321,5304],{},"per-element lifecycle events such as ",[20,5322,5323],{},"@vue:mounted",[921,5325,5326,5304],{},[20,5327,5328],{},"v-memo",[921,5330,5331,3341,5333,3341,5336,3341,5339,139,5342,5345],{},[20,5332,5210],{},[20,5334,5335],{},"$props",[20,5337,5338],{},"$attrs",[20,5340,5341],{},"$slots",[20,5343,5344],{},"$refs"," on component template refs.",[16,5347,5348,5349,5351,5352,5354],{},"Component libraries built on the Virtual DOM also deserve testing. The notes mention \"rough edges\" when using a Virtual DOM component library inside Vapor, without naming any library. If one of them relies on ",[20,5350,5314],{},", the ",[20,5353,1115],{}," above could be a problem, but only a test in your own project will tell.",[16,5356,5357,5358,5361],{},"The official notes don't list ",[20,5359,5360],{},"\u003CSuspense>"," or Vue DevTools as incompatible with Vapor, but that doesn't guarantee full support. The RC changelog records several fixes for Vapor and Suspense integration, so support is still being worked on. Test both in your own setup before deciding.",[16,5363,5364],{},"The same goes for SSR. The RC changelog has several hydration fixes for Vapor components, which suggests SSR is supported but still being tuned. If your app renders on the server, test hydration carefully.",[608,5366,5368,5369],{"id":5367},"events-and-delegate","Events and ",[20,5370,5371],{},".delegate",[16,5373,5374,5375,5378,5379,5382,5383,5385],{},"In earlier RCs, Vapor delegated events to ",[20,5376,5377],{},"document",", so a ",[20,5380,5381],{},"stopPropagation()"," on an ancestor could stop a child's handler. Since rc.2, listeners are attached directly to the element, as in regular Vue, and delegation became opt-in through the ",[20,5384,5371],{}," modifier:",[120,5387,5389],{"className":203,"code":5388,"language":205,"meta":126,"style":126},"\u003Cbutton @click.delegate=\"onClick\" \u002F>\n",[20,5390,5391],{"__ignoreMap":126},[209,5392,5393,5395,5397,5400,5403,5405,5408,5410,5412,5415,5417],{"class":211,"line":212},[209,5394,216],{"class":215},[209,5396,1622],{"class":219},[209,5398,5399],{"class":215}," @",[209,5401,5402],{"class":223},"click",[209,5404,702],{"class":215},[209,5406,5407],{"class":223},"delegate",[209,5409,230],{"class":219},[209,5411,234],{"class":233},[209,5413,5414],{"class":215},"onClick",[209,5416,234],{"class":233},[209,5418,879],{"class":215},[16,5420,3571,5421,5424],{},[20,5422,5423],{},"compilerOptions.eventDelegation"," option was removed.",[608,5426,5428],{"id":5427},"custom-directives","Custom directives",[16,5430,5431],{},"Custom directives also have a different interface in Vapor. Instead of an object with hooks, you write a function that receives the element and a reactive getter, and it can return a cleanup function:",[120,5433,5435],{"className":1131,"code":5434,"language":238,"meta":126,"style":126},"import { watchEffect } from 'vue'\n\nconst MyDirective = (el, source) => {\n  watchEffect(() => {\n    el.textContent = source()\n  })\n  return () => console.log('cleanup')\n}\n",[20,5436,5437,5452,5456,5482,5493,5505,5509,5535],{"__ignoreMap":126},[209,5438,5439,5441,5444,5446,5448,5450],{"class":211,"line":212},[209,5440,4848],{"class":219},[209,5442,5443],{"class":215}," { watchEffect } ",[209,5445,1661],{"class":219},[209,5447,4856],{"class":233},[209,5449,205],{"class":237},[209,5451,4861],{"class":233},[209,5453,5454],{"class":211,"line":246},[209,5455,268],{"emptyLinePlaceholder":267},[209,5457,5458,5460,5463,5466,5468,5471,5473,5476,5478,5480],{"class":211,"line":264},[209,5459,249],{"class":219},[209,5461,5462],{"class":257}," MyDirective",[209,5464,5465],{"class":219}," =",[209,5467,416],{"class":215},[209,5469,5470],{"class":668},"el",[209,5472,3341],{"class":215},[209,5474,5475],{"class":668},"source",[209,5477,672],{"class":215},[209,5479,287],{"class":219},[209,5481,2054],{"class":215},[209,5483,5484,5487,5489,5491],{"class":211,"line":271},[209,5485,5486],{"class":257},"  watchEffect",[209,5488,284],{"class":215},[209,5490,287],{"class":219},[209,5492,2054],{"class":215},[209,5494,5495,5498,5500,5503],{"class":211,"line":296},[209,5496,5497],{"class":215},"    el.textContent ",[209,5499,230],{"class":219},[209,5501,5502],{"class":257}," source",[209,5504,396],{"class":215},[209,5506,5507],{"class":211,"line":301},[209,5508,3149],{"class":215},[209,5510,5511,5513,5516,5518,5521,5524,5526,5528,5531,5533],{"class":211,"line":326},[209,5512,1236],{"class":219},[209,5514,5515],{"class":215}," () ",[209,5517,287],{"class":219},[209,5519,5520],{"class":215}," console.",[209,5522,5523],{"class":257},"log",[209,5525,362],{"class":215},[209,5527,5043],{"class":233},[209,5529,5530],{"class":237},"cleanup",[209,5532,5043],{"class":233},[209,5534,4884],{"class":215},[209,5536,5537],{"class":211,"line":352},[209,5538,489],{"class":215},[16,5540,5541,5542,5545,5546,5549,5550,702],{},"The full signature is ",[20,5543,5544],{},"(node, value?, argument?, modifiers?)",", where ",[20,5547,5548],{},"value"," is the reactive getter. For a refresher on the traditional model, see ",[1798,5551,5553],{"href":5552},"\u002Fen\u002Fblog\u002Fwhat-are-directives-in-vue-and-how-to-implement-them","what directives are in Vue",[25,5555,5557],{"id":5556},"where-vapor-helps","Where Vapor helps",[16,5559,5560],{},"The gain shows up where an app does a lot of DOM update work: dense lists and tables, components that change several times per second, and pages where initial JavaScript size matters. For an ordinary CRUD app, users will likely not notice the difference.",[16,5562,5563,5564,5567],{},"Vapor also doesn't fix problems that don't come from the renderer. Rendering 10,000 items in the DOM is slow in both modes, and the fix is to virtualize the list. Calling heavy functions straight from the template, like ",[20,5565,5566],{},"{{ calculateTotal(item) }}",", is still a bad idea in both modes.",[25,5569,5571],{"id":5570},"should-you-adopt-it-now","Should you adopt it now?",[16,5573,5574],{},"The Vue team says Vapor's feature set is complete in the RC and recommends two uses for now: applying Vapor to parts of an existing app, such as a performance-sensitive page, and building small new apps entirely in Vapor.",[16,5576,5577],{},"For a production app that already runs well on Vue 3.5, waiting for the stable release is the safer choice. For an isolated page where you've already measured a bottleneck, a trial in Nuxt 4.6 or in a Vue app with interop is a reasonable experiment. Before you start, check that:",[918,5579,5580,5583,5589,5592],{},[921,5581,5582],{},"you measured current performance (the browser's Performance panel works) and found a rendering bottleneck;",[921,5584,5585,5586,5588],{},"the component uses ",[20,5587,594],{}," or only a template;",[921,5590,5591],{},"the UI libraries it relies on work with interop in your project;",[921,5593,5594],{},"you can isolate Vapor to one route or component and back it out without pain.",[25,5596,5597],{"id":1702},"Frequently asked questions",[608,5599,5601],{"id":5600},"what-is-vapor-mode-in-vue","What is Vapor Mode in Vue?",[16,5603,5604],{},"It's an opt-in compilation mode for SFCs, available in the Vue 3.6 RC. Instead of building and diffing a Virtual DOM tree at runtime, the compiler generates code that updates the DOM directly.",[608,5606,5608],{"id":5607},"is-vue-36-stable-yet","Is Vue 3.6 stable yet?",[16,5610,5611,5612,5614,5615,702],{},"Not yet. As of October 6, 2026, the latest version is ",[20,5613,4744],{}," and the stable release is ",[20,5616,4748],{},[608,5618,5620],{"id":5619},"does-vapor-mode-replace-the-virtual-dom","Does Vapor Mode replace the Virtual DOM?",[16,5622,5623,5624,702],{},"No. It's optional and lives alongside the Virtual DOM in the same app through ",[20,5625,4980],{},[608,5627,5629],{"id":5628},"do-i-need-to-rewrite-my-app-to-use-vapor","Do I need to rewrite my app to use Vapor?",[16,5631,5632,5633,5635,5636,5638],{},"No. You can mark one component at a time with ",[20,5634,4814],{},". Components that use the Options API stay out, because Vapor only accepts ",[20,5637,594],{}," and templates.",[608,5640,5642],{"id":5641},"does-vapor-mode-work-with-the-options-api","Does Vapor Mode work with the Options API?",[16,5644,5645],{},"No. The official notes list the Options API as unsupported.",[608,5647,5649],{"id":5648},"does-vapor-mode-work-with-nuxt","Does Vapor Mode work with Nuxt?",[16,5651,5652,5653,1786,5656,5658,5659,5661],{},"Yes, since Nuxt 4.6, in interop mode: set ",[20,5654,5655],{},"vue: { vapor: true }",[20,5657,2747],{}," and mark components with ",[20,5660,4961],{},". The app root stays on the Virtual DOM.",[608,5663,5665],{"id":5664},"is-vapor-mode-faster-than-the-virtual-dom","Is Vapor Mode faster than the Virtual DOM?",[16,5667,5668],{},"According to the Vue 3.6 notes, Vapor reached Solid and Svelte 5 levels in third-party benchmarks. For most apps the perceived gain is small, and the real effect on your project only shows up when you measure it.",[1805,5670,5671],{},"html pre.shiki code .sCdxs, html code.shiki .sCdxs{--shiki-default:#F8F8F2}html pre.shiki code .s0Tla, html code.shiki .s0Tla{--shiki-default:#FF79C6}html pre.shiki code .sY_PY, html code.shiki .sY_PY{--shiki-default:#50FA7B;--shiki-default-font-style:italic}html pre.shiki code .seVfx, html code.shiki .seVfx{--shiki-default:#E9F284}html pre.shiki code .s-mGx, html code.shiki .s-mGx{--shiki-default:#F1FA8C}html pre.shiki code .sAOxA, html code.shiki .sAOxA{--shiki-default:#50FA7B}html pre.shiki code .sIQBb, html code.shiki .sIQBb{--shiki-default:#BD93F9}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 pre.shiki code .sGEwX, html code.shiki .sGEwX{--shiki-default:#FFB86C;--shiki-default-font-style:italic}",{"title":126,"searchDepth":246,"depth":246,"links":5673},[5674,5677,5681,5682,5687,5688,5689],{"id":4762,"depth":246,"text":4763,"children":5675},[5676],{"id":4775,"depth":264,"text":4776},{"id":4804,"depth":246,"text":4805,"children":5678},[5679,5680],{"id":4969,"depth":264,"text":4970},{"id":5056,"depth":264,"text":5057},{"id":5132,"depth":246,"text":5133},{"id":5280,"depth":246,"text":5281,"children":5683},[5684,5686],{"id":5367,"depth":264,"text":5685},"Events and .delegate",{"id":5427,"depth":264,"text":5428},{"id":5556,"depth":246,"text":5557},{"id":5570,"depth":246,"text":5571},{"id":1702,"depth":246,"text":5597,"children":5690},[5691,5692,5693,5694,5695,5696,5697],{"id":5600,"depth":264,"text":5601},{"id":5607,"depth":264,"text":5608},{"id":5619,"depth":264,"text":5620},{"id":5628,"depth":264,"text":5629},{"id":5641,"depth":264,"text":5642},{"id":5648,"depth":264,"text":5649},{"id":5664,"depth":264,"text":5665},[1835,1834],"What Vue Vapor Mode is, how to turn it on in Vue 3.6 (RC) and Nuxt 4.6, what it doesn't support yet, and when it's worth trying, with code examples and an FAQ.","vue-vapor-mode-what-it-is-and-how-to-use-it",{},"\u002Fen\u002Fblog\u002Fvue-vapor-mode-what-it-is-and-how-to-use-it","vue-vapor-mode-como-funciona-e-como-usar",{"title":4735,"description":5699},{"loc":5702,"lastmod":1844},"en\u002Fblog\u002Fvue-vapor-mode-what-it-is-and-how-to-use-it",1612,"t7xqEIHrQ1NF0PTd4QIeo_dO6I9PgndCWb5gfcF3uhE",1791682337630]