[{"data":1,"prerenderedAt":9277},["ShallowReactive",2],{"en-vuejs-category-articles":3},[4,1848,2436,3877,4560,5536,8273],{"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":5525,"description":5526,"enAlternateSlug":5527,"extension":1837,"meta":5528,"navigation":267,"path":5529,"ptBrAlternateSlug":5530,"publishedAt":1844,"seo":5531,"sitemap":5532,"stem":5533,"updatedAt":1844,"wordCount":5534,"__hash__":5535},"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":4565,"toc":5499},[4566,4581,4587,4591,4594,4597,4600,4604,4629,4633,4643,4772,4779,4794,4798,4809,4878,4881,4885,4891,4948,4957,4961,4966,5011,5024,5027,5091,5102,5105,5109,5112,5173,5182,5189,5192,5199,5213,5246,5252,5256,5259,5366,5381,5385,5388,5395,5399,5402,5405,5422,5425,5429,5432,5436,5444,5448,5453,5457,5466,5470,5473,5477,5489,5493,5496],[16,4567,4568,4569,4572,4573,4576,4577,4580],{},"Vue 3.6 is not stable yet. As of October 6, 2026, the latest version is ",[20,4570,4571],{},"3.6.0-rc.10"," (September 30), and the stable release is still ",[20,4574,4575],{},"3.5.43"," (September 17). I checked both on npm and in the ",[20,4578,4579],{},"vuejs\u002Fcore"," releases on that date.",[16,4582,4583,4586],{},[3891,4584,4585],{},"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,4588,4590],{"id":4589},"how-vapor-works","How Vapor works",[16,4592,4593],{},"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,4595,4596],{},"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,4598,4599],{},"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,4601,4603],{"id":4602},"what-about-alien-signals","What about Alien Signals?",[16,4605,4606,4607,4610,4611,4616,4617,3341,4620,3341,4623,139,4625,4628],{},"Vue 3.6 also ships a major refactor of ",[20,4608,4609],{},"@vue\u002Freactivity"," based on the ",[1798,4612,4615],{"href":4613,"rel":4614},"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,4618,4619],{},"ref",[20,4621,4622],{},"reactive",[20,4624,578],{},[20,4626,4627],{},"watch"," keep the same API. Since it's part of Vue 3.6, you get it whether or not you use Vapor.",[25,4630,4632],{"id":4631},"how-to-enable-vapor-mode","How to enable Vapor Mode",[16,4634,4635,4636,4638,4639,4642],{},"Vapor works with SFCs that use ",[20,4637,594],{}," or only a template. You mark the component with the ",[20,4640,4641],{},"vapor"," attribute:",[120,4644,4646],{"className":203,"code":4645,"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,4647,4648,4671,4689,4693,4712,4720,4724,4732,4751,4756,4764],{"__ignoreMap":126},[209,4649,4650,4652,4654,4656,4659,4661,4663,4665,4667,4669],{"class":211,"line":212},[209,4651,216],{"class":215},[209,4653,220],{"class":219},[209,4655,224],{"class":223},[209,4657,4658],{"class":223}," vapor",[209,4660,227],{"class":223},[209,4662,230],{"class":219},[209,4664,234],{"class":233},[209,4666,238],{"class":237},[209,4668,234],{"class":233},[209,4670,243],{"class":215},[209,4672,4673,4676,4679,4681,4684,4686],{"class":211,"line":246},[209,4674,4675],{"class":219},"import",[209,4677,4678],{"class":215}," { ref } ",[209,4680,1661],{"class":219},[209,4682,4683],{"class":233}," '",[209,4685,205],{"class":237},[209,4687,4688],{"class":233},"'\n",[209,4690,4691],{"class":211,"line":264},[209,4692,268],{"emptyLinePlaceholder":267},[209,4694,4695,4697,4700,4702,4705,4707,4709],{"class":211,"line":271},[209,4696,249],{"class":219},[209,4698,4699],{"class":215}," count ",[209,4701,230],{"class":219},[209,4703,4704],{"class":257}," ref",[209,4706,362],{"class":215},[209,4708,1223],{"class":445},[209,4710,4711],{"class":215},")\n",[209,4713,4714,4716,4718],{"class":211,"line":296},[209,4715,495],{"class":215},[209,4717,220],{"class":219},[209,4719,243],{"class":215},[209,4721,4722],{"class":211,"line":301},[209,4723,268],{"emptyLinePlaceholder":267},[209,4725,4726,4728,4730],{"class":211,"line":326},[209,4727,216],{"class":215},[209,4729,512],{"class":219},[209,4731,243],{"class":215},[209,4733,4734,4736,4738,4740,4742,4744,4747,4749],{"class":211,"line":352},[209,4735,520],{"class":215},[209,4737,1622],{"class":219},[209,4739,1625],{"class":223},[209,4741,230],{"class":219},[209,4743,234],{"class":233},[209,4745,4746],{"class":237},"count++",[209,4748,234],{"class":233},[209,4750,243],{"class":215},[209,4752,4753],{"class":211,"line":399},[209,4754,4755],{"class":215},"    count is {{ count }}\n",[209,4757,4758,4760,4762],{"class":211,"line":405},[209,4759,559],{"class":215},[209,4761,1622],{"class":219},[209,4763,243],{"class":215},[209,4765,4766,4768,4770],{"class":211,"line":410},[209,4767,495],{"class":215},[209,4769,512],{"class":219},[209,4771,243],{"class":215},[16,4773,4774,4775,4778],{},"Outside Nuxt, ",[20,4776,4777],{},"@vitejs\u002Fplugin-vue"," compiles Vapor SFCs (the plugin's changelog records Vapor component support, including template-only ones), so use a recent version.",[16,4780,4781,4782,4785,4786,4789,4790,4793],{},"Vue also accepts ",[20,4783,4784],{},"\u003Cscript vapor>"," as shorthand for ",[20,4787,4788],{},"\u003Cscript setup vapor>",". You can instead put the marker on the template with ",[20,4791,4792],{},"\u003Ctemplate vapor>"," to compile the whole SFC in Vapor mode.",[608,4795,4797],{"id":4796},"vapor-components-in-an-existing-app","Vapor components in an existing app",[16,4799,4800,4801,4804,4805,4808],{},"In an app created with ",[20,4802,4803],{},"createApp()",", you need to install ",[20,4806,4807],{},"vaporInteropPlugin"," to use Vapor components:",[120,4810,4812],{"className":1131,"code":4811,"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,4813,4814,4829,4845,4849],{"__ignoreMap":126},[209,4815,4816,4818,4821,4823,4825,4827],{"class":211,"line":212},[209,4817,4675],{"class":219},[209,4819,4820],{"class":215}," { createApp, vaporInteropPlugin } ",[209,4822,1661],{"class":219},[209,4824,4683],{"class":233},[209,4826,205],{"class":237},[209,4828,4688],{"class":233},[209,4830,4831,4833,4836,4838,4840,4843],{"class":211,"line":246},[209,4832,4675],{"class":219},[209,4834,4835],{"class":215}," App ",[209,4837,1661],{"class":219},[209,4839,4683],{"class":233},[209,4841,4842],{"class":237},".\u002FApp.vue",[209,4844,4688],{"class":233},[209,4846,4847],{"class":211,"line":264},[209,4848,268],{"emptyLinePlaceholder":267},[209,4850,4851,4854,4857,4860,4863,4866,4868,4871,4874,4876],{"class":211,"line":271},[209,4852,4853],{"class":257},"createApp",[209,4855,4856],{"class":215},"(App).",[209,4858,4859],{"class":257},"use",[209,4861,4862],{"class":215},"(vaporInteropPlugin).",[209,4864,4865],{"class":257},"mount",[209,4867,362],{"class":215},[209,4869,4870],{"class":233},"'",[209,4872,4873],{"class":237},"#app",[209,4875,4870],{"class":233},[209,4877,4711],{"class":215},[16,4879,4880],{},"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,4882,4884],{"id":4883},"a-fully-vapor-app","A fully Vapor app",[16,4886,4887,4888,309],{},"If every component is Vapor, use ",[20,4889,4890],{},"createVaporApp()",[120,4892,4894],{"className":1131,"code":4893,"language":238,"meta":126,"style":126},"import { createVaporApp } from 'vue'\nimport App from '.\u002FApp.vue'\n\ncreateVaporApp(App).mount('#app')\n",[20,4895,4896,4911,4925,4929],{"__ignoreMap":126},[209,4897,4898,4900,4903,4905,4907,4909],{"class":211,"line":212},[209,4899,4675],{"class":219},[209,4901,4902],{"class":215}," { createVaporApp } ",[209,4904,1661],{"class":219},[209,4906,4683],{"class":233},[209,4908,205],{"class":237},[209,4910,4688],{"class":233},[209,4912,4913,4915,4917,4919,4921,4923],{"class":211,"line":246},[209,4914,4675],{"class":219},[209,4916,4835],{"class":215},[209,4918,1661],{"class":219},[209,4920,4683],{"class":233},[209,4922,4842],{"class":237},[209,4924,4688],{"class":233},[209,4926,4927],{"class":211,"line":264},[209,4928,268],{"emptyLinePlaceholder":267},[209,4930,4931,4934,4936,4938,4940,4942,4944,4946],{"class":211,"line":271},[209,4932,4933],{"class":257},"createVaporApp",[209,4935,4856],{"class":215},[209,4937,4865],{"class":257},[209,4939,362],{"class":215},[209,4941,4870],{"class":233},[209,4943,4873],{"class":237},[209,4945,4870],{"class":233},[209,4947,4711],{"class":215},[16,4949,4950,4951,4953,4954,4956],{},"The Virtual DOM runtime then stays out of the bundle. Each component still needs the ",[20,4952,4641],{}," marker. Components written with render functions or JSX remain Virtual DOM components and need interop. A Vapor app can also install ",[20,4955,4807],{}," to accept Virtual DOM components, but that pulls the runtime back in and cuts into the bundle savings.",[25,4958,4960],{"id":4959},"vapor-in-nuxt-46","Vapor in Nuxt 4.6",[16,4962,4963,4964,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,4965,2747],{},[120,4967,4969],{"className":1131,"code":4968,"language":238,"meta":126,"style":126},"export default defineNuxtConfig({\n  vue: {\n    vapor: true,\n  },\n})\n",[20,4970,4971,4981,4990,5002,5006],{"__ignoreMap":126},[209,4972,4973,4975,4977,4979],{"class":211,"line":212},[209,4974,1145],{"class":219},[209,4976,2762],{"class":219},[209,4978,2765],{"class":257},[209,4980,434],{"class":215},[209,4982,4983,4986,4988],{"class":211,"line":246},[209,4984,4985],{"class":215},"  vue",[209,4987,309],{"class":219},[209,4989,2054],{"class":215},[209,4991,4992,4995,4997,5000],{"class":211,"line":264},[209,4993,4994],{"class":215},"    vapor",[209,4996,309],{"class":219},[209,4998,4999],{"class":445}," true",[209,5001,349],{"class":215},[209,5003,5004],{"class":211,"line":271},[209,5005,3245],{"class":215},[209,5007,5008],{"class":211,"line":296},[209,5009,5010],{"class":215},"})\n",[16,5012,5013,5014,5016,5017,5020,5021,5023],{},"Then mark components with ",[20,5015,4788],{},", as in the example above. Nuxt requires Vue ",[20,5018,5019],{},"^3.6.0-rc.2"," or newer. According to the release post, routing, ",[20,5022,582],{},", layouts and most built-in components keep working unchanged. The post recommends trying Vapor in a fresh project first.",[16,5025,5026],{},"The Nuxt docs list a few limitations of interop mode:",[918,5028,5029,5032,5039,5059,5066,5084],{},[921,5030,5031],{},"the app root can't be Vapor, because fully Vapor apps aren't available in Nuxt;",[921,5033,5034,5035,5038],{},"Vapor components don't expose ",[20,5036,5037],{},"$el"," on template refs;",[921,5040,5041,5044,5045,5048,5049,3341,5052,139,5055,5058],{},[20,5042,5043],{},"\u003CClientOnly>"," skips its attribute forwarding, and ",[20,5046,5047],{},"\u003CNuxtIsland>",", server components and the head components that read text children (",[20,5050,5051],{},"\u003CTitle>",[20,5053,5054],{},"\u003CStyle>",[20,5056,5057],{},"\u003CNoscript>",") warn if you pass them a Vapor slot;",[921,5060,5061,5062,5065],{},"keyed ",[20,5063,5064],{},"onPrehydrate"," falls back to the unkeyed form (and warns in development), because there is no component instance to attach the key to;",[921,5067,5068,139,5071,5074,5075,5078,5079,1786,5081,5083],{},[20,5069,5070],{},"asyncData",[20,5072,5073],{},"fetchKey"," through ",[20,5076,5077],{},"defineNuxtComponent"," don't work (use ",[20,5080,582],{},[20,5082,594],{},");",[921,5085,5086,5087,5090],{},"Nuxt composables called after an ",[20,5088,5089],{},"await"," may lose their context.",[16,5092,5093,5094,5097,5098,5101],{},"The same release post has performance numbers for Nuxt 4.6: ",[20,5095,5096],{},"nuxt build"," on a starter project went from 4.4 s to 3.6 s (18% faster), and a page with 300 ",[20,5099,5100],{},"\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,5103,5104],{},"If you're still on Nuxt 3, it reached end of life on July 31, 2026.",[25,5106,5108],{"id":5107},"what-vapor-doesnt-support-yet","What Vapor doesn't support yet",[16,5110,5111],{},"Vapor supports a subset of Vue's APIs. The official notes list what is unavailable or doesn't apply in Vapor components:",[918,5113,5114,5132,5137,5145,5151,5156],{},[921,5115,5116,5117,3341,5120,3341,5123,5126,5127,5131],{},"the Options API (",[20,5118,5119],{},"data",[20,5121,5122],{},"methods",[20,5124,5125],{},"mounted"," and so on). If you want a refresher on the difference, see ",[1798,5128,5130],{"href":5129},"\u002Fen\u002Fblog\u002Fwhat-is-the-difference-between-composition-api-and-options-api-in-vuejs","Composition API vs Options API",";",[921,5133,5134,5131],{},[20,5135,5136],{},"app.config.globalProperties",[921,5138,5139,5142,5143,5131],{},[20,5140,5141],{},"getCurrentInstance()",", which returns ",[20,5144,1115],{},[921,5146,5147,5148,5131],{},"per-element lifecycle events such as ",[20,5149,5150],{},"@vue:mounted",[921,5152,5153,5131],{},[20,5154,5155],{},"v-memo",[921,5157,5158,3341,5160,3341,5163,3341,5166,139,5169,5172],{},[20,5159,5037],{},[20,5161,5162],{},"$props",[20,5164,5165],{},"$attrs",[20,5167,5168],{},"$slots",[20,5170,5171],{},"$refs"," on component template refs.",[16,5174,5175,5176,5178,5179,5181],{},"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,5177,5141],{},", the ",[20,5180,1115],{}," above could be a problem, but only a test in your own project will tell.",[16,5183,5184,5185,5188],{},"The official notes don't list ",[20,5186,5187],{},"\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,5190,5191],{},"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,5193,5195,5196],{"id":5194},"events-and-delegate","Events and ",[20,5197,5198],{},".delegate",[16,5200,5201,5202,5205,5206,5209,5210,5212],{},"In earlier RCs, Vapor delegated events to ",[20,5203,5204],{},"document",", so a ",[20,5207,5208],{},"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,5211,5198],{}," modifier:",[120,5214,5216],{"className":203,"code":5215,"language":205,"meta":126,"style":126},"\u003Cbutton @click.delegate=\"onClick\" \u002F>\n",[20,5217,5218],{"__ignoreMap":126},[209,5219,5220,5222,5224,5227,5230,5232,5235,5237,5239,5242,5244],{"class":211,"line":212},[209,5221,216],{"class":215},[209,5223,1622],{"class":219},[209,5225,5226],{"class":215}," @",[209,5228,5229],{"class":223},"click",[209,5231,702],{"class":215},[209,5233,5234],{"class":223},"delegate",[209,5236,230],{"class":219},[209,5238,234],{"class":233},[209,5240,5241],{"class":215},"onClick",[209,5243,234],{"class":233},[209,5245,879],{"class":215},[16,5247,3571,5248,5251],{},[20,5249,5250],{},"compilerOptions.eventDelegation"," option was removed.",[608,5253,5255],{"id":5254},"custom-directives","Custom directives",[16,5257,5258],{},"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,5260,5262],{"className":1131,"code":5261,"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,5263,5264,5279,5283,5309,5320,5332,5336,5362],{"__ignoreMap":126},[209,5265,5266,5268,5271,5273,5275,5277],{"class":211,"line":212},[209,5267,4675],{"class":219},[209,5269,5270],{"class":215}," { watchEffect } ",[209,5272,1661],{"class":219},[209,5274,4683],{"class":233},[209,5276,205],{"class":237},[209,5278,4688],{"class":233},[209,5280,5281],{"class":211,"line":246},[209,5282,268],{"emptyLinePlaceholder":267},[209,5284,5285,5287,5290,5293,5295,5298,5300,5303,5305,5307],{"class":211,"line":264},[209,5286,249],{"class":219},[209,5288,5289],{"class":257}," MyDirective",[209,5291,5292],{"class":219}," =",[209,5294,416],{"class":215},[209,5296,5297],{"class":668},"el",[209,5299,3341],{"class":215},[209,5301,5302],{"class":668},"source",[209,5304,672],{"class":215},[209,5306,287],{"class":219},[209,5308,2054],{"class":215},[209,5310,5311,5314,5316,5318],{"class":211,"line":271},[209,5312,5313],{"class":257},"  watchEffect",[209,5315,284],{"class":215},[209,5317,287],{"class":219},[209,5319,2054],{"class":215},[209,5321,5322,5325,5327,5330],{"class":211,"line":296},[209,5323,5324],{"class":215},"    el.textContent ",[209,5326,230],{"class":219},[209,5328,5329],{"class":257}," source",[209,5331,396],{"class":215},[209,5333,5334],{"class":211,"line":301},[209,5335,3149],{"class":215},[209,5337,5338,5340,5343,5345,5348,5351,5353,5355,5358,5360],{"class":211,"line":326},[209,5339,1236],{"class":219},[209,5341,5342],{"class":215}," () ",[209,5344,287],{"class":219},[209,5346,5347],{"class":215}," console.",[209,5349,5350],{"class":257},"log",[209,5352,362],{"class":215},[209,5354,4870],{"class":233},[209,5356,5357],{"class":237},"cleanup",[209,5359,4870],{"class":233},[209,5361,4711],{"class":215},[209,5363,5364],{"class":211,"line":352},[209,5365,489],{"class":215},[16,5367,5368,5369,5372,5373,5376,5377,702],{},"The full signature is ",[20,5370,5371],{},"(node, value?, argument?, modifiers?)",", where ",[20,5374,5375],{},"value"," is the reactive getter. For a refresher on the traditional model, see ",[1798,5378,5380],{"href":5379},"\u002Fen\u002Fblog\u002Fwhat-are-directives-in-vue-and-how-to-implement-them","what directives are in Vue",[25,5382,5384],{"id":5383},"where-vapor-helps","Where Vapor helps",[16,5386,5387],{},"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,5389,5390,5391,5394],{},"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,5392,5393],{},"{{ calculateTotal(item) }}",", is still a bad idea in both modes.",[25,5396,5398],{"id":5397},"should-you-adopt-it-now","Should you adopt it now?",[16,5400,5401],{},"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,5403,5404],{},"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,5406,5407,5410,5416,5419],{},[921,5408,5409],{},"you measured current performance (the browser's Performance panel works) and found a rendering bottleneck;",[921,5411,5412,5413,5415],{},"the component uses ",[20,5414,594],{}," or only a template;",[921,5417,5418],{},"the UI libraries it relies on work with interop in your project;",[921,5420,5421],{},"you can isolate Vapor to one route or component and back it out without pain.",[25,5423,5424],{"id":1702},"Frequently asked questions",[608,5426,5428],{"id":5427},"what-is-vapor-mode-in-vue","What is Vapor Mode in Vue?",[16,5430,5431],{},"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,5433,5435],{"id":5434},"is-vue-36-stable-yet","Is Vue 3.6 stable yet?",[16,5437,5438,5439,5441,5442,702],{},"Not yet. As of October 6, 2026, the latest version is ",[20,5440,4571],{}," and the stable release is ",[20,5443,4575],{},[608,5445,5447],{"id":5446},"does-vapor-mode-replace-the-virtual-dom","Does Vapor Mode replace the Virtual DOM?",[16,5449,5450,5451,702],{},"No. It's optional and lives alongside the Virtual DOM in the same app through ",[20,5452,4807],{},[608,5454,5456],{"id":5455},"do-i-need-to-rewrite-my-app-to-use-vapor","Do I need to rewrite my app to use Vapor?",[16,5458,5459,5460,5462,5463,5465],{},"No. You can mark one component at a time with ",[20,5461,4641],{},". Components that use the Options API stay out, because Vapor only accepts ",[20,5464,594],{}," and templates.",[608,5467,5469],{"id":5468},"does-vapor-mode-work-with-the-options-api","Does Vapor Mode work with the Options API?",[16,5471,5472],{},"No. The official notes list the Options API as unsupported.",[608,5474,5476],{"id":5475},"does-vapor-mode-work-with-nuxt","Does Vapor Mode work with Nuxt?",[16,5478,5479,5480,1786,5483,5485,5486,5488],{},"Yes, since Nuxt 4.6, in interop mode: set ",[20,5481,5482],{},"vue: { vapor: true }",[20,5484,2747],{}," and mark components with ",[20,5487,4788],{},". The app root stays on the Virtual DOM.",[608,5490,5492],{"id":5491},"is-vapor-mode-faster-than-the-virtual-dom","Is Vapor Mode faster than the Virtual DOM?",[16,5494,5495],{},"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,5497,5498],{},"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":5500},[5501,5504,5508,5509,5514,5515,5516],{"id":4589,"depth":246,"text":4590,"children":5502},[5503],{"id":4602,"depth":264,"text":4603},{"id":4631,"depth":246,"text":4632,"children":5505},[5506,5507],{"id":4796,"depth":264,"text":4797},{"id":4883,"depth":264,"text":4884},{"id":4959,"depth":246,"text":4960},{"id":5107,"depth":246,"text":5108,"children":5510},[5511,5513],{"id":5194,"depth":264,"text":5512},"Events and .delegate",{"id":5254,"depth":264,"text":5255},{"id":5383,"depth":246,"text":5384},{"id":5397,"depth":246,"text":5398},{"id":1702,"depth":246,"text":5424,"children":5517},[5518,5519,5520,5521,5522,5523,5524],{"id":5427,"depth":264,"text":5428},{"id":5434,"depth":264,"text":5435},{"id":5446,"depth":264,"text":5447},{"id":5455,"depth":264,"text":5456},{"id":5468,"depth":264,"text":5469},{"id":5475,"depth":264,"text":5476},{"id":5491,"depth":264,"text":5492},[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":4562,"description":5526},{"loc":5529,"lastmod":1844},"en\u002Fblog\u002Fvue-vapor-mode-what-it-is-and-how-to-use-it",1612,"t7xqEIHrQ1NF0PTd4QIeo_dO6I9PgndCWb5gfcF3uhE",{"id":5537,"title":5538,"articleCover":5539,"articleCoverEmbed":8,"articleCoverEmbedUploadDate":8,"articleCoverHeight":5540,"articleCoverWidth":5541,"authorId":5542,"body":5543,"categoriesIds":8262,"description":8263,"enAlternateSlug":8264,"extension":1837,"meta":8265,"navigation":267,"path":5379,"ptBrAlternateSlug":8266,"publishedAt":8267,"seo":8268,"sitemap":8269,"stem":8270,"updatedAt":1844,"wordCount":8271,"__hash__":8272},"blogEn\u002Fen\u002Fblog\u002Fwhat-are-directives-in-vue-and-how-to-implement-them.md","Vue 3 Directives: Built-in and Custom","\u002Fimages\u002Fen\u002Fblog\u002Fvue\u002Fwhat-are-directives-in-vue-and-how-to-implement-them.webp",772,1372,"eduardo-goncalves-souza",{"type":13,"value":5544,"toc":8227},[5545,5559,5564,5568,5571,5580,5584,5598,5761,5787,5791,5795,5801,5908,5912,5917,6045,6058,6062,6067,6232,6236,6245,6434,6441,6445,6457,6628,6632,6641,6685,6689,6703,6711,6715,6718,6752,6756,6767,6776,6793,6797,6800,6804,6821,6914,6920,6924,6930,7014,7133,7137,7140,7183,7232,7236,7243,7377,7398,7402,7411,7694,7710,7714,7717,7720,7724,7739,7842,7860,7875,7947,7996,8000,8003,8051,8066,8070,8109,8111,8115,8121,8125,8142,8146,8149,8153,8165,8169,8172,8176,8188,8190,8201,8224],[16,5546,5547,5548,5551,5552,139,5555,5558],{},"Directives are special template attributes, always prefixed with ",[20,5549,5550],{},"v-",", that connect your HTML to the state and behavior of your application. This article covers the built-in Vue 3 directives, the syntax with arguments and modifiers, how to choose between ",[20,5553,5554],{},"v-if",[20,5556,5557],{},"v-show",", and how to build your own directive, including in Nuxt 4, where you also have to handle server-side rendering.",[16,5560,5561,5562,702],{},"The examples use Vue 3.5 with ",[20,5563,594],{},[25,5565,5567],{"id":5566},"what-are-directives-in-vue","What Are Directives in Vue?",[16,5569,5570],{},"A directive takes a JavaScript expression as its value and applies reactive behavior to the element it sits on. When the value changes, Vue updates the element. Directives control attributes, events, list rendering and which elements appear on screen.",[16,5572,5573,5574,139,5576,5579],{},"There are two groups. Built-in directives, like ",[20,5575,5554],{},[20,5577,5578],{},"v-for",", ship with Vue. Custom directives are the ones you write yourself for cases where you need to work directly with a DOM element.",[25,5581,5583],{"id":5582},"how-does-the-directive-syntax-work","How Does the Directive Syntax Work?",[16,5585,5586,5587,5589,5590,139,5593,5589,5595,309],{},"A directive has up to four parts: the name, an optional argument after a colon, optional modifiers separated by dots, and the value. The first two lines below are equivalent, because ",[20,5588,309],{}," is shorthand for ",[20,5591,5592],{},"v-bind:",[20,5594,1865],{},[20,5596,5597],{},"v-on:",[120,5599,5601],{"className":203,"code":5600,"language":205,"meta":126,"style":126},"\u003Ctemplate>\n  \u003Ca v-bind:href=\"url\">Visit our website\u003C\u002Fa>\n  \u003Ca :href=\"url\">Visit our website\u003C\u002Fa>\n\n  \u003Cbutton @click=\"save\">Save\u003C\u002Fbutton>\n\n  \u003C!-- dynamic argument: the attribute name comes from a variable -->\n  \u003Ca :[attribute]=\"url\">Visit our website\u003C\u002Fa>\n\n  \u003C!-- modifier: calls event.preventDefault() on submit -->\n  \u003Cform @submit.prevent=\"submit\">...\u003C\u002Fform>\n\u003C\u002Ftemplate>\n",[20,5602,5603,5611,5635,5658,5662,5686,5690,5695,5718,5722,5727,5753],{"__ignoreMap":126},[209,5604,5605,5607,5609],{"class":211,"line":212},[209,5606,216],{"class":215},[209,5608,512],{"class":219},[209,5610,243],{"class":215},[209,5612,5613,5615,5617,5620,5622,5624,5626,5628,5631,5633],{"class":211,"line":246},[209,5614,520],{"class":215},[209,5616,1798],{"class":219},[209,5618,5619],{"class":223}," v-bind:href",[209,5621,230],{"class":219},[209,5623,234],{"class":233},[209,5625,2082],{"class":237},[209,5627,234],{"class":233},[209,5629,5630],{"class":215},">Visit our website\u003C\u002F",[209,5632,1798],{"class":219},[209,5634,243],{"class":215},[209,5636,5637,5639,5641,5644,5646,5648,5650,5652,5654,5656],{"class":211,"line":264},[209,5638,520],{"class":215},[209,5640,1798],{"class":219},[209,5642,5643],{"class":223}," :href",[209,5645,230],{"class":219},[209,5647,234],{"class":233},[209,5649,2082],{"class":237},[209,5651,234],{"class":233},[209,5653,5630],{"class":215},[209,5655,1798],{"class":219},[209,5657,243],{"class":215},[209,5659,5660],{"class":211,"line":271},[209,5661,268],{"emptyLinePlaceholder":267},[209,5663,5664,5666,5668,5670,5672,5674,5677,5679,5682,5684],{"class":211,"line":296},[209,5665,520],{"class":215},[209,5667,1622],{"class":219},[209,5669,1625],{"class":223},[209,5671,230],{"class":219},[209,5673,234],{"class":233},[209,5675,5676],{"class":237},"save",[209,5678,234],{"class":233},[209,5680,5681],{"class":215},">Save\u003C\u002F",[209,5683,1622],{"class":219},[209,5685,243],{"class":215},[209,5687,5688],{"class":211,"line":301},[209,5689,268],{"emptyLinePlaceholder":267},[209,5691,5692],{"class":211,"line":326},[209,5693,5694],{"class":1139},"  \u003C!-- dynamic argument: the attribute name comes from a variable -->\n",[209,5696,5697,5699,5701,5704,5706,5708,5710,5712,5714,5716],{"class":211,"line":352},[209,5698,520],{"class":215},[209,5700,1798],{"class":219},[209,5702,5703],{"class":223}," :[attribute]",[209,5705,230],{"class":219},[209,5707,234],{"class":233},[209,5709,2082],{"class":237},[209,5711,234],{"class":233},[209,5713,5630],{"class":215},[209,5715,1798],{"class":219},[209,5717,243],{"class":215},[209,5719,5720],{"class":211,"line":399},[209,5721,268],{"emptyLinePlaceholder":267},[209,5723,5724],{"class":211,"line":405},[209,5725,5726],{"class":1139},"  \u003C!-- modifier: calls event.preventDefault() on submit -->\n",[209,5728,5729,5731,5734,5737,5739,5741,5744,5746,5749,5751],{"class":211,"line":410},[209,5730,520],{"class":215},[209,5732,5733],{"class":219},"form",[209,5735,5736],{"class":223}," @submit.prevent",[209,5738,230],{"class":219},[209,5740,234],{"class":233},[209,5742,5743],{"class":237},"submit",[209,5745,234],{"class":233},[209,5747,5748],{"class":215},">...\u003C\u002F",[209,5750,5733],{"class":219},[209,5752,243],{"class":215},[209,5754,5755,5757,5759],{"class":211,"line":425},[209,5756,495],{"class":215},[209,5758,512],{"class":219},[209,5760,243],{"class":215},[16,5762,5763,5764,139,5767,5770,5771,5774,5775,3341,5778,139,5781,5770,5784,702],{},"In Vue projects, the short form is the most common. Modifiers change how a directive behaves, and each directive has its own. ",[20,5765,5766],{},".prevent",[20,5768,5769],{},".stop"," apply to ",[20,5772,5773],{},"v-on",", while ",[20,5776,5777],{},".lazy",[20,5779,5780],{},".number",[20,5782,5783],{},".trim",[20,5785,5786],{},"v-model",[25,5788,5790],{"id":5789},"what-are-the-main-built-in-directives","What Are the Main Built-in Directives?",[608,5792,5794],{"id":5793},"v-bind-dynamic-attributes","v-bind: Dynamic Attributes",[16,5796,5797,5800],{},[20,5798,5799],{},"v-bind"," links the value of a variable to an element attribute. If the variable changes, the attribute changes with it:",[120,5802,5804],{"className":203,"code":5803,"language":205,"meta":126,"style":126},"\u003Ctemplate>\n  \u003Ca :href=\"url\">Visit our website\u003C\u002Fa>\n\u003C\u002Ftemplate>\n\n\u003Cscript setup>\nimport { ref } from \"vue\";\n\nconst url = ref(\"https:\u002F\u002Fcodigoaoponto.com\u002F\");\n\u003C\u002Fscript>\n",[20,5805,5806,5814,5836,5844,5848,5858,5874,5878,5900],{"__ignoreMap":126},[209,5807,5808,5810,5812],{"class":211,"line":212},[209,5809,216],{"class":215},[209,5811,512],{"class":219},[209,5813,243],{"class":215},[209,5815,5816,5818,5820,5822,5824,5826,5828,5830,5832,5834],{"class":211,"line":246},[209,5817,520],{"class":215},[209,5819,1798],{"class":219},[209,5821,5643],{"class":223},[209,5823,230],{"class":219},[209,5825,234],{"class":233},[209,5827,2082],{"class":237},[209,5829,234],{"class":233},[209,5831,5630],{"class":215},[209,5833,1798],{"class":219},[209,5835,243],{"class":215},[209,5837,5838,5840,5842],{"class":211,"line":264},[209,5839,495],{"class":215},[209,5841,512],{"class":219},[209,5843,243],{"class":215},[209,5845,5846],{"class":211,"line":271},[209,5847,268],{"emptyLinePlaceholder":267},[209,5849,5850,5852,5854,5856],{"class":211,"line":296},[209,5851,216],{"class":215},[209,5853,220],{"class":219},[209,5855,224],{"class":223},[209,5857,243],{"class":215},[209,5859,5860,5862,5864,5866,5868,5870,5872],{"class":211,"line":301},[209,5861,4675],{"class":219},[209,5863,4678],{"class":215},[209,5865,1661],{"class":219},[209,5867,459],{"class":233},[209,5869,205],{"class":237},[209,5871,234],{"class":233},[209,5873,1247],{"class":215},[209,5875,5876],{"class":211,"line":326},[209,5877,268],{"emptyLinePlaceholder":267},[209,5879,5880,5882,5885,5887,5889,5891,5893,5896,5898],{"class":211,"line":352},[209,5881,249],{"class":219},[209,5883,5884],{"class":215}," url ",[209,5886,230],{"class":219},[209,5888,4704],{"class":257},[209,5890,362],{"class":215},[209,5892,234],{"class":233},[209,5894,5895],{"class":237},"https:\u002F\u002Fcodigoaoponto.com\u002F",[209,5897,234],{"class":233},[209,5899,402],{"class":215},[209,5901,5902,5904,5906],{"class":211,"line":399},[209,5903,495],{"class":215},[209,5905,220],{"class":219},[209,5907,243],{"class":215},[608,5909,5911],{"id":5910},"v-model-two-way-binding","v-model: Two-Way Binding",[16,5913,5914,5916],{},[20,5915,5786],{}," keeps a form field and a variable in sync in both directions. Typing in the field updates the variable, and changing the variable updates the field:",[120,5918,5920],{"className":203,"code":5919,"language":205,"meta":126,"style":126},"\u003Ctemplate>\n  \u003Cinput v-model=\"name\" type=\"text\" \u002F>\n  \u003Cdiv>Hello! My name is {{ name }}\u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript setup>\nimport { ref } from \"vue\";\n\nconst name = ref(\"Test\");\n\u003C\u002Fscript>\n",[20,5921,5922,5930,5960,5973,5981,5985,5995,6011,6015,6037],{"__ignoreMap":126},[209,5923,5924,5926,5928],{"class":211,"line":212},[209,5925,216],{"class":215},[209,5927,512],{"class":219},[209,5929,243],{"class":215},[209,5931,5932,5934,5937,5940,5942,5944,5946,5948,5950,5952,5954,5956,5958],{"class":211,"line":246},[209,5933,520],{"class":215},[209,5935,5936],{"class":219},"input",[209,5938,5939],{"class":223}," v-model",[209,5941,230],{"class":219},[209,5943,234],{"class":233},[209,5945,2859],{"class":237},[209,5947,234],{"class":233},[209,5949,2520],{"class":223},[209,5951,230],{"class":219},[209,5953,234],{"class":233},[209,5955,125],{"class":237},[209,5957,234],{"class":233},[209,5959,879],{"class":215},[209,5961,5962,5964,5966,5969,5971],{"class":211,"line":264},[209,5963,520],{"class":215},[209,5965,774],{"class":219},[209,5967,5968],{"class":215},">Hello! My name is {{ name }}\u003C\u002F",[209,5970,774],{"class":219},[209,5972,243],{"class":215},[209,5974,5975,5977,5979],{"class":211,"line":271},[209,5976,495],{"class":215},[209,5978,512],{"class":219},[209,5980,243],{"class":215},[209,5982,5983],{"class":211,"line":296},[209,5984,268],{"emptyLinePlaceholder":267},[209,5986,5987,5989,5991,5993],{"class":211,"line":301},[209,5988,216],{"class":215},[209,5990,220],{"class":219},[209,5992,224],{"class":223},[209,5994,243],{"class":215},[209,5996,5997,5999,6001,6003,6005,6007,6009],{"class":211,"line":326},[209,5998,4675],{"class":219},[209,6000,4678],{"class":215},[209,6002,1661],{"class":219},[209,6004,459],{"class":233},[209,6006,205],{"class":237},[209,6008,234],{"class":233},[209,6010,1247],{"class":215},[209,6012,6013],{"class":211,"line":352},[209,6014,268],{"emptyLinePlaceholder":267},[209,6016,6017,6019,6022,6024,6026,6028,6030,6033,6035],{"class":211,"line":399},[209,6018,249],{"class":219},[209,6020,6021],{"class":215}," name ",[209,6023,230],{"class":219},[209,6025,4704],{"class":257},[209,6027,362],{"class":215},[209,6029,234],{"class":233},[209,6031,6032],{"class":237},"Test",[209,6034,234],{"class":233},[209,6036,402],{"class":215},[209,6038,6039,6041,6043],{"class":211,"line":405},[209,6040,495],{"class":215},[209,6042,220],{"class":219},[209,6044,243],{"class":215},[16,6046,6047,6048,6050,6051,6053,6054,6057],{},"On components, ",[20,6049,5786],{}," relies on a matching prop and event. In Vue 3 you can have more than one ",[20,6052,5786],{}," on the same component, each with its own name. Since Vue 3.4, the ",[20,6055,6056],{},"defineModel"," macro creates that prop and event pair for you.",[608,6059,6061],{"id":6060},"v-on-events","v-on: Events",[16,6063,6064,6066],{},[20,6065,5773],{}," connects a browser event to a function:",[120,6068,6070],{"className":203,"code":6069,"language":205,"meta":126,"style":126},"\u003Ctemplate>\n  \u003Cinput v-model=\"name\" type=\"text\" \u002F>\n  \u003Cbutton @click=\"reset\">Reset\u003C\u002Fbutton>\n\u003C\u002Ftemplate>\n\n\u003Cscript setup>\nimport { ref } from \"vue\";\n\nconst name = ref(\"Test\");\n\nfunction reset() {\n  name.value = \"\";\n}\n\u003C\u002Fscript>\n",[20,6071,6072,6080,6108,6132,6140,6144,6154,6170,6174,6194,6198,6208,6220,6224],{"__ignoreMap":126},[209,6073,6074,6076,6078],{"class":211,"line":212},[209,6075,216],{"class":215},[209,6077,512],{"class":219},[209,6079,243],{"class":215},[209,6081,6082,6084,6086,6088,6090,6092,6094,6096,6098,6100,6102,6104,6106],{"class":211,"line":246},[209,6083,520],{"class":215},[209,6085,5936],{"class":219},[209,6087,5939],{"class":223},[209,6089,230],{"class":219},[209,6091,234],{"class":233},[209,6093,2859],{"class":237},[209,6095,234],{"class":233},[209,6097,2520],{"class":223},[209,6099,230],{"class":219},[209,6101,234],{"class":233},[209,6103,125],{"class":237},[209,6105,234],{"class":233},[209,6107,879],{"class":215},[209,6109,6110,6112,6114,6116,6118,6120,6123,6125,6128,6130],{"class":211,"line":264},[209,6111,520],{"class":215},[209,6113,1622],{"class":219},[209,6115,1625],{"class":223},[209,6117,230],{"class":219},[209,6119,234],{"class":233},[209,6121,6122],{"class":237},"reset",[209,6124,234],{"class":233},[209,6126,6127],{"class":215},">Reset\u003C\u002F",[209,6129,1622],{"class":219},[209,6131,243],{"class":215},[209,6133,6134,6136,6138],{"class":211,"line":271},[209,6135,495],{"class":215},[209,6137,512],{"class":219},[209,6139,243],{"class":215},[209,6141,6142],{"class":211,"line":296},[209,6143,268],{"emptyLinePlaceholder":267},[209,6145,6146,6148,6150,6152],{"class":211,"line":301},[209,6147,216],{"class":215},[209,6149,220],{"class":219},[209,6151,224],{"class":223},[209,6153,243],{"class":215},[209,6155,6156,6158,6160,6162,6164,6166,6168],{"class":211,"line":326},[209,6157,4675],{"class":219},[209,6159,4678],{"class":215},[209,6161,1661],{"class":219},[209,6163,459],{"class":233},[209,6165,205],{"class":237},[209,6167,234],{"class":233},[209,6169,1247],{"class":215},[209,6171,6172],{"class":211,"line":352},[209,6173,268],{"emptyLinePlaceholder":267},[209,6175,6176,6178,6180,6182,6184,6186,6188,6190,6192],{"class":211,"line":399},[209,6177,249],{"class":219},[209,6179,6021],{"class":215},[209,6181,230],{"class":219},[209,6183,4704],{"class":257},[209,6185,362],{"class":215},[209,6187,234],{"class":233},[209,6189,6032],{"class":237},[209,6191,234],{"class":233},[209,6193,402],{"class":215},[209,6195,6196],{"class":211,"line":405},[209,6197,268],{"emptyLinePlaceholder":267},[209,6199,6200,6203,6206],{"class":211,"line":410},[209,6201,6202],{"class":219},"function",[209,6204,6205],{"class":257}," reset",[209,6207,1508],{"class":215},[209,6209,6210,6213,6215,6218],{"class":211,"line":425},[209,6211,6212],{"class":215},"  name.value ",[209,6214,230],{"class":219},[209,6216,6217],{"class":233}," \"\"",[209,6219,1247],{"class":215},[209,6221,6222],{"class":211,"line":437},[209,6223,489],{"class":215},[209,6225,6226,6228,6230],{"class":211,"line":451},[209,6227,495],{"class":215},[209,6229,220],{"class":219},[209,6231,243],{"class":215},[608,6233,6235],{"id":6234},"v-for-lists-and-the-key","v-for: Lists and the Key",[16,6237,6238,6240,6241,6244],{},[20,6239,5578],{}," creates one element for each item in an array or object. It needs a ",[20,6242,6243],{},":key"," with a unique, stable value for each item, so Vue knows which element matches which piece of data:",[120,6246,6248],{"className":203,"code":6247,"language":205,"meta":126,"style":126},"\u003Ctemplate>\n  \u003Cul>\n    \u003Cli v-for=\"task in tasks\" :key=\"task.id\">\n      {{ task.title }}\n    \u003C\u002Fli>\n  \u003C\u002Ful>\n\u003C\u002Ftemplate>\n\n\u003Cscript setup>\nconst tasks = [\n  { id: 101, title: \"Review the documentation\" },\n  { id: 102, title: \"Update the dependencies\" },\n  { id: 103, title: \"Publish the article\" }\n];\n\u003C\u002Fscript>\n",[20,6249,6250,6258,6266,6298,6303,6311,6319,6327,6331,6341,6352,6376,6398,6421,6426],{"__ignoreMap":126},[209,6251,6252,6254,6256],{"class":211,"line":212},[209,6253,216],{"class":215},[209,6255,512],{"class":219},[209,6257,243],{"class":215},[209,6259,6260,6262,6264],{"class":211,"line":246},[209,6261,520],{"class":215},[209,6263,918],{"class":219},[209,6265,243],{"class":215},[209,6267,6268,6270,6272,6275,6277,6279,6282,6284,6287,6289,6291,6294,6296],{"class":211,"line":264},[209,6269,543],{"class":215},[209,6271,921],{"class":219},[209,6273,6274],{"class":223}," v-for",[209,6276,230],{"class":219},[209,6278,234],{"class":233},[209,6280,6281],{"class":237},"task in tasks",[209,6283,234],{"class":233},[209,6285,6286],{"class":223}," :key",[209,6288,230],{"class":219},[209,6290,234],{"class":233},[209,6292,6293],{"class":237},"task.id",[209,6295,234],{"class":233},[209,6297,243],{"class":215},[209,6299,6300],{"class":211,"line":271},[209,6301,6302],{"class":215},"      {{ task.title }}\n",[209,6304,6305,6307,6309],{"class":211,"line":296},[209,6306,865],{"class":215},[209,6308,921],{"class":219},[209,6310,243],{"class":215},[209,6312,6313,6315,6317],{"class":211,"line":301},[209,6314,559],{"class":215},[209,6316,918],{"class":219},[209,6318,243],{"class":215},[209,6320,6321,6323,6325],{"class":211,"line":326},[209,6322,495],{"class":215},[209,6324,512],{"class":219},[209,6326,243],{"class":215},[209,6328,6329],{"class":211,"line":352},[209,6330,268],{"emptyLinePlaceholder":267},[209,6332,6333,6335,6337,6339],{"class":211,"line":399},[209,6334,216],{"class":215},[209,6336,220],{"class":219},[209,6338,224],{"class":223},[209,6340,243],{"class":215},[209,6342,6343,6345,6348,6350],{"class":211,"line":405},[209,6344,249],{"class":219},[209,6346,6347],{"class":215}," tasks ",[209,6349,230],{"class":219},[209,6351,2069],{"class":215},[209,6353,6354,6357,6359,6362,6365,6367,6369,6372,6374],{"class":211,"line":410},[209,6355,6356],{"class":215},"  { id",[209,6358,309],{"class":219},[209,6360,6361],{"class":445}," 101",[209,6363,6364],{"class":215},", title",[209,6366,309],{"class":219},[209,6368,459],{"class":233},[209,6370,6371],{"class":237},"Review the documentation",[209,6373,234],{"class":233},[209,6375,3105],{"class":215},[209,6377,6378,6380,6382,6385,6387,6389,6391,6394,6396],{"class":211,"line":425},[209,6379,6356],{"class":215},[209,6381,309],{"class":219},[209,6383,6384],{"class":445}," 102",[209,6386,6364],{"class":215},[209,6388,309],{"class":219},[209,6390,459],{"class":233},[209,6392,6393],{"class":237},"Update the dependencies",[209,6395,234],{"class":233},[209,6397,3105],{"class":215},[209,6399,6400,6402,6404,6407,6409,6411,6413,6416,6418],{"class":211,"line":437},[209,6401,6356],{"class":215},[209,6403,309],{"class":219},[209,6405,6406],{"class":445}," 103",[209,6408,6364],{"class":215},[209,6410,309],{"class":219},[209,6412,459],{"class":233},[209,6414,6415],{"class":237},"Publish the article",[209,6417,234],{"class":233},[209,6419,6420],{"class":215}," }\n",[209,6422,6423],{"class":211,"line":451},[209,6424,6425],{"class":215},"];\n",[209,6427,6428,6430,6432],{"class":211,"line":469},[209,6429,495],{"class":215},[209,6431,220],{"class":219},[209,6433,243],{"class":215},[16,6435,6436,6437,6440],{},"The array index only works as a ",[20,6438,6439],{},"key"," in static lists. If the list can be reordered, filtered, or gain and lose items, the index makes Vue reuse the wrong element. The problem shows up mostly when each row has a text field, a component or an animation with its own state.",[608,6442,6444],{"id":6443},"v-if-v-else-if-and-v-else-conditional-rendering","v-if, v-else-if and v-else: Conditional Rendering",[16,6446,6447,6449,6450,139,6453,6456],{},[20,6448,5554],{}," includes the element only when the condition is true. ",[20,6451,6452],{},"v-else-if",[20,6454,6455],{},"v-else"," chain the alternatives:",[120,6458,6460],{"className":203,"code":6459,"language":205,"meta":126,"style":126},"\u003Ctemplate>\n  \u003Cinput v-model=\"name\" type=\"text\" \u002F>\n  \u003Cdiv v-if=\"name.length > 10\">Your name is too long!\u003C\u002Fdiv>\n  \u003Cdiv v-else-if=\"name\">Hello! My name is {{ name }}\u003C\u002Fdiv>\n  \u003Cdiv v-else>Please enter your name.\u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript setup>\nimport { ref } from \"vue\";\n\nconst name = ref(\"\");\n\u003C\u002Fscript>\n",[20,6461,6462,6470,6498,6522,6545,6561,6569,6573,6583,6599,6603,6620],{"__ignoreMap":126},[209,6463,6464,6466,6468],{"class":211,"line":212},[209,6465,216],{"class":215},[209,6467,512],{"class":219},[209,6469,243],{"class":215},[209,6471,6472,6474,6476,6478,6480,6482,6484,6486,6488,6490,6492,6494,6496],{"class":211,"line":246},[209,6473,520],{"class":215},[209,6475,5936],{"class":219},[209,6477,5939],{"class":223},[209,6479,230],{"class":219},[209,6481,234],{"class":233},[209,6483,2859],{"class":237},[209,6485,234],{"class":233},[209,6487,2520],{"class":223},[209,6489,230],{"class":219},[209,6491,234],{"class":233},[209,6493,125],{"class":237},[209,6495,234],{"class":233},[209,6497,879],{"class":215},[209,6499,6500,6502,6504,6506,6508,6510,6513,6515,6518,6520],{"class":211,"line":264},[209,6501,520],{"class":215},[209,6503,774],{"class":219},[209,6505,526],{"class":223},[209,6507,230],{"class":219},[209,6509,234],{"class":233},[209,6511,6512],{"class":237},"name.length > 10",[209,6514,234],{"class":233},[209,6516,6517],{"class":215},">Your name is too long!\u003C\u002F",[209,6519,774],{"class":219},[209,6521,243],{"class":215},[209,6523,6524,6526,6528,6531,6533,6535,6537,6539,6541,6543],{"class":211,"line":271},[209,6525,520],{"class":215},[209,6527,774],{"class":219},[209,6529,6530],{"class":223}," v-else-if",[209,6532,230],{"class":219},[209,6534,234],{"class":233},[209,6536,2859],{"class":237},[209,6538,234],{"class":233},[209,6540,5968],{"class":215},[209,6542,774],{"class":219},[209,6544,243],{"class":215},[209,6546,6547,6549,6551,6554,6557,6559],{"class":211,"line":296},[209,6548,520],{"class":215},[209,6550,774],{"class":219},[209,6552,6553],{"class":223}," v-else",[209,6555,6556],{"class":215},">Please enter your name.\u003C\u002F",[209,6558,774],{"class":219},[209,6560,243],{"class":215},[209,6562,6563,6565,6567],{"class":211,"line":301},[209,6564,495],{"class":215},[209,6566,512],{"class":219},[209,6568,243],{"class":215},[209,6570,6571],{"class":211,"line":326},[209,6572,268],{"emptyLinePlaceholder":267},[209,6574,6575,6577,6579,6581],{"class":211,"line":352},[209,6576,216],{"class":215},[209,6578,220],{"class":219},[209,6580,224],{"class":223},[209,6582,243],{"class":215},[209,6584,6585,6587,6589,6591,6593,6595,6597],{"class":211,"line":399},[209,6586,4675],{"class":219},[209,6588,4678],{"class":215},[209,6590,1661],{"class":219},[209,6592,459],{"class":233},[209,6594,205],{"class":237},[209,6596,234],{"class":233},[209,6598,1247],{"class":215},[209,6600,6601],{"class":211,"line":405},[209,6602,268],{"emptyLinePlaceholder":267},[209,6604,6605,6607,6609,6611,6613,6615,6618],{"class":211,"line":410},[209,6606,249],{"class":219},[209,6608,6021],{"class":215},[209,6610,230],{"class":219},[209,6612,4704],{"class":257},[209,6614,362],{"class":215},[209,6616,6617],{"class":233},"\"\"",[209,6619,402],{"class":215},[209,6621,6622,6624,6626],{"class":211,"line":425},[209,6623,495],{"class":215},[209,6625,220],{"class":219},[209,6627,243],{"class":215},[608,6629,6631],{"id":6630},"v-show-visibility","v-show: Visibility",[16,6633,6634,6636,6637,6640],{},[20,6635,5557],{}," keeps the element in the DOM and toggles the CSS ",[20,6638,6639],{},"display"," property:",[120,6642,6644],{"className":203,"code":6643,"language":205,"meta":126,"style":126},"\u003Ctemplate>\n  \u003Cdiv v-show=\"name.length > 10\">Your name is too long!\u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n",[20,6645,6646,6654,6677],{"__ignoreMap":126},[209,6647,6648,6650,6652],{"class":211,"line":212},[209,6649,216],{"class":215},[209,6651,512],{"class":219},[209,6653,243],{"class":215},[209,6655,6656,6658,6660,6663,6665,6667,6669,6671,6673,6675],{"class":211,"line":246},[209,6657,520],{"class":215},[209,6659,774],{"class":219},[209,6661,6662],{"class":223}," v-show",[209,6664,230],{"class":219},[209,6666,234],{"class":233},[209,6668,6512],{"class":237},[209,6670,234],{"class":233},[209,6672,6517],{"class":215},[209,6674,774],{"class":219},[209,6676,243],{"class":215},[209,6678,6679,6681,6683],{"class":211,"line":264},[209,6680,495],{"class":215},[209,6682,512],{"class":219},[209,6684,243],{"class":215},[608,6686,6688],{"id":6687},"v-html-and-v-text","v-html and v-text",[16,6690,6691,6694,6695,6698,6699,6702],{},[20,6692,6693],{},"v-text"," sets the element's text, like the ",[20,6696,6697],{},"{{ }}"," interpolation does. ",[20,6700,6701],{},"v-html"," inserts the string as HTML.",[2496,6704,6705],{},[16,6706,6707,6708,6710],{},"Never use ",[20,6709,6701],{}," with user-provided or external content without sanitizing it first. Malicious HTML in that string is an XSS vulnerability. Regular interpolation treats everything as text and is the safe option.",[608,6712,6714],{"id":6713},"other-directives","Other Directives",[16,6716,6717],{},"Vue has five more directives that show up less often:",[918,6719,6720,6726,6732,6741,6747],{},[921,6721,6722,6725],{},[20,6723,6724],{},"v-slot"," receives the content of named and scoped slots.",[921,6727,6728,6731],{},[20,6729,6730],{},"v-once"," renders the element once and skips later updates.",[921,6733,6734,6737,6738,6740],{},[20,6735,6736],{},"v-pre"," skips compiling the element and its children, which is useful for showing ",[20,6739,6697],{}," literally.",[921,6742,6743,6746],{},[20,6744,6745],{},"v-cloak"," hides the template until it is compiled, in projects without a build step.",[921,6748,6749,6751],{},[20,6750,5155],{}," skips re-processing a part of the template while the listed values stay the same.",[25,6753,6755],{"id":6754},"v-if-or-v-show-which-one-should-you-choose","v-if or v-show: Which One Should You Choose?",[16,6757,6758,6759,6761,6762,6764,6765,702],{},"Both hide an element, but in different ways. ",[20,6760,5554],{}," only renders the block when the condition is true and, when it changes, destroys and recreates the block with its components and listeners. ",[20,6763,5557],{}," always renders the element and only toggles ",[20,6766,6639],{},[16,6768,6769,6770,6772,6773,6775],{},"The documentation's guidance is to prefer ",[20,6771,5557],{}," when you toggle something very often, because toggling is cheaper. Prefer ",[20,6774,5554],{}," when the condition rarely changes at runtime, because the block is not even created until it is needed.",[16,6777,6778,6780,6781,6784,6785,6787,6788,6790,6791,702],{},[20,6779,5557],{}," has two limitations: it does not work on ",[20,6782,6783],{},"\u003Ctemplate>"," and it cannot be combined with ",[20,6786,6455],{},". To hide several elements at once, use ",[20,6789,5554],{}," on a ",[20,6792,6783],{},[25,6794,6796],{"id":6795},"how-to-create-a-custom-directive-in-vue-3","How to Create a Custom Directive in Vue 3",[16,6798,6799],{},"A custom directive is an object with lifecycle functions that receive the DOM element. The Vue documentation recommends preferring declarative templates and keeping directives for when you need direct access to the element.",[608,6801,6803],{"id":6802},"a-local-directive-with-script-setup","A Local Directive with script setup",[16,6805,6806,6807,6809,6810,6813,6814,6817,6818,309],{},"Inside ",[20,6808,594],{},", any camelCase variable that starts with ",[20,6811,6812],{},"v"," becomes a directive. The variable ",[20,6815,6816],{},"vFocus"," is available in the template as ",[20,6819,6820],{},"v-focus",[120,6822,6824],{"className":203,"code":6823,"language":205,"meta":126,"style":126},"\u003Cscript setup>\nconst vFocus = {\n  mounted: (el) => el.focus()\n};\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Cinput v-focus \u002F>\n\u003C\u002Ftemplate>\n",[20,6825,6826,6836,6847,6870,6875,6883,6887,6895,6906],{"__ignoreMap":126},[209,6827,6828,6830,6832,6834],{"class":211,"line":212},[209,6829,216],{"class":215},[209,6831,220],{"class":219},[209,6833,224],{"class":223},[209,6835,243],{"class":215},[209,6837,6838,6840,6843,6845],{"class":211,"line":246},[209,6839,249],{"class":219},[209,6841,6842],{"class":215}," vFocus ",[209,6844,230],{"class":219},[209,6846,2054],{"class":215},[209,6848,6849,6852,6854,6856,6858,6860,6862,6865,6868],{"class":211,"line":264},[209,6850,6851],{"class":257},"  mounted",[209,6853,309],{"class":219},[209,6855,416],{"class":215},[209,6857,5297],{"class":668},[209,6859,672],{"class":215},[209,6861,287],{"class":219},[209,6863,6864],{"class":215}," el.",[209,6866,6867],{"class":257},"focus",[209,6869,396],{"class":215},[209,6871,6872],{"class":211,"line":271},[209,6873,6874],{"class":215},"};\n",[209,6876,6877,6879,6881],{"class":211,"line":296},[209,6878,495],{"class":215},[209,6880,220],{"class":219},[209,6882,243],{"class":215},[209,6884,6885],{"class":211,"line":301},[209,6886,268],{"emptyLinePlaceholder":267},[209,6888,6889,6891,6893],{"class":211,"line":326},[209,6890,216],{"class":215},[209,6892,512],{"class":219},[209,6894,243],{"class":215},[209,6896,6897,6899,6901,6904],{"class":211,"line":352},[209,6898,520],{"class":215},[209,6900,5936],{"class":219},[209,6902,6903],{"class":223}," v-focus",[209,6905,879],{"class":215},[209,6907,6908,6910,6912],{"class":211,"line":399},[209,6909,495],{"class":215},[209,6911,512],{"class":219},[209,6913,243],{"class":215},[16,6915,6916,6917,6919],{},"Here the ",[20,6918,5125],{}," hook runs when the element is inserted into the DOM and focuses the field.",[608,6921,6923],{"id":6922},"global-registration-with-appdirective","Global Registration with app.directive",[16,6925,6926,6927,309],{},"If several parts of the application use the directive, write it in its own file and register it once with ",[20,6928,6929],{},"app.directive",[120,6931,6933],{"className":1131,"code":6932,"language":238,"meta":126,"style":126},"\u002F\u002F directives\u002Ffocus.ts\nimport type { Directive } from \"vue\";\n\nexport const vFocus: Directive\u003CHTMLElement> = {\n  mounted: (el) => el.focus()\n};\n",[20,6934,6935,6940,6959,6963,6990,7010],{"__ignoreMap":126},[209,6936,6937],{"class":211,"line":212},[209,6938,6939],{"class":1139},"\u002F\u002F directives\u002Ffocus.ts\n",[209,6941,6942,6944,6946,6949,6951,6953,6955,6957],{"class":211,"line":246},[209,6943,4675],{"class":219},[209,6945,2520],{"class":219},[209,6947,6948],{"class":215}," { Directive } ",[209,6950,1661],{"class":219},[209,6952,459],{"class":233},[209,6954,205],{"class":237},[209,6956,234],{"class":233},[209,6958,1247],{"class":215},[209,6960,6961],{"class":211,"line":264},[209,6962,268],{"emptyLinePlaceholder":267},[209,6964,6965,6967,6970,6973,6975,6978,6980,6983,6986,6988],{"class":211,"line":271},[209,6966,1145],{"class":219},[209,6968,6969],{"class":219}," const",[209,6971,6972],{"class":215}," vFocus",[209,6974,309],{"class":219},[209,6976,6977],{"class":1163}," Directive",[209,6979,216],{"class":215},[209,6981,6982],{"class":668},"HTMLElement",[209,6984,6985],{"class":215},"> ",[209,6987,230],{"class":219},[209,6989,2054],{"class":215},[209,6991,6992,6994,6996,6998,7000,7002,7004,7006,7008],{"class":211,"line":296},[209,6993,6851],{"class":257},[209,6995,309],{"class":219},[209,6997,416],{"class":215},[209,6999,5297],{"class":668},[209,7001,672],{"class":215},[209,7003,287],{"class":219},[209,7005,6864],{"class":215},[209,7007,6867],{"class":257},[209,7009,396],{"class":215},[209,7011,7012],{"class":211,"line":301},[209,7013,6874],{"class":215},[120,7015,7017],{"className":1131,"code":7016,"language":238,"meta":126,"style":126},"\u002F\u002F main.ts\nimport { createApp } from \"vue\";\nimport App from \".\u002FApp.vue\";\nimport { vFocus } from \".\u002Fdirectives\u002Ffocus\";\n\nconst app = createApp(App);\n\napp.directive(\"focus\", vFocus);\napp.mount(\"#app\");\n",[20,7018,7019,7024,7041,7057,7075,7079,7094,7098,7117],{"__ignoreMap":126},[209,7020,7021],{"class":211,"line":212},[209,7022,7023],{"class":1139},"\u002F\u002F main.ts\n",[209,7025,7026,7028,7031,7033,7035,7037,7039],{"class":211,"line":246},[209,7027,4675],{"class":219},[209,7029,7030],{"class":215}," { createApp } ",[209,7032,1661],{"class":219},[209,7034,459],{"class":233},[209,7036,205],{"class":237},[209,7038,234],{"class":233},[209,7040,1247],{"class":215},[209,7042,7043,7045,7047,7049,7051,7053,7055],{"class":211,"line":264},[209,7044,4675],{"class":219},[209,7046,4835],{"class":215},[209,7048,1661],{"class":219},[209,7050,459],{"class":233},[209,7052,4842],{"class":237},[209,7054,234],{"class":233},[209,7056,1247],{"class":215},[209,7058,7059,7061,7064,7066,7068,7071,7073],{"class":211,"line":271},[209,7060,4675],{"class":219},[209,7062,7063],{"class":215}," { vFocus } ",[209,7065,1661],{"class":219},[209,7067,459],{"class":233},[209,7069,7070],{"class":237},".\u002Fdirectives\u002Ffocus",[209,7072,234],{"class":233},[209,7074,1247],{"class":215},[209,7076,7077],{"class":211,"line":296},[209,7078,268],{"emptyLinePlaceholder":267},[209,7080,7081,7083,7086,7088,7091],{"class":211,"line":301},[209,7082,249],{"class":219},[209,7084,7085],{"class":215}," app ",[209,7087,230],{"class":219},[209,7089,7090],{"class":257}," createApp",[209,7092,7093],{"class":215},"(App);\n",[209,7095,7096],{"class":211,"line":326},[209,7097,268],{"emptyLinePlaceholder":267},[209,7099,7100,7103,7106,7108,7110,7112,7114],{"class":211,"line":352},[209,7101,7102],{"class":215},"app.",[209,7104,7105],{"class":257},"directive",[209,7107,362],{"class":215},[209,7109,234],{"class":233},[209,7111,6867],{"class":237},[209,7113,234],{"class":233},[209,7115,7116],{"class":215},", vFocus);\n",[209,7118,7119,7121,7123,7125,7127,7129,7131],{"class":211,"line":399},[209,7120,7102],{"class":215},[209,7122,4865],{"class":257},[209,7124,362],{"class":215},[209,7126,234],{"class":233},[209,7128,4873],{"class":237},[209,7130,234],{"class":233},[209,7132,402],{"class":215},[608,7134,7136],{"id":7135},"lifecycle-hooks","Lifecycle Hooks",[16,7138,7139],{},"A directive can implement these hooks, listed in the order they run:",[918,7141,7142,7148,7154,7159,7165,7171,7177],{},[921,7143,7144,7147],{},[20,7145,7146],{},"created",": before the element's attributes and listeners are applied.",[921,7149,7150,7153],{},[20,7151,7152],{},"beforeMount",": before the element is inserted into the DOM.",[921,7155,7156,7158],{},[20,7157,5125],{},": after the parent component and all its children are mounted.",[921,7160,7161,7164],{},[20,7162,7163],{},"beforeUpdate",": before the parent component updates.",[921,7166,7167,7170],{},[20,7168,7169],{},"updated",": after the parent component and its children update.",[921,7172,7173,7176],{},[20,7174,7175],{},"beforeUnmount",": before the parent component is unmounted.",[921,7178,7179,7182],{},[20,7180,7181],{},"unmounted",": after the parent component is unmounted.",[16,7184,7185,7186,7188,7189,139,7192,7195,7196,3356,7199,7201,7202,7204,7205,7208,7209,139,7211,7213,7214,5178,7217,7220,7221,7224,7225,7228,7229,7231],{},"All of them receive ",[20,7187,5297],{}," (the element), ",[20,7190,7191],{},"binding",[20,7193,7194],{},"vnode",". The update hooks also receive ",[20,7197,7198],{},"prevVnode",[20,7200,7191],{}," object carries the ",[20,7203,5375],{}," passed to the directive, the ",[20,7206,7207],{},"oldValue"," (only in ",[20,7210,7163],{},[20,7212,7169],{},"), the ",[20,7215,7216],{},"arg",[20,7218,7219],{},"modifiers",", the component ",[20,7222,7223],{},"instance"," and the directive definition itself in ",[20,7226,7227],{},"dir",". Treat all of these except ",[20,7230,5297],{}," as read-only.",[608,7233,7235],{"id":7234},"value-argument-and-modifiers","Value, Argument and Modifiers",[16,7237,7238,7239,139,7241,309],{},"When the same behavior should run on mount and on every update, write the directive as a function. It is called on ",[20,7240,5125],{},[20,7242,7169],{},[120,7244,7246],{"className":1131,"code":7245,"language":238,"meta":126,"style":126},"\u002F\u002F directives\u002Fhighlight.ts\nimport type { Directive } from \"vue\";\n\n\u002F\u002F usage: \u003Cp v-highlight:background.smooth=\"'#fff3b0'\">Text\u003C\u002Fp>\nexport const vHighlight: Directive\u003CHTMLElement, string> = (el, binding) => {\n  el.style.setProperty(binding.arg ?? \"color\", binding.value);\n\n  if (binding.modifiers.smooth) {\n    el.style.transition = \"all 0.3s\";\n  }\n};\n",[20,7247,7248,7253,7271,7275,7280,7319,7342,7346,7353,7369,7373],{"__ignoreMap":126},[209,7249,7250],{"class":211,"line":212},[209,7251,7252],{"class":1139},"\u002F\u002F directives\u002Fhighlight.ts\n",[209,7254,7255,7257,7259,7261,7263,7265,7267,7269],{"class":211,"line":246},[209,7256,4675],{"class":219},[209,7258,2520],{"class":219},[209,7260,6948],{"class":215},[209,7262,1661],{"class":219},[209,7264,459],{"class":233},[209,7266,205],{"class":237},[209,7268,234],{"class":233},[209,7270,1247],{"class":215},[209,7272,7273],{"class":211,"line":264},[209,7274,268],{"emptyLinePlaceholder":267},[209,7276,7277],{"class":211,"line":271},[209,7278,7279],{"class":1139},"\u002F\u002F usage: \u003Cp v-highlight:background.smooth=\"'#fff3b0'\">Text\u003C\u002Fp>\n",[209,7281,7282,7284,7286,7289,7291,7293,7295,7297,7299,7301,7303,7305,7307,7309,7311,7313,7315,7317],{"class":211,"line":296},[209,7283,1145],{"class":219},[209,7285,6969],{"class":219},[209,7287,7288],{"class":257}," vHighlight",[209,7290,309],{"class":219},[209,7292,6977],{"class":1163},[209,7294,216],{"class":215},[209,7296,6982],{"class":668},[209,7298,3341],{"class":215},[209,7300,1177],{"class":1163},[209,7302,6985],{"class":215},[209,7304,230],{"class":219},[209,7306,416],{"class":215},[209,7308,5297],{"class":668},[209,7310,3341],{"class":215},[209,7312,7191],{"class":668},[209,7314,672],{"class":215},[209,7316,287],{"class":219},[209,7318,2054],{"class":215},[209,7320,7321,7324,7327,7330,7332,7334,7337,7339],{"class":211,"line":301},[209,7322,7323],{"class":215},"  el.style.",[209,7325,7326],{"class":257},"setProperty",[209,7328,7329],{"class":215},"(binding.arg ",[209,7331,1241],{"class":219},[209,7333,459],{"class":233},[209,7335,7336],{"class":237},"color",[209,7338,234],{"class":233},[209,7340,7341],{"class":215},", binding.value);\n",[209,7343,7344],{"class":211,"line":326},[209,7345,268],{"emptyLinePlaceholder":267},[209,7347,7348,7350],{"class":211,"line":352},[209,7349,4101],{"class":219},[209,7351,7352],{"class":215}," (binding.modifiers.smooth) {\n",[209,7354,7355,7358,7360,7362,7365,7367],{"class":211,"line":399},[209,7356,7357],{"class":215},"    el.style.transition ",[209,7359,230],{"class":219},[209,7361,459],{"class":233},[209,7363,7364],{"class":237},"all 0.3s",[209,7366,234],{"class":233},[209,7368,1247],{"class":215},[209,7370,7371],{"class":211,"line":405},[209,7372,2212],{"class":215},[209,7374,7375],{"class":211,"line":410},[209,7376,6874],{"class":215},[16,7378,3571,7379,7381,7382,5178,7385,7388,7389,3686,7392,7394,7395,702],{},[20,7380,7216],{}," is ",[20,7383,7384],{},"background",[20,7386,7387],{},"smooth"," modifier shows up in ",[20,7390,7391],{},"binding.modifiers",[20,7393,5375],{}," is the color. When the directive needs several options, the value can be an object: ",[20,7396,7397],{},"v-highlight=\"{ color: 'white', duration: 300 }\"",[608,7399,7401],{"id":7400},"cleaning-up-listeners","Cleaning Up Listeners",[16,7403,7404,7405,1866,7407,7410],{},"Any directive that registers a listener on ",[20,7406,5204],{},[20,7408,7409],{},"window",", an observer or a timer has to undo it when the element leaves the screen. The classic example is closing a menu when the user clicks outside of it:",[120,7412,7414],{"className":1131,"code":7413,"language":238,"meta":126,"style":126},"\u002F\u002F directives\u002FclickOutside.ts\nimport type { Directive } from \"vue\";\n\ntype ElementWithListener = HTMLElement & {\n  _onClickOutside?: (event: MouseEvent) => void;\n};\n\nexport const vClickOutside: Directive\u003CElementWithListener, () => void> = {\n  mounted(el, binding) {\n    el._onClickOutside = (event) => {\n      if (!el.contains(event.target as Node)) {\n        binding.value();\n      }\n    };\n\n    document.addEventListener(\"click\", el._onClickOutside);\n  },\n\n  unmounted(el) {\n    if (el._onClickOutside) {\n      document.removeEventListener(\"click\", el._onClickOutside);\n      delete el._onClickOutside;\n    }\n  }\n};\n",[20,7415,7416,7421,7439,7443,7461,7488,7492,7496,7526,7540,7560,7586,7595,7599,7604,7608,7627,7631,7635,7646,7654,7672,7680,7685,7689],{"__ignoreMap":126},[209,7417,7418],{"class":211,"line":212},[209,7419,7420],{"class":1139},"\u002F\u002F directives\u002FclickOutside.ts\n",[209,7422,7423,7425,7427,7429,7431,7433,7435,7437],{"class":211,"line":246},[209,7424,4675],{"class":219},[209,7426,2520],{"class":219},[209,7428,6948],{"class":215},[209,7430,1661],{"class":219},[209,7432,459],{"class":233},[209,7434,205],{"class":237},[209,7436,234],{"class":233},[209,7438,1247],{"class":215},[209,7440,7441],{"class":211,"line":264},[209,7442,268],{"emptyLinePlaceholder":267},[209,7444,7445,7448,7451,7453,7456,7459],{"class":211,"line":271},[209,7446,7447],{"class":219},"type",[209,7449,7450],{"class":1163}," ElementWithListener",[209,7452,5292],{"class":219},[209,7454,7455],{"class":1163}," HTMLElement",[209,7457,7458],{"class":219}," &",[209,7460,2054],{"class":215},[209,7462,7463,7466,7469,7471,7474,7476,7479,7481,7483,7486],{"class":211,"line":296},[209,7464,7465],{"class":257},"  _onClickOutside",[209,7467,7468],{"class":219},"?:",[209,7470,416],{"class":215},[209,7472,7473],{"class":668},"event",[209,7475,309],{"class":219},[209,7477,7478],{"class":1163}," MouseEvent",[209,7480,672],{"class":215},[209,7482,287],{"class":219},[209,7484,7485],{"class":1163}," void",[209,7487,1247],{"class":215},[209,7489,7490],{"class":211,"line":301},[209,7491,6874],{"class":215},[209,7493,7494],{"class":211,"line":326},[209,7495,268],{"emptyLinePlaceholder":267},[209,7497,7498,7500,7502,7505,7507,7509,7511,7514,7516,7518,7520,7522,7524],{"class":211,"line":352},[209,7499,1145],{"class":219},[209,7501,6969],{"class":219},[209,7503,7504],{"class":215}," vClickOutside",[209,7506,309],{"class":219},[209,7508,6977],{"class":1163},[209,7510,216],{"class":215},[209,7512,7513],{"class":668},"ElementWithListener",[209,7515,2952],{"class":215},[209,7517,287],{"class":219},[209,7519,7485],{"class":1163},[209,7521,6985],{"class":215},[209,7523,230],{"class":219},[209,7525,2054],{"class":215},[209,7527,7528,7530,7532,7534,7536,7538],{"class":211,"line":399},[209,7529,6851],{"class":257},[209,7531,362],{"class":215},[209,7533,5297],{"class":668},[209,7535,3341],{"class":215},[209,7537,7191],{"class":668},[209,7539,1195],{"class":215},[209,7541,7542,7545,7548,7550,7552,7554,7556,7558],{"class":211,"line":405},[209,7543,7544],{"class":215},"    el.",[209,7546,7547],{"class":257},"_onClickOutside",[209,7549,5292],{"class":219},[209,7551,416],{"class":215},[209,7553,7473],{"class":668},[209,7555,672],{"class":215},[209,7557,287],{"class":219},[209,7559,2054],{"class":215},[209,7561,7562,7564,7566,7568,7571,7574,7577,7580,7583],{"class":211,"line":410},[209,7563,4271],{"class":219},[209,7565,416],{"class":215},[209,7567,419],{"class":219},[209,7569,7570],{"class":215},"el.",[209,7572,7573],{"class":257},"contains",[209,7575,7576],{"class":215},"(event.target ",[209,7578,7579],{"class":219},"as",[209,7581,7582],{"class":1163}," Node",[209,7584,7585],{"class":215},")) {\n",[209,7587,7588,7591,7593],{"class":211,"line":425},[209,7589,7590],{"class":215},"        binding.",[209,7592,5375],{"class":257},[209,7594,261],{"class":215},[209,7596,7597],{"class":211,"line":437},[209,7598,2202],{"class":215},[209,7600,7601],{"class":211,"line":451},[209,7602,7603],{"class":215},"    };\n",[209,7605,7606],{"class":211,"line":469},[209,7607,268],{"emptyLinePlaceholder":267},[209,7609,7610,7613,7616,7618,7620,7622,7624],{"class":211,"line":480},[209,7611,7612],{"class":215},"    document.",[209,7614,7615],{"class":257},"addEventListener",[209,7617,362],{"class":215},[209,7619,234],{"class":233},[209,7621,5229],{"class":237},[209,7623,234],{"class":233},[209,7625,7626],{"class":215},", el._onClickOutside);\n",[209,7628,7629],{"class":211,"line":486},[209,7630,3245],{"class":215},[209,7632,7633],{"class":211,"line":492},[209,7634,268],{"emptyLinePlaceholder":267},[209,7636,7637,7640,7642,7644],{"class":211,"line":502},[209,7638,7639],{"class":257},"  unmounted",[209,7641,362],{"class":215},[209,7643,5297],{"class":668},[209,7645,1195],{"class":215},[209,7647,7648,7651],{"class":211,"line":507},[209,7649,7650],{"class":219},"    if",[209,7652,7653],{"class":215}," (el._onClickOutside) {\n",[209,7655,7656,7659,7662,7664,7666,7668,7670],{"class":211,"line":517},[209,7657,7658],{"class":215},"      document.",[209,7660,7661],{"class":257},"removeEventListener",[209,7663,362],{"class":215},[209,7665,234],{"class":233},[209,7667,5229],{"class":237},[209,7669,234],{"class":233},[209,7671,7626],{"class":215},[209,7673,7674,7677],{"class":211,"line":540},[209,7675,7676],{"class":219},"      delete",[209,7678,7679],{"class":215}," el._onClickOutside;\n",[209,7681,7682],{"class":211,"line":556},[209,7683,7684],{"class":215},"    }\n",[209,7686,7687],{"class":211,"line":566},[209,7688,2212],{"class":215},[209,7690,7692],{"class":211,"line":7691},25,[209,7693,6874],{"class":215},[16,7695,7696,7697,7699,7700,7702,7703,7706,7707,7709],{},"The function is stored on the element so ",[20,7698,7181],{}," removes exactly the same reference that ",[20,7701,5125],{}," registered. Without that, the listener stays alive after the element is gone. This example reads ",[20,7704,7705],{},"binding.value"," only on mount. If the function you pass can change, update the reference in the ",[20,7708,7169],{}," hook too.",[25,7711,7713],{"id":7712},"directive-composable-or-component","Directive, Composable or Component?",[16,7715,7716],{},"A directive fits when the logic has to touch one specific element, like focusing a field. If the logic has state or works without being tied to an element, like a reusable debounce, a composable is a better fit. When there is visual structure, like an accessible tooltip or a menu with keyboard support and submenus, a component is the right choice.",[16,7718,7719],{},"A good tooltip needs positioning, focus, keyboard support and the Esc key. A directive that tries to cover all of that tends to end up more complicated than the equivalent component.",[25,7721,7723],{"id":7722},"how-to-use-custom-directives-in-nuxt-4","How to Use Custom Directives in Nuxt 4",[16,7725,7726,7727,7730,7731,7734,7735,7738],{},"In Nuxt, a global directive is registered in a plugin inside ",[20,7728,7729],{},"app\u002Fplugins\u002F",", using ",[20,7732,7733],{},"nuxtApp.vueApp.directive",". Because Nuxt renders the page on the server, there is one more rule: during SSR, Vue ignores a directive's hooks, since they manipulate the DOM and the server has no DOM. If the directive needs to add an attribute to the generated HTML, use the ",[20,7736,7737],{},"getSSRProps"," hook, which returns the props to render:",[120,7740,7742],{"className":1131,"code":7741,"language":238,"meta":126,"style":126},"\u002F\u002F app\u002Fplugins\u002Fdirectives.ts\nexport default defineNuxtPlugin((nuxtApp) => {\n  nuxtApp.vueApp.directive(\"focus\", {\n    mounted: (el: HTMLElement) => el.focus(),\n    getSSRProps: () => ({ autofocus: true })\n  });\n});\n",[20,7743,7744,7749,7769,7787,7813,7834,7838],{"__ignoreMap":126},[209,7745,7746],{"class":211,"line":212},[209,7747,7748],{"class":1139},"\u002F\u002F app\u002Fplugins\u002Fdirectives.ts\n",[209,7750,7751,7753,7755,7758,7760,7763,7765,7767],{"class":211,"line":246},[209,7752,1145],{"class":219},[209,7754,2762],{"class":219},[209,7756,7757],{"class":257}," defineNuxtPlugin",[209,7759,4064],{"class":215},[209,7761,7762],{"class":668},"nuxtApp",[209,7764,672],{"class":215},[209,7766,287],{"class":219},[209,7768,2054],{"class":215},[209,7770,7771,7774,7776,7778,7780,7782,7784],{"class":211,"line":264},[209,7772,7773],{"class":215},"  nuxtApp.vueApp.",[209,7775,7105],{"class":257},[209,7777,362],{"class":215},[209,7779,234],{"class":233},[209,7781,6867],{"class":237},[209,7783,234],{"class":233},[209,7785,7786],{"class":215},", {\n",[209,7788,7789,7792,7794,7796,7798,7800,7802,7804,7806,7808,7810],{"class":211,"line":271},[209,7790,7791],{"class":257},"    mounted",[209,7793,309],{"class":219},[209,7795,416],{"class":215},[209,7797,5297],{"class":668},[209,7799,309],{"class":219},[209,7801,7455],{"class":1163},[209,7803,672],{"class":215},[209,7805,287],{"class":219},[209,7807,6864],{"class":215},[209,7809,6867],{"class":257},[209,7811,7812],{"class":215},"(),\n",[209,7814,7815,7818,7820,7822,7824,7827,7829,7831],{"class":211,"line":296},[209,7816,7817],{"class":257},"    getSSRProps",[209,7819,309],{"class":219},[209,7821,5342],{"class":215},[209,7823,287],{"class":219},[209,7825,7826],{"class":215}," ({ autofocus",[209,7828,309],{"class":219},[209,7830,4999],{"class":445},[209,7832,7833],{"class":215}," })\n",[209,7835,7836],{"class":211,"line":301},[209,7837,483],{"class":215},[209,7839,7840],{"class":211,"line":326},[209,7841,718],{"class":215},[16,7843,7844,7845,7848,7849,7852,7853,7855,7856,7859],{},"With this plugin, ",[20,7846,7847],{},"\u003Cinput v-focus \u002F>"," leaves the server as ",[20,7850,7851],{},"\u003Cinput autofocus>",". Without ",[20,7854,7737],{},", it would be a bare ",[20,7857,7858],{},"\u003Cinput>",", and focus would only arrive after hydration.",[16,7861,7862,7863,7866,7867,7870,7871,7874],{},"The Nuxt documentation asks you to register the directive on both client and server, unless it is only used on one side. For a browser-only directive, like ",[20,7864,7865],{},"v-click-outside",", create the plugin with a ",[20,7868,7869],{},".client"," suffix and a ",[20,7872,7873],{},".server"," plugin with an empty directive, only so Vue recognizes the name:",[120,7876,7878],{"className":1131,"code":7877,"language":238,"meta":126,"style":126},"\u002F\u002F app\u002Fplugins\u002Fclick-outside.client.ts\nimport { vClickOutside } from \"~\u002Fdirectives\u002FclickOutside\";\n\nexport default defineNuxtPlugin((nuxtApp) => {\n  nuxtApp.vueApp.directive(\"click-outside\", vClickOutside);\n});\n",[20,7879,7880,7885,7903,7907,7925,7943],{"__ignoreMap":126},[209,7881,7882],{"class":211,"line":212},[209,7883,7884],{"class":1139},"\u002F\u002F app\u002Fplugins\u002Fclick-outside.client.ts\n",[209,7886,7887,7889,7892,7894,7896,7899,7901],{"class":211,"line":246},[209,7888,4675],{"class":219},[209,7890,7891],{"class":215}," { vClickOutside } ",[209,7893,1661],{"class":219},[209,7895,459],{"class":233},[209,7897,7898],{"class":237},"~\u002Fdirectives\u002FclickOutside",[209,7900,234],{"class":233},[209,7902,1247],{"class":215},[209,7904,7905],{"class":211,"line":264},[209,7906,268],{"emptyLinePlaceholder":267},[209,7908,7909,7911,7913,7915,7917,7919,7921,7923],{"class":211,"line":271},[209,7910,1145],{"class":219},[209,7912,2762],{"class":219},[209,7914,7757],{"class":257},[209,7916,4064],{"class":215},[209,7918,7762],{"class":668},[209,7920,672],{"class":215},[209,7922,287],{"class":219},[209,7924,2054],{"class":215},[209,7926,7927,7929,7931,7933,7935,7938,7940],{"class":211,"line":296},[209,7928,7773],{"class":215},[209,7930,7105],{"class":257},[209,7932,362],{"class":215},[209,7934,234],{"class":233},[209,7936,7937],{"class":237},"click-outside",[209,7939,234],{"class":233},[209,7941,7942],{"class":215},", vClickOutside);\n",[209,7944,7945],{"class":211,"line":301},[209,7946,718],{"class":215},[120,7948,7950],{"className":1131,"code":7949,"language":238,"meta":126,"style":126},"\u002F\u002F app\u002Fplugins\u002Fclick-outside.server.ts\nexport default defineNuxtPlugin((nuxtApp) => {\n  nuxtApp.vueApp.directive(\"click-outside\", {});\n});\n",[20,7951,7952,7957,7975,7992],{"__ignoreMap":126},[209,7953,7954],{"class":211,"line":212},[209,7955,7956],{"class":1139},"\u002F\u002F app\u002Fplugins\u002Fclick-outside.server.ts\n",[209,7958,7959,7961,7963,7965,7967,7969,7971,7973],{"class":211,"line":246},[209,7960,1145],{"class":219},[209,7962,2762],{"class":219},[209,7964,7757],{"class":257},[209,7966,4064],{"class":215},[209,7968,7762],{"class":668},[209,7970,672],{"class":215},[209,7972,287],{"class":219},[209,7974,2054],{"class":215},[209,7976,7977,7979,7981,7983,7985,7987,7989],{"class":211,"line":264},[209,7978,7773],{"class":215},[209,7980,7105],{"class":257},[209,7982,362],{"class":215},[209,7984,234],{"class":233},[209,7986,7937],{"class":237},[209,7988,234],{"class":233},[209,7990,7991],{"class":215},", {});\n",[209,7993,7994],{"class":211,"line":271},[209,7995,718],{"class":215},[25,7997,7999],{"id":7998},"what-changed-in-directives-from-vue-2-to-vue-3","What Changed in Directives from Vue 2 to Vue 3?",[16,8001,8002],{},"Many older tutorials still use the Vue 2 hooks, and they do not work in Vue 3. Here is the mapping:",[918,8004,8005,8013,8020,8027,8034,8042],{},[921,8006,8007,8010,8011,702],{},[20,8008,8009],{},"bind"," became ",[20,8012,7152],{},[921,8014,8015,8010,8018,702],{},[20,8016,8017],{},"inserted",[20,8019,5125],{},[921,8021,8022,8010,8025,702],{},[20,8023,8024],{},"componentUpdated",[20,8026,7169],{},[921,8028,8029,8010,8032,702],{},[20,8030,8031],{},"unbind",[20,8033,7181],{},[921,8035,8036,8039,8040,702],{},[20,8037,8038],{},"update"," was removed, because it was redundant with ",[20,8041,7169],{},[921,8043,8044,3341,8046,139,8048,8050],{},[20,8045,7146],{},[20,8047,7163],{},[20,8049,7175],{}," are new.",[16,8052,3571,8053,8055,8056,8059,8060,587,8063,702],{},[20,8054,7191],{}," object also lost the ",[20,8057,8058],{},"expression"," string, and the component instance moved from ",[20,8061,8062],{},"vnode.context",[20,8064,8065],{},"binding.instance",[25,8067,8069],{"id":8068},"what-are-the-most-common-mistakes-with-directives","What Are the Most Common Mistakes with Directives?",[918,8071,8072,8083,8089,8095,8100,8103,8106],{},[921,8073,8074,8075,139,8077,8079,8080,8082],{},"Using ",[20,8076,5554],{},[20,8078,5578],{}," on the same element. ",[20,8081,5554],{}," is evaluated first, and the documentation does not recommend the combination. Filter the list in a computed property or split it into two elements.",[921,8084,8085,8086,8088],{},"Using the index as ",[20,8087,6439],{}," in lists that change.",[921,8090,8091,8092,8094],{},"Applying ",[20,8093,6701],{}," to text that came from a user.",[921,8096,8097,8098,702],{},"Registering a listener or timer without removing it in ",[20,8099,7181],{},[921,8101,8102],{},"Applying a custom directive to a component with more than one root element. The directive is ignored and Vue emits a warning. The documentation does not recommend custom directives on components.",[921,8104,8105],{},"Counting on DOM manipulation during SSR.",[921,8107,8108],{},"Creating a directive for something that would be simpler as a component.",[25,8110,1703],{"id":1702},[608,8112,8114],{"id":8113},"what-are-directives-in-vue-1","What are directives in Vue?",[16,8116,8117,8118,8120],{},"They are special template attributes, prefixed with ",[20,8119,5550],{},", that apply reactive behavior to an element, such as changing attributes, listening to events, repeating elements or controlling rendering.",[608,8122,8124],{"id":8123},"what-is-the-difference-between-v-if-and-v-show","What is the difference between v-if and v-show?",[16,8126,8127,8129,8130,8132,8133,8135,8136,8138,8139,8141],{},[20,8128,5554],{}," mounts and unmounts the block depending on the condition. ",[20,8131,5557],{}," keeps the element in the DOM and toggles ",[20,8134,6639],{},". Use ",[20,8137,5557],{}," for frequent toggles and ",[20,8140,5554],{}," when the condition rarely changes.",[608,8143,8145],{"id":8144},"can-i-use-the-array-index-as-the-key-in-v-for","Can I use the array index as the key in v-for?",[16,8147,8148],{},"Only in static lists that never change order or size. In lists that change, use a stable id from the item itself.",[608,8150,8152],{"id":8151},"how-do-i-create-a-custom-directive-with-script-setup","How do I create a custom directive with script setup?",[16,8154,8155,8156,8158,8159,8161,8162,8164],{},"Declare a camelCase variable that starts with ",[20,8157,6812],{},", like ",[20,8160,6816],{},", and use it in the template as ",[20,8163,6820],{},". Vue treats that as local registration.",[608,8166,8168],{"id":8167},"when-should-i-use-a-directive-instead-of-a-composable","When should I use a directive instead of a composable?",[16,8170,8171],{},"Use a directive when the logic has to manipulate a DOM element directly. If the logic has state or does not depend on a specific element, prefer a composable.",[608,8173,8175],{"id":8174},"do-custom-directives-work-with-ssr-in-nuxt","Do custom directives work with SSR in Nuxt?",[16,8177,8178,8179,8181,8182,8184,8185,8187],{},"The hooks that touch the DOM do not run on the server. To generate attributes in the server HTML, implement ",[20,8180,7737],{},". For a directive that only makes sense in the browser, register it in a ",[20,8183,7869],{}," plugin and create a ",[20,8186,7873],{}," stub.",[25,8189,1793],{"id":1792},[16,8191,8192,8193,8195,8196,139,8198,8200],{},"To pick the right tool, start with the element: if the logic has to touch it, use a directive; if it has state and can be reused, use a composable; if it has visual structure, use a component. Among the built-in directives, swap the index for a stable id in ",[20,8194,5578],{}," and choose between ",[20,8197,5554],{},[20,8199,5557],{}," by how often the toggle happens.",[16,8202,8203,8204,3341,8209,3341,8214,139,8219,702],{},"To go deeper, read the documentation pages on ",[1798,8205,8208],{"href":8206,"rel":8207},"https:\u002F\u002Fvuejs.org\u002Fguide\u002Freusability\u002Fcustom-directives.html",[1802],"custom directives",[1798,8210,8213],{"href":8211,"rel":8212},"https:\u002F\u002Fvuejs.org\u002Fguide\u002Fessentials\u002Fconditional.html",[1802],"conditional rendering",[1798,8215,8218],{"href":8216,"rel":8217},"https:\u002F\u002Fvuejs.org\u002Fguide\u002Fessentials\u002Flist.html",[1802],"list rendering",[1798,8220,8223],{"href":8221,"rel":8222},"https:\u002F\u002Fnuxt.com\u002Fdocs\u002F4.x\u002Fdirectory-structure\u002Fapp\u002Fplugins",[1802],"Nuxt plugins",[1805,8225,8226],{},"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 .shSDL, html code.shiki .shSDL{--shiki-default:#6272A4}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 .sIQBb, html code.shiki .sIQBb{--shiki-default:#BD93F9}html pre.shiki code .sGEwX, html code.shiki .sGEwX{--shiki-default:#FFB86C;--shiki-default-font-style:italic}html pre.shiki code .sCp4m, html code.shiki .sCp4m{--shiki-default:#8BE9FD;--shiki-default-font-style:italic}",{"title":126,"searchDepth":246,"depth":246,"links":8228},[8229,8230,8231,8241,8242,8249,8250,8251,8252,8253,8261],{"id":5566,"depth":246,"text":5567},{"id":5582,"depth":246,"text":5583},{"id":5789,"depth":246,"text":5790,"children":8232},[8233,8234,8235,8236,8237,8238,8239,8240],{"id":5793,"depth":264,"text":5794},{"id":5910,"depth":264,"text":5911},{"id":6060,"depth":264,"text":6061},{"id":6234,"depth":264,"text":6235},{"id":6443,"depth":264,"text":6444},{"id":6630,"depth":264,"text":6631},{"id":6687,"depth":264,"text":6688},{"id":6713,"depth":264,"text":6714},{"id":6754,"depth":246,"text":6755},{"id":6795,"depth":246,"text":6796,"children":8243},[8244,8245,8246,8247,8248],{"id":6802,"depth":264,"text":6803},{"id":6922,"depth":264,"text":6923},{"id":7135,"depth":264,"text":7136},{"id":7234,"depth":264,"text":7235},{"id":7400,"depth":264,"text":7401},{"id":7712,"depth":246,"text":7713},{"id":7722,"depth":246,"text":7723},{"id":7998,"depth":246,"text":7999},{"id":8068,"depth":246,"text":8069},{"id":1702,"depth":246,"text":1703,"children":8254},[8255,8256,8257,8258,8259,8260],{"id":8113,"depth":264,"text":8114},{"id":8123,"depth":264,"text":8124},{"id":8144,"depth":264,"text":8145},{"id":8151,"depth":264,"text":8152},{"id":8167,"depth":264,"text":8168},{"id":8174,"depth":264,"text":8175},{"id":1792,"depth":246,"text":1793},[1835],"Learn how to use built-in directives and create custom directives in Vue 3, with hooks, keys, modifiers and the SSR caveats in Nuxt, plus practical examples.","what-are-directives-in-vue-and-how-to-implement-them",{},"o-que-sao-diretivas-no-vue-e-como-implementa-las","2025-03-21",{"title":5538,"description":8263},{"loc":5379,"lastmod":1844},"en\u002Fblog\u002Fwhat-are-directives-in-vue-and-how-to-implement-them",1985,"MP9b1GahnutCQGJ7G6uaQ8OKz9FbuFu6Qf0wfyxt85A",{"id":8274,"title":8275,"articleCover":8276,"articleCoverEmbed":8,"articleCoverEmbedUploadDate":8,"articleCoverHeight":5540,"articleCoverWidth":5541,"authorId":5542,"body":8277,"categoriesIds":9264,"description":9265,"enAlternateSlug":9266,"extension":1837,"meta":9267,"navigation":267,"path":5129,"ptBrAlternateSlug":9268,"publishedAt":9269,"seo":9270,"sitemap":9271,"stem":9274,"updatedAt":1844,"wordCount":9275,"__hash__":9276},"blogEn\u002Fen\u002Fblog\u002Fwhat-is-the-difference-between-composition-api-and-options-api-in-vuejs.md","Composition API vs Options API in Vue.js: What's the Difference?","\u002Fimages\u002Fen\u002Fblog\u002Fvue\u002Fwhat-is-the-difference-between-composition-api-and-options-api-in-vuejs.webp",{"type":13,"value":8278,"toc":9253},[8279,8289,8293,8296,8299,8302,8609,8613,8630,8650,8653,8687,8691,8694,8697,8700,8956,8959,8988,8991,9024,9027,9033,9037,9045,9140,9144,9149,9188,9193,9237,9241,9244,9247,9250],[16,8280,8281,8282,3686,8285,8288],{},"In web development, the choice of framework and tools is crucial for the structure, scalability, and maintenance of a project. Two strategies stand out when we talk about Vue.js: the ",[3891,8283,8284],{},"Options API",[3891,8286,8287],{},"Composition API",". Both provide different ways to organize and manage the logic of an application, each with its own benefits and specific use cases.",[25,8290,8292],{"id":8291},"options-api-the-essence-of-vue","Options API: The Essence of Vue",[16,8294,8295],{},"The Options API is the traditional way of using Vue.js. Present since the beginning, its syntax is based on an options object to structure the properties of a component.",[16,8297,8298],{},"It may seem complicated, but in practice, it's simpler than it seems!",[16,8300,8301],{},"Here's how it looks:",[120,8303,8305],{"className":203,"code":8304,"language":205,"meta":126,"style":126},"\u003Ctemplate>\n  \u003Cdiv>\n    \u003Cp>Count: {{ count }}\u003C\u002Fp>\n    \u003Cbr \u002F>\n    \u003Cbutton v-on:click=\"increment\">Increment\u003C\u002Fbutton>\n    \u003Cbutton v-on:click=\"reset\">Reset\u003C\u002Fbutton>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript>\nexport default {\n  \u002F\u002F Component state\n  data() {\n    return {\n      count: 0\n    };\n  },\n\n  \u002F\u002F Component methods\n  methods: {\n    increment() {\n      this.count++;\n    },\n    reset() {\n      this.count = 0;\n    }\n  },\n\n  \u002F\u002F Computed property\n  computed: {\n    doubleCount() {\n      return this.count * 2;\n    }\n  }\n};\n\u003C\u002Fscript>\n",[20,8306,8307,8315,8323,8336,8345,8370,8392,8400,8408,8412,8420,8428,8433,8440,8446,8456,8460,8464,8468,8473,8482,8489,8503,8507,8514,8527,8532,8537,8542,8548,8558,8566,8585,8590,8595,8600],{"__ignoreMap":126},[209,8308,8309,8311,8313],{"class":211,"line":212},[209,8310,216],{"class":215},[209,8312,512],{"class":219},[209,8314,243],{"class":215},[209,8316,8317,8319,8321],{"class":211,"line":246},[209,8318,520],{"class":215},[209,8320,774],{"class":219},[209,8322,243],{"class":215},[209,8324,8325,8327,8329,8332,8334],{"class":211,"line":264},[209,8326,543],{"class":215},[209,8328,16],{"class":219},[209,8330,8331],{"class":215},">Count: {{ count }}\u003C\u002F",[209,8333,16],{"class":219},[209,8335,243],{"class":215},[209,8337,8338,8340,8343],{"class":211,"line":271},[209,8339,543],{"class":215},[209,8341,8342],{"class":219},"br",[209,8344,879],{"class":215},[209,8346,8347,8349,8351,8354,8356,8358,8361,8363,8366,8368],{"class":211,"line":296},[209,8348,543],{"class":215},[209,8350,1622],{"class":219},[209,8352,8353],{"class":223}," v-on:click",[209,8355,230],{"class":219},[209,8357,234],{"class":233},[209,8359,8360],{"class":237},"increment",[209,8362,234],{"class":233},[209,8364,8365],{"class":215},">Increment\u003C\u002F",[209,8367,1622],{"class":219},[209,8369,243],{"class":215},[209,8371,8372,8374,8376,8378,8380,8382,8384,8386,8388,8390],{"class":211,"line":301},[209,8373,543],{"class":215},[209,8375,1622],{"class":219},[209,8377,8353],{"class":223},[209,8379,230],{"class":219},[209,8381,234],{"class":233},[209,8383,6122],{"class":237},[209,8385,234],{"class":233},[209,8387,6127],{"class":215},[209,8389,1622],{"class":219},[209,8391,243],{"class":215},[209,8393,8394,8396,8398],{"class":211,"line":326},[209,8395,559],{"class":215},[209,8397,774],{"class":219},[209,8399,243],{"class":215},[209,8401,8402,8404,8406],{"class":211,"line":352},[209,8403,495],{"class":215},[209,8405,512],{"class":219},[209,8407,243],{"class":215},[209,8409,8410],{"class":211,"line":399},[209,8411,268],{"emptyLinePlaceholder":267},[209,8413,8414,8416,8418],{"class":211,"line":405},[209,8415,216],{"class":215},[209,8417,220],{"class":219},[209,8419,243],{"class":215},[209,8421,8422,8424,8426],{"class":211,"line":410},[209,8423,1145],{"class":219},[209,8425,2762],{"class":219},[209,8427,2054],{"class":215},[209,8429,8430],{"class":211,"line":425},[209,8431,8432],{"class":1139},"  \u002F\u002F Component state\n",[209,8434,8435,8438],{"class":211,"line":437},[209,8436,8437],{"class":257},"  data",[209,8439,1508],{"class":215},[209,8441,8442,8444],{"class":211,"line":451},[209,8443,4113],{"class":219},[209,8445,2054],{"class":215},[209,8447,8448,8451,8453],{"class":211,"line":469},[209,8449,8450],{"class":215},"      count",[209,8452,309],{"class":219},[209,8454,8455],{"class":445}," 0\n",[209,8457,8458],{"class":211,"line":480},[209,8459,7603],{"class":215},[209,8461,8462],{"class":211,"line":486},[209,8463,3245],{"class":215},[209,8465,8466],{"class":211,"line":492},[209,8467,268],{"emptyLinePlaceholder":267},[209,8469,8470],{"class":211,"line":502},[209,8471,8472],{"class":1139},"  \u002F\u002F Component methods\n",[209,8474,8475,8478,8480],{"class":211,"line":507},[209,8476,8477],{"class":215},"  methods",[209,8479,309],{"class":219},[209,8481,2054],{"class":215},[209,8483,8484,8487],{"class":211,"line":517},[209,8485,8486],{"class":257},"    increment",[209,8488,1508],{"class":215},[209,8490,8491,8495,8498,8501],{"class":211,"line":540},[209,8492,8494],{"class":8493},"sqerP","      this",[209,8496,8497],{"class":215},".count",[209,8499,8500],{"class":219},"++",[209,8502,1247],{"class":215},[209,8504,8505],{"class":211,"line":556},[209,8506,4309],{"class":215},[209,8508,8509,8512],{"class":211,"line":566},[209,8510,8511],{"class":257},"    reset",[209,8513,1508],{"class":215},[209,8515,8516,8518,8521,8523,8525],{"class":211,"line":7691},[209,8517,8494],{"class":8493},[209,8519,8520],{"class":215},".count ",[209,8522,230],{"class":219},[209,8524,4090],{"class":445},[209,8526,1247],{"class":215},[209,8528,8530],{"class":211,"line":8529},26,[209,8531,7684],{"class":215},[209,8533,8535],{"class":211,"line":8534},27,[209,8536,3245],{"class":215},[209,8538,8540],{"class":211,"line":8539},28,[209,8541,268],{"emptyLinePlaceholder":267},[209,8543,8545],{"class":211,"line":8544},29,[209,8546,8547],{"class":1139},"  \u002F\u002F Computed property\n",[209,8549,8551,8554,8556],{"class":211,"line":8550},30,[209,8552,8553],{"class":215},"  computed",[209,8555,309],{"class":219},[209,8557,2054],{"class":215},[209,8559,8561,8564],{"class":211,"line":8560},31,[209,8562,8563],{"class":257},"    doubleCount",[209,8565,1508],{"class":215},[209,8567,8569,8572,8575,8577,8580,8583],{"class":211,"line":8568},32,[209,8570,8571],{"class":219},"      return",[209,8573,8574],{"class":8493}," this",[209,8576,8520],{"class":215},[209,8578,8579],{"class":219},"*",[209,8581,8582],{"class":445}," 2",[209,8584,1247],{"class":215},[209,8586,8588],{"class":211,"line":8587},33,[209,8589,7684],{"class":215},[209,8591,8593],{"class":211,"line":8592},34,[209,8594,2212],{"class":215},[209,8596,8598],{"class":211,"line":8597},35,[209,8599,6874],{"class":215},[209,8601,8603,8605,8607],{"class":211,"line":8602},36,[209,8604,495],{"class":215},[209,8606,220],{"class":219},[209,8608,243],{"class":215},[608,8610,8612],{"id":8611},"structure","Structure:",[918,8614,8615,8620,8625],{},[921,8616,8617,8619],{},[20,8618,5119],{},": Defines the reactive data of the component, i.e., where the variables that will have dynamic values are declared.",[921,8621,8622,8624],{},[20,8623,5122],{},": Where we specify custom functions. These functions can change states, react to user events, and perform specific logic.",[921,8626,8627,8629],{},[20,8628,578],{},": Defines computed properties that derive from data, i.e., values that only need to be calculated once and updated only when a related variable changes.",[16,8631,8632,8633,3341,8635,3341,8638,3341,8641,3341,8644,3362,8646,8649],{},"Vue.js also has other options, such as ",[20,8634,2859],{},[20,8636,8637],{},"components",[20,8639,8640],{},"props",[20,8642,8643],{},"directives",[20,8645,4627],{},[20,8647,8648],{},"emits",", among others. However, we can explore these aspects further in a future article.",[16,8651,8652],{},"Let's examine some of the properties and limitations presented by the Options API:",[918,8654,8655,8674,8684],{},[921,8656,8657,8658,8661,8662,8664,8665,8668,8669,139,8671,8673],{},"Access to data through ",[20,8659,8660],{},"this",": Variables defined in the ",[20,8663,5119],{}," property can only be accessed through the ",[20,8666,8667],{},"this."," context, as illustrated in the ",[20,8670,8360],{},[20,8672,6122],{}," methods.",[921,8675,8676,8677,3341,8679,3341,8681,8683],{},"Rigid code division: Functions and variables are categorized into specific sections, such as ",[20,8678,5119],{},[20,8680,5122],{},[20,8682,578],{},". This can split the logic associated with similar functionality, making it harder to read and maintain the code in larger-scale projects.",[921,8685,8686],{},"Difficulties in reusing logic: The Options API uses Mixins to share logic between components, which can result in naming conflicts and issues in code traceability.",[25,8688,8690],{"id":8689},"composition-api-flexibility-and-practicality","Composition API: Flexibility and Practicality",[16,8692,8693],{},"The Composition API is the new way to structure code, introduced in Vue 3. With the Composition API, you can structure your component's logic in a more transparent and intuitive way.",[16,8695,8696],{},"Let's see how much has really changed and clarify any doubts about the Composition API.",[16,8698,8699],{},"I'll create the same application, but this time with the Composition API. Observe how it looks:",[120,8701,8703],{"className":203,"code":8702,"language":205,"meta":126,"style":126},"\u003Ctemplate>\n  \u003Cdiv>\n    \u003Cp>Count: {{ count }}\u003C\u002Fp>\n    \u003Cbr \u002F>\n    \u003Cbutton v-on:click=\"increment\">Increment\u003C\u002Fbutton>\n    \u003Cbutton v-on:click=\"reset\">Reset\u003C\u002Fbutton>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript setup>\nimport { ref, computed } from \"vue\";\n\n\u002F\u002F Reactive state\nconst count = ref(0);\n\n\u002F\u002F Methods\nfunction increment() {\n  count.value++;\n}\n\nfunction reset() {\n  count.value = 0;\n}\n\n\u002F\u002F Computed property\nconst doubleCount = computed(() => count.value * 2);\n\u003C\u002Fscript>\n",[20,8704,8705,8713,8721,8733,8741,8763,8785,8793,8801,8805,8815,8832,8836,8841,8857,8861,8866,8875,8884,8888,8892,8900,8911,8915,8919,8924,8948],{"__ignoreMap":126},[209,8706,8707,8709,8711],{"class":211,"line":212},[209,8708,216],{"class":215},[209,8710,512],{"class":219},[209,8712,243],{"class":215},[209,8714,8715,8717,8719],{"class":211,"line":246},[209,8716,520],{"class":215},[209,8718,774],{"class":219},[209,8720,243],{"class":215},[209,8722,8723,8725,8727,8729,8731],{"class":211,"line":264},[209,8724,543],{"class":215},[209,8726,16],{"class":219},[209,8728,8331],{"class":215},[209,8730,16],{"class":219},[209,8732,243],{"class":215},[209,8734,8735,8737,8739],{"class":211,"line":271},[209,8736,543],{"class":215},[209,8738,8342],{"class":219},[209,8740,879],{"class":215},[209,8742,8743,8745,8747,8749,8751,8753,8755,8757,8759,8761],{"class":211,"line":296},[209,8744,543],{"class":215},[209,8746,1622],{"class":219},[209,8748,8353],{"class":223},[209,8750,230],{"class":219},[209,8752,234],{"class":233},[209,8754,8360],{"class":237},[209,8756,234],{"class":233},[209,8758,8365],{"class":215},[209,8760,1622],{"class":219},[209,8762,243],{"class":215},[209,8764,8765,8767,8769,8771,8773,8775,8777,8779,8781,8783],{"class":211,"line":301},[209,8766,543],{"class":215},[209,8768,1622],{"class":219},[209,8770,8353],{"class":223},[209,8772,230],{"class":219},[209,8774,234],{"class":233},[209,8776,6122],{"class":237},[209,8778,234],{"class":233},[209,8780,6127],{"class":215},[209,8782,1622],{"class":219},[209,8784,243],{"class":215},[209,8786,8787,8789,8791],{"class":211,"line":326},[209,8788,559],{"class":215},[209,8790,774],{"class":219},[209,8792,243],{"class":215},[209,8794,8795,8797,8799],{"class":211,"line":352},[209,8796,495],{"class":215},[209,8798,512],{"class":219},[209,8800,243],{"class":215},[209,8802,8803],{"class":211,"line":399},[209,8804,268],{"emptyLinePlaceholder":267},[209,8806,8807,8809,8811,8813],{"class":211,"line":405},[209,8808,216],{"class":215},[209,8810,220],{"class":219},[209,8812,224],{"class":223},[209,8814,243],{"class":215},[209,8816,8817,8819,8822,8824,8826,8828,8830],{"class":211,"line":410},[209,8818,4675],{"class":219},[209,8820,8821],{"class":215}," { ref, computed } ",[209,8823,1661],{"class":219},[209,8825,459],{"class":233},[209,8827,205],{"class":237},[209,8829,234],{"class":233},[209,8831,1247],{"class":215},[209,8833,8834],{"class":211,"line":425},[209,8835,268],{"emptyLinePlaceholder":267},[209,8837,8838],{"class":211,"line":437},[209,8839,8840],{"class":1139},"\u002F\u002F Reactive state\n",[209,8842,8843,8845,8847,8849,8851,8853,8855],{"class":211,"line":451},[209,8844,249],{"class":219},[209,8846,4699],{"class":215},[209,8848,230],{"class":219},[209,8850,4704],{"class":257},[209,8852,362],{"class":215},[209,8854,1223],{"class":445},[209,8856,402],{"class":215},[209,8858,8859],{"class":211,"line":469},[209,8860,268],{"emptyLinePlaceholder":267},[209,8862,8863],{"class":211,"line":480},[209,8864,8865],{"class":1139},"\u002F\u002F Methods\n",[209,8867,8868,8870,8873],{"class":211,"line":486},[209,8869,6202],{"class":219},[209,8871,8872],{"class":257}," increment",[209,8874,1508],{"class":215},[209,8876,8877,8880,8882],{"class":211,"line":492},[209,8878,8879],{"class":215},"  count.value",[209,8881,8500],{"class":219},[209,8883,1247],{"class":215},[209,8885,8886],{"class":211,"line":502},[209,8887,489],{"class":215},[209,8889,8890],{"class":211,"line":507},[209,8891,268],{"emptyLinePlaceholder":267},[209,8893,8894,8896,8898],{"class":211,"line":517},[209,8895,6202],{"class":219},[209,8897,6205],{"class":257},[209,8899,1508],{"class":215},[209,8901,8902,8905,8907,8909],{"class":211,"line":540},[209,8903,8904],{"class":215},"  count.value ",[209,8906,230],{"class":219},[209,8908,4090],{"class":445},[209,8910,1247],{"class":215},[209,8912,8913],{"class":211,"line":556},[209,8914,489],{"class":215},[209,8916,8917],{"class":211,"line":566},[209,8918,268],{"emptyLinePlaceholder":267},[209,8920,8921],{"class":211,"line":7691},[209,8922,8923],{"class":1139},"\u002F\u002F Computed property\n",[209,8925,8926,8928,8931,8933,8935,8937,8939,8942,8944,8946],{"class":211,"line":8529},[209,8927,249],{"class":219},[209,8929,8930],{"class":215}," doubleCount ",[209,8932,230],{"class":219},[209,8934,281],{"class":257},[209,8936,284],{"class":215},[209,8938,287],{"class":219},[209,8940,8941],{"class":215}," count.value ",[209,8943,8579],{"class":219},[209,8945,8582],{"class":445},[209,8947,402],{"class":215},[209,8949,8950,8952,8954],{"class":211,"line":8534},[209,8951,495],{"class":215},[209,8953,220],{"class":219},[209,8955,243],{"class":215},[608,8957,8612],{"id":8958},"structure-1",[918,8960,8961,8969,8977],{},[921,8962,8963,8968],{},[3891,8964,8965],{},[20,8966,8967],{},"\u003Cscript setup\u002F>",": Where all the logic and code that will not be rendered to the user is placed.",[921,8970,8971,8976],{},[3891,8972,8973],{},[20,8974,8975],{},"ref()",": Defines whether a variable will be reactive or not.",[921,8978,8979,8984,8985,8987],{},[3891,8980,8981],{},[20,8982,8983],{},"computed()",": Performs the same function as the ",[20,8986,578],{}," in the Options API.",[16,8989,8990],{},"Let's explore some of the functionalities brought by the Composition API:",[918,8992,8993,8996,9009],{},[921,8994,8995],{},"It is necessary to import what we are going to use.",[921,8997,8998,8999,9001,9002,9005,9006,9008],{},"We need to use the ",[20,9000,8975],{}," method to make the variable reactive. For example, if ",[20,9003,9004],{},"count"," did not have ",[20,9007,8975],{},", when updated, it would not update automatically and would require a page reload.",[921,9010,9011,9012,9014,9015,9018,9019,139,9021,9023],{},"The data of variables that use ",[20,9013,8975],{}," can only be accessed using ",[20,9016,9017],{},".value",", as used in the ",[20,9020,8360],{},[20,9022,6122],{}," functions.",[16,9025,9026],{},"Below is a gif of the application we created in this article with Vue.js:",[1935,9028],{"alt":9029,"height":9030,"src":9031,"width":9032},"Simple application created for the article using Vue.js APIs",450,"images\u002Fblog\u002Fvue\u002Fdemonstracao-do-aplicativo-criado-no-artigo-com-as-apis-do-vue.gif",800,[25,9034,9036],{"id":9035},"detailed-comparison","Detailed Comparison",[16,9038,9039,9040,139,9042,9044],{},"Each API in Vue.js has unique characteristics that affect how the code is organized, managed, and interpreted. The choice between ",[3891,9041,8284],{},[3891,9043,8287],{}," mainly impacts code structuring and readability. Below, we highlight the main differences between them:",[40,9046,9047,9058],{},[43,9048,9049],{},[46,9050,9051,9054,9056],{},[49,9052,9053],{},"Characteristic",[49,9055,8284],{},[49,9057,8287],{},[56,9059,9060,9080,9101,9114,9127],{},[46,9061,9062,9067,9077],{},[61,9063,9064],{},[3891,9065,9066],{},"Code Structuring",[61,9068,9069,9070,3341,9072,3341,9074,9076],{},"Structures code according to the type of option (",[20,9071,5119],{},[20,9073,5122],{},[20,9075,578],{},", etc.), potentially fragmenting logic into complex parts.",[61,9078,9079],{},"Organizes code by function, improving interpretation and aligning it with modern JavaScript.",[46,9081,9082,9087,9092],{},[61,9083,9084],{},[3891,9085,9086],{},"Reactivity",[61,9088,9089,9090,702],{},"Automatically manages reactive properties defined in ",[20,9091,5119],{},[61,9093,9094,9095,139,9097,9100],{},"Requires explicit use of ",[20,9096,8975],{},[20,9098,9099],{},"reactive()"," to make variables reactive.",[46,9102,9103,9108,9111],{},[61,9104,9105],{},[3891,9106,9107],{},"Readability",[61,9109,9110],{},"For those experienced in Object-Oriented Programming (OOP), it may be more familiar, though it can be less intuitive for large-scale projects.",[61,9112,9113],{},"Ideal for developers experienced in JavaScript, and provides exceptional support for TypeScript.",[46,9115,9116,9121,9124],{},[61,9117,9118],{},[3891,9119,9120],{},"Learning Curve",[61,9122,9123],{},"Perfect for beginners, as it follows an organized and predictable structure, where each component function is divided into specific blocks.",[61,9125,9126],{},"Has a steeper learning curve, as it requires a deeper understanding of concepts like reactivity. However, it becomes more intuitive with practice.",[46,9128,9129,9134,9137],{},[61,9130,9131],{},[3891,9132,9133],{},"Maintenance and Scalability",[61,9135,9136],{},"In large-scale projects, maintenance can become challenging, as the logic of a component is fragmented into several sections, making it harder to read and debug.",[61,9138,9139],{},"Ideal for scalable projects, as it simplifies code organization and allows for modular and reusable responsibility division.",[25,9141,9143],{"id":9142},"but-when-to-use-each-api","But when to use each API?",[16,9145,9146],{},[3891,9147,9148],{},"The Options API is ideal for:",[918,9150,9151,9164,9170,9176,9182],{},[921,9152,9153,9156,9157,3341,9159,3341,9161,9163],{},[3891,9154,9155],{},"More structured and declarative code"," - Ideal for those who prefer a clear distinction between state, methods, and computed properties (such as ",[20,9158,5119],{},[20,9160,5122],{},[20,9162,578],{},", etc.).",[921,9165,9166,9169],{},[3891,9167,9168],{},"Easy learning"," - If you are starting with Vue.js, this methodology is more intuitive and organized, following a clearly established pattern.",[921,9171,9172,9175],{},[3891,9173,9174],{},"Compatibility with older libraries"," - Some Vue libraries still rely on the Options API, making this option more practical to prevent compatibility issues.",[921,9177,9178,9181],{},[3891,9179,9180],{},"Simplified debugging"," - Due to the fixed structure of the code, it becomes easier to understand the flow and identify failures, especially for those accustomed to Vue.js's conventional architecture.",[921,9183,9184,9187],{},[3891,9185,9186],{},"Effective resource use in smaller projects"," - In smaller-scale applications, the Options API provides a straightforward and efficient solution, free of extra complexity.",[16,9189,9190],{},[3891,9191,9192],{},"The Composition API is more recommended for:",[918,9194,9195,9205,9216,9225,9231],{},[921,9196,9197,9200,9201,9204],{},[3891,9198,9199],{},"Greater adaptability and code reuse"," - Through ",[3891,9202,9203],{},"Functions",", we can share logic between components without the limitations of Mixins in the Options API.",[921,9206,9207,9210,9211,3341,9213,9215],{},[3891,9208,9209],{},"Improved organization in large projects"," - Instead of segmenting code by options (",[20,9212,5119],{},[20,9214,5122],{},", etc.), logic can be grouped by functionality, simplifying maintenance.",[921,9217,9218,9221,9222,9224],{},[3891,9219,9220],{},"Advanced reactivity management"," - By using ",[20,9223,8975],{},", you gain more control over the state.",[921,9226,9227,9230],{},[3891,9228,9229],{},"Ease in creating custom hooks"," - The Composition API allows the creation of reusable hooks, making the structure more modular and simplifying code maintenance.",[921,9232,9233,9236],{},[3891,9234,9235],{},"Enhanced experience with TypeScript"," - If you plan to use TypeScript in your project, the Composition API integrates more smoothly, providing more precise typing and improved support for type inference.",[25,9238,9240],{"id":9239},"final-considerations","Final Considerations",[16,9242,9243],{},"Now that you know the differences between the Options API and the Composition API, you are ready to choose the best option for your project. Remember that each has its advantages and disadvantages, and everything depends on the complexity of your project.",[16,9245,9246],{},"However, when it comes to personal preference, I generally prefer using the Composition API in my personal projects, due to its optimization with TypeScript and the ability to group logic.",[16,9248,9249],{},"Regardless of the API chosen, Vue.js remains a powerful and flexible tool for Front-End development. 🚀",[1805,9251,9252],{},"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 .shSDL, html code.shiki .shSDL{--shiki-default:#6272A4}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 pre.shiki code .sqerP, html code.shiki .sqerP{--shiki-default:#BD93F9;--shiki-default-font-style:italic}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":9254},[9255,9258,9261,9262,9263],{"id":8291,"depth":246,"text":8292,"children":9256},[9257],{"id":8611,"depth":264,"text":8612},{"id":8689,"depth":246,"text":8690,"children":9259},[9260],{"id":8958,"depth":264,"text":8612},{"id":9035,"depth":246,"text":9036},{"id":9142,"depth":246,"text":9143},{"id":9239,"depth":246,"text":9240},[1835],"The main differences, advantages and practical uses of the Composition API and Options API in Vue.js, and when to choose each one for your Vue projects.","what-is-the-difference-between-composition-api-and-options-api-in-vuejs",{},"qual-a-diferenca-entre-a-composition-api-e-options-api-no-vuejs","2025-02-20",{"title":8275,"description":9265},{"loc":5129,"images":9272,"lastmod":1844},[9273],{"loc":9031},"en\u002Fblog\u002Fwhat-is-the-difference-between-composition-api-and-options-api-in-vuejs",1116,"nG0MKJOH288W3J_TYpNbak5gQlviE0AzgmNbo70PzNE",1791682338524]