[{"data":1,"prerenderedAt":1953},["ShallowReactive",2],{"en-article-\u002Fen\u002Fblog\u002Fhow-the-routing-system-works-in-nuxt":3,"en-related-posts-\u002Fen\u002Fblog\u002Fhow-the-routing-system-works-in-nuxt":1847},{"id":4,"title":5,"articleCover":6,"articleCoverEmbed":7,"articleCoverEmbedUploadDate":7,"articleCoverHeight":8,"articleCoverWidth":9,"authorId":10,"body":11,"categoriesIds":1832,"description":1835,"enAlternateSlug":195,"extension":1836,"meta":1837,"navigation":266,"path":1838,"ptBrAlternateSlug":1839,"publishedAt":1840,"seo":1841,"sitemap":1842,"stem":1844,"updatedAt":1843,"wordCount":1845,"__hash__":1846},"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":12,"value":13,"toc":1807},"minimark",[14,23,28,38,82,108,111,115,118,128,143,147,150,186,197,200,572,599,606,611,625,726,731,735,738,741,747,754,895,901,905,916,939,943,950,983,986,990,1003,1082,1085,1089,1099,1105,1128,1251,1354,1361,1365,1368,1446,1457,1461,1470,1649,1669,1673,1699,1703,1707,1720,1724,1733,1737,1750,1754,1766,1770,1789,1793,1803],[15,16,17,18,22],"p",{},"Nuxt turns the files inside the ",[19,20,21],"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.",[24,25,27],"h2",{"id":26},"how-file-based-routing-works-in-nuxt","How File-Based Routing Works in Nuxt",[15,29,30,31,34,35,37],{},"Every ",[19,32,33],{},".vue"," file inside the ",[19,36,21],{}," folder becomes a page, and its URL comes from the file path:",[39,40,41,54],"table",{},[42,43,44],"thead",{},[45,46,47,51],"tr",{},[48,49,50],"th",{},"File",[48,52,53],{},"Route",[55,56,57,66,74],"tbody",{},[45,58,59,63],{},[60,61,62],"td",{},"app\u002Fpages\u002Findex.vue",[60,64,65],{},"\u002F",[45,67,68,71],{},[60,69,70],{},"app\u002Fpages\u002Flinks.vue",[60,72,73],{},"\u002Flinks",[45,75,76,79],{},[60,77,78],{},"app\u002Fpages\u002Ftools\u002Fcpf-generator.vue",[60,80,81],{},"\u002Ftools\u002Fcpf-generator",[15,83,84,85,88,89,92,93,96,97,100,101,104,105,107],{},"In Nuxt 4, new projects keep application code inside an ",[19,86,87],{},"app\u002F"," directory, so pages live in ",[19,90,91],{},"app\u002Fpages\u002F",", layouts in ",[19,94,95],{},"app\u002Flayouts\u002F"," and route middleware in ",[19,98,99],{},"app\u002Fmiddleware\u002F",". If you migrated from Nuxt 3 and still have a top-level ",[19,102,103],{},"pages\u002F"," folder, it keeps working: Nuxt detects the old structure and does not force the new one. The examples below use the ",[19,106,87],{}," paths.",[15,109,110],{},"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.",[24,112,114],{"id":113},"static-routes","Static Routes",[15,116,117],{},"A static route is a page whose URL never changes, such as an about page or a tool page:",[119,120,126],"pre",{"className":121,"code":123,"language":124,"meta":125},[122],"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","",[19,127,123],{"__ignoreMap":125},[15,129,130,131,134,135,138,139,142],{},"If the base URL is ",[19,132,133],{},"https:\u002F\u002Fonlinestore.com",", Nuxt serves ",[19,136,137],{},"https:\u002F\u002Fonlinestore.com\u002Fabout"," and ",[19,140,141],{},"https:\u002F\u002Fonlinestore.com\u002Fprivacy-policy"," from these files.",[24,144,146],{"id":145},"dynamic-routes","Dynamic Routes",[15,148,149],{},"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:",[39,151,152,163],{},[42,153,154],{},[45,155,156,159,161],{},[48,157,158],{},"Parameter location",[48,160,50],{},[48,162,53],{},[55,164,165,176],{},[45,166,167,170,173],{},[60,168,169],{},"File name",[60,171,172],{},"app\u002Fpages\u002Fblog\u002F[slug].vue",[60,174,175],{},"\u002Fblog\u002Fmy-post",[45,177,178,181,184],{},[60,179,180],{},"Directory name",[60,182,183],{},"app\u002Fpages\u002Fblog\u002F[slug]\u002Findex.vue",[60,185,175],{},[15,187,188,189,192,193,196],{},"The name inside the brackets is the key you read from ",[19,190,191],{},"route.params",". A slug is the readable part of a URL that identifies a resource, usually derived from its title, like ",[19,194,195],{},"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.",[15,198,199],{},"Here is a post page that loads its content from Nuxt Content using the slug:",[119,201,205],{"className":202,"code":203,"language":204,"meta":125,"style":125},"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",[19,206,207,243,261,268,293,298,323,349,396,402,407,422,434,448,466,477,483,489,499,504,514,537,553,563],{"__ignoreMap":125},[208,209,212,216,220,224,227,230,234,238,240],"span",{"class":210,"line":211},"line",1,[208,213,215],{"class":214},"sCdxs","\u003C",[208,217,219],{"class":218},"s0Tla","script",[208,221,223],{"class":222},"sY_PY"," setup",[208,225,226],{"class":222}," lang",[208,228,229],{"class":218},"=",[208,231,233],{"class":232},"seVfx","\"",[208,235,237],{"class":236},"s-mGx","ts",[208,239,233],{"class":232},[208,241,242],{"class":214},">\n",[208,244,246,249,252,254,258],{"class":210,"line":245},2,[208,247,248],{"class":218},"const",[208,250,251],{"class":214}," route ",[208,253,229],{"class":218},[208,255,257],{"class":256},"sAOxA"," useRoute",[208,259,260],{"class":214},"();\n",[208,262,264],{"class":210,"line":263},3,[208,265,267],{"emptyLinePlaceholder":266},true,"\n",[208,269,271,273,276,278,281,284,287,290],{"class":210,"line":270},4,[208,272,248],{"class":218},[208,274,275],{"class":214}," slug ",[208,277,229],{"class":218},[208,279,280],{"class":256}," computed",[208,282,283],{"class":214},"(() ",[208,285,286],{"class":218},"=>",[208,288,289],{"class":256}," String",[208,291,292],{"class":214},"(route.params.slug));\n",[208,294,296],{"class":210,"line":295},5,[208,297,267],{"emptyLinePlaceholder":266},[208,299,301,303,306,309,312,314,317,320],{"class":210,"line":300},6,[208,302,248],{"class":218},[208,304,305],{"class":214}," { data",[208,307,308],{"class":218},":",[208,310,311],{"class":214}," post } ",[208,313,229],{"class":218},[208,315,316],{"class":218}," await",[208,318,319],{"class":256}," useAsyncData",[208,321,322],{"class":214},"(\n",[208,324,326,329,331,334,337,340,343,346],{"class":210,"line":325},7,[208,327,328],{"class":214},"  () ",[208,330,286],{"class":218},[208,332,333],{"class":236}," `post-",[208,335,336],{"class":218},"${",[208,338,339],{"class":214},"slug.value",[208,341,342],{"class":218},"}",[208,344,345],{"class":236},"`",[208,347,348],{"class":214},",\n",[208,350,352,354,356,359,362,364,367,369,372,375,377,380,382,384,386,388,390,393],{"class":210,"line":351},8,[208,353,328],{"class":214},[208,355,286],{"class":218},[208,357,358],{"class":256}," queryCollection",[208,360,361],{"class":214},"(",[208,363,233],{"class":232},[208,365,366],{"class":236},"blog",[208,368,233],{"class":232},[208,370,371],{"class":214},").",[208,373,374],{"class":256},"path",[208,376,361],{"class":214},[208,378,379],{"class":236},"`\u002Fblog\u002F",[208,381,336],{"class":218},[208,383,339],{"class":214},[208,385,342],{"class":218},[208,387,345],{"class":236},[208,389,371],{"class":214},[208,391,392],{"class":256},"first",[208,394,395],{"class":214},"()\n",[208,397,399],{"class":210,"line":398},9,[208,400,401],{"class":214},");\n",[208,403,405],{"class":210,"line":404},10,[208,406,267],{"emptyLinePlaceholder":266},[208,408,410,413,416,419],{"class":210,"line":409},11,[208,411,412],{"class":218},"if",[208,414,415],{"class":214}," (",[208,417,418],{"class":218},"!",[208,420,421],{"class":214},"post.value) {\n",[208,423,425,428,431],{"class":210,"line":424},12,[208,426,427],{"class":218},"  throw",[208,429,430],{"class":256}," createError",[208,432,433],{"class":214},"({\n",[208,435,437,440,442,446],{"class":210,"line":436},13,[208,438,439],{"class":214},"    status",[208,441,308],{"class":218},[208,443,445],{"class":444},"sIQBb"," 404",[208,447,348],{"class":214},[208,449,451,454,456,459,462,464],{"class":210,"line":450},14,[208,452,453],{"class":214},"    statusText",[208,455,308],{"class":218},[208,457,458],{"class":232}," \"",[208,460,461],{"class":236},"Post not found",[208,463,233],{"class":232},[208,465,348],{"class":214},[208,467,469,472,474],{"class":210,"line":468},15,[208,470,471],{"class":214},"    fatal",[208,473,308],{"class":218},[208,475,476],{"class":444}," true\n",[208,478,480],{"class":210,"line":479},16,[208,481,482],{"class":214},"  });\n",[208,484,486],{"class":210,"line":485},17,[208,487,488],{"class":214},"}\n",[208,490,492,495,497],{"class":210,"line":491},18,[208,493,494],{"class":214},"\u003C\u002F",[208,496,219],{"class":218},[208,498,242],{"class":214},[208,500,502],{"class":210,"line":501},19,[208,503,267],{"emptyLinePlaceholder":266},[208,505,507,509,512],{"class":210,"line":506},20,[208,508,215],{"class":214},[208,510,511],{"class":218},"template",[208,513,242],{"class":214},[208,515,517,520,523,526,528,530,533,535],{"class":210,"line":516},21,[208,518,519],{"class":214},"  \u003C",[208,521,522],{"class":218},"article",[208,524,525],{"class":222}," v-if",[208,527,229],{"class":218},[208,529,233],{"class":232},[208,531,532],{"class":236},"post",[208,534,233],{"class":232},[208,536,242],{"class":214},[208,538,540,543,546,549,551],{"class":210,"line":539},22,[208,541,542],{"class":214},"    \u003C",[208,544,545],{"class":218},"h1",[208,547,548],{"class":214},">{{ post.title }}\u003C\u002F",[208,550,545],{"class":218},[208,552,242],{"class":214},[208,554,556,559,561],{"class":210,"line":555},23,[208,557,558],{"class":214},"  \u003C\u002F",[208,560,522],{"class":218},[208,562,242],{"class":214},[208,564,566,568,570],{"class":210,"line":565},24,[208,567,494],{"class":214},[208,569,511],{"class":218},[208,571,242],{"class":214},[15,573,574,575,578,579,582,583,586,587,590,591,594,595,598],{},"The slug is a ",[19,576,577],{},"computed",", and the ",[19,580,581],{},"useAsyncData"," key is a function that depends on it. When the user goes from ",[19,584,585],{},"\u002Fblog\u002Fa"," to ",[19,588,589],{},"\u002Fblog\u002Fb",", Vue Router can keep the same page component mounted, so the code in ",[19,592,593],{},"\u003Cscript setup>"," does not necessarily run again. With a reactive key, Nuxt fetches the new post when the slug changes. If you read ",[19,596,597],{},"route.params.slug"," once and store it in a plain variable, the URL changes but the page keeps showing the previous post.",[15,600,601,602,605],{},"A slug that does not exist also needs a 404. Without the ",[19,603,604],{},"createError"," check, it renders an empty page with a 200 status, and search engines see a valid but empty URL.",[607,608,610],"h3",{"id":609},"validating-the-parameter-before-rendering","Validating the Parameter Before Rendering",[15,612,613,616,617,620,621,624],{},[19,614,615],{},"definePageMeta"," accepts a ",[19,618,619],{},"validate"," function that decides whether the route can be rendered by that page. If it returns ",[19,622,623],{},"false"," and no other route matches, Nuxt responds with a 404:",[119,626,628],{"className":202,"code":627,"language":204,"meta":125,"style":125},"\u003Cscript setup lang=\"ts\">\ndefinePageMeta({\n  validate: (route) => \u002F^[a-z0-9-]+$\u002F.test(String(route.params.slug))\n});\n\u003C\u002Fscript>\n",[19,629,630,650,656,713,718],{"__ignoreMap":125},[208,631,632,634,636,638,640,642,644,646,648],{"class":210,"line":211},[208,633,215],{"class":214},[208,635,219],{"class":218},[208,637,223],{"class":222},[208,639,226],{"class":222},[208,641,229],{"class":218},[208,643,233],{"class":232},[208,645,237],{"class":236},[208,647,233],{"class":232},[208,649,242],{"class":214},[208,651,652,654],{"class":210,"line":245},[208,653,615],{"class":256},[208,655,433],{"class":214},[208,657,658,661,663,665,669,672,674,678,681,685,688,691,694,697,699,702,705,707,710],{"class":210,"line":263},[208,659,660],{"class":256},"  validate",[208,662,308],{"class":218},[208,664,415],{"class":214},[208,666,668],{"class":667},"sGEwX","route",[208,670,671],{"class":214},") ",[208,673,286],{"class":218},[208,675,677],{"class":676},"s-doY"," \u002F",[208,679,680],{"class":218},"^",[208,682,684],{"class":683},"sLL85","[",[208,686,687],{"class":444},"a-z0-9",[208,689,690],{"class":236},"-",[208,692,693],{"class":683},"]",[208,695,696],{"class":218},"+$",[208,698,65],{"class":676},[208,700,701],{"class":214},".",[208,703,704],{"class":256},"test",[208,706,361],{"class":214},[208,708,709],{"class":256},"String",[208,711,712],{"class":214},"(route.params.slug))\n",[208,714,715],{"class":210,"line":270},[208,716,717],{"class":214},"});\n",[208,719,720,722,724],{"class":210,"line":295},[208,721,494],{"class":214},[208,723,219],{"class":218},[208,725,242],{"class":214},[15,727,728,730],{},[19,729,619],{}," only checks the shape of the parameter. Checking that the post exists still needs the data lookup shown above.",[24,732,734],{"id":733},"nested-routes","Nested Routes",[15,736,737],{},"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.",[15,739,740],{},"To create one, you need a parent page file and a folder with the same name that holds the children:",[119,742,745],{"className":743,"code":744,"language":124,"meta":125},[122],"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",[19,746,744],{"__ignoreMap":125},[15,748,749,750,753],{},"The parent file renders the shared interface and uses ",[19,751,752],{},"\u003CNuxtPage \u002F>"," where the child page should appear:",[119,755,757],{"className":202,"code":756,"language":204,"meta":125,"style":125},"\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",[19,758,759,767,776,785,812,836,860,869,879,887],{"__ignoreMap":125},[208,760,761,763,765],{"class":210,"line":211},[208,762,215],{"class":214},[208,764,511],{"class":218},[208,766,242],{"class":214},[208,768,769,771,774],{"class":210,"line":245},[208,770,519],{"class":214},[208,772,773],{"class":218},"div",[208,775,242],{"class":214},[208,777,778,780,783],{"class":210,"line":263},[208,779,542],{"class":214},[208,781,782],{"class":218},"nav",[208,784,242],{"class":214},[208,786,787,790,793,796,798,800,803,805,808,810],{"class":210,"line":270},[208,788,789],{"class":214},"      \u003C",[208,791,792],{"class":218},"NuxtLink",[208,794,795],{"class":222}," to",[208,797,229],{"class":218},[208,799,233],{"class":232},[208,801,802],{"class":236},"\u002Fadmin\u002Forders",[208,804,233],{"class":232},[208,806,807],{"class":214},">Orders\u003C\u002F",[208,809,792],{"class":218},[208,811,242],{"class":214},[208,813,814,816,818,820,822,824,827,829,832,834],{"class":210,"line":295},[208,815,789],{"class":214},[208,817,792],{"class":218},[208,819,795],{"class":222},[208,821,229],{"class":218},[208,823,233],{"class":232},[208,825,826],{"class":236},"\u002Fadmin\u002Fproducts",[208,828,233],{"class":232},[208,830,831],{"class":214},">Products\u003C\u002F",[208,833,792],{"class":218},[208,835,242],{"class":214},[208,837,838,840,842,844,846,848,851,853,856,858],{"class":210,"line":300},[208,839,789],{"class":214},[208,841,792],{"class":218},[208,843,795],{"class":222},[208,845,229],{"class":218},[208,847,233],{"class":232},[208,849,850],{"class":236},"\u002Fadmin\u002Fsettings",[208,852,233],{"class":232},[208,854,855],{"class":214},">Settings\u003C\u002F",[208,857,792],{"class":218},[208,859,242],{"class":214},[208,861,862,865,867],{"class":210,"line":325},[208,863,864],{"class":214},"    \u003C\u002F",[208,866,782],{"class":218},[208,868,242],{"class":214},[208,870,871,873,876],{"class":210,"line":351},[208,872,542],{"class":214},[208,874,875],{"class":218},"NuxtPage",[208,877,878],{"class":214}," \u002F>\n",[208,880,881,883,885],{"class":210,"line":398},[208,882,558],{"class":214},[208,884,773],{"class":218},[208,886,242],{"class":214},[208,888,889,891,893],{"class":210,"line":404},[208,890,494],{"class":214},[208,892,511],{"class":218},[208,894,242],{"class":214},[15,896,897,898,900],{},"Navigating between the children only swaps what ",[19,899,752],{}," renders, so the menu stays in place.",[607,902,904],{"id":903},"nested-routes-are-not-layouts","Nested Routes Are Not Layouts",[15,906,907,908,911,912,915],{},"Nuxt has no ",[19,909,910],{},"layout.vue"," file inside ",[19,913,914],{},"pages\u002Fadmin",". A parent page and a layout are separate features:",[917,918,919,929],"ul",{},[920,921,922,923,926,927,701],"li",{},"A nested parent page (",[19,924,925],{},"admin.vue",") is part of the route tree. It renders child routes through ",[19,928,752],{},[920,930,931,932,934,935,938],{},"A layout lives in ",[19,933,95],{}," and is a reusable shell, like a header and footer. A page picks one with ",[19,936,937],{},"definePageMeta({ layout: \"dashboard\" })",", and the layout renders the page through its slot.",[24,940,942],{"id":941},"optional-and-catch-all-routes","Optional and Catch-All Routes",[15,944,945,946,949],{},"Two more bracket patterns cover cases that a plain ",[19,947,948],{},"[slug]"," does not:",[39,951,952,961],{},[42,953,954],{},[45,955,956,958],{},[48,957,50],{},[48,959,960],{},"Matches",[55,962,963,975],{},[45,964,965,968],{},[60,966,967],{},"app\u002Fpages\u002F[[slug]].vue",[60,969,970,138,972],{},[19,971,65],{},[19,973,974],{},"\u002Fanything",[45,976,977,980],{},[60,978,979],{},"app\u002Fpages\u002F[...slug].vue",[60,981,982],{},"any path, including several segments",[15,984,985],{},"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.",[24,987,989],{"id":988},"custom-paths-and-aliases","Custom Paths and Aliases",[15,991,992,993,995,996,998,999,1002],{},"Sometimes the public URL should not follow the file structure. ",[19,994,615],{}," lets you set a custom ",[19,997,374],{}," or add an ",[19,1000,1001],{},"alias"," for a page:",[119,1004,1006],{"className":202,"code":1005,"language":204,"meta":125,"style":125},"\u003Cscript setup lang=\"ts\">\ndefinePageMeta({\n  path: \"\u002Fshirts\u002F:slug\",\n  alias: [\"\u002Ft-shirts\u002F:slug\"]\n});\n\u003C\u002Fscript>\n",[19,1007,1008,1028,1034,1050,1070,1074],{"__ignoreMap":125},[208,1009,1010,1012,1014,1016,1018,1020,1022,1024,1026],{"class":210,"line":211},[208,1011,215],{"class":214},[208,1013,219],{"class":218},[208,1015,223],{"class":222},[208,1017,226],{"class":222},[208,1019,229],{"class":218},[208,1021,233],{"class":232},[208,1023,237],{"class":236},[208,1025,233],{"class":232},[208,1027,242],{"class":214},[208,1029,1030,1032],{"class":210,"line":245},[208,1031,615],{"class":256},[208,1033,433],{"class":214},[208,1035,1036,1039,1041,1043,1046,1048],{"class":210,"line":263},[208,1037,1038],{"class":214},"  path",[208,1040,308],{"class":218},[208,1042,458],{"class":232},[208,1044,1045],{"class":236},"\u002Fshirts\u002F:slug",[208,1047,233],{"class":232},[208,1049,348],{"class":214},[208,1051,1052,1055,1057,1060,1062,1065,1067],{"class":210,"line":270},[208,1053,1054],{"class":214},"  alias",[208,1056,308],{"class":218},[208,1058,1059],{"class":214}," [",[208,1061,233],{"class":232},[208,1063,1064],{"class":236},"\u002Ft-shirts\u002F:slug",[208,1066,233],{"class":232},[208,1068,1069],{"class":214},"]\n",[208,1071,1072],{"class":210,"line":295},[208,1073,717],{"class":214},[208,1075,1076,1078,1080],{"class":210,"line":300},[208,1077,494],{"class":214},[208,1079,219],{"class":218},[208,1081,242],{"class":214},[15,1083,1084],{},"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.",[24,1086,1088],{"id":1087},"query-parameters","Query Parameters",[15,1090,1091,1092,1095,1096,308],{},"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 ",[19,1093,1094],{},"?name=value",", and extra parameters are joined with ",[19,1097,1098],{},"&",[119,1100,1103],{"className":1101,"code":1102,"language":124,"meta":125},[122],"https:\u002F\u002Fonlinestore.com\u002Ftshirts?color=blue&size=m&page=2\n",[19,1104,1102],{"__ignoreMap":125},[15,1106,1107,1108,1111,1112,1115,1116,1119,1120,1123,1124,1127],{},"You read them with ",[19,1109,1110],{},"useRoute().query",". The value of a parameter is not always a single string. It can be a string, ",[19,1113,1114],{},"null"," (for ",[19,1117,1118],{},"?color"," with no value), an array (for ",[19,1121,1122],{},"?tag=seo&tag=nuxt",") or ",[19,1125,1126],{},"undefined"," when the parameter is missing. A small helper avoids bugs when you use the value in a filter or API call:",[119,1129,1132],{"className":1130,"code":1131,"language":237,"meta":125,"style":125},"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",[19,1133,1134,1140,1153,1190,1195,1231,1247],{"__ignoreMap":125},[208,1135,1136],{"class":210,"line":211},[208,1137,1139],{"class":1138},"shSDL","\u002F\u002F app\u002Futils\u002Fquery.ts\n",[208,1141,1142,1145,1148,1151],{"class":210,"line":245},[208,1143,1144],{"class":218},"export",[208,1146,1147],{"class":218}," function",[208,1149,1150],{"class":256}," firstQueryValue",[208,1152,322],{"class":214},[208,1154,1155,1158,1160,1164,1167,1170,1172,1174,1177,1179,1181,1184,1187],{"class":210,"line":263},[208,1156,1157],{"class":667},"  value",[208,1159,308],{"class":218},[208,1161,1163],{"class":1162},"sCp4m"," string",[208,1165,1166],{"class":218}," |",[208,1168,1169],{"class":1162}," null",[208,1171,1166],{"class":218},[208,1173,415],{"class":214},[208,1175,1176],{"class":1162},"string",[208,1178,1166],{"class":218},[208,1180,1169],{"class":1162},[208,1182,1183],{"class":214},")[] ",[208,1185,1186],{"class":218},"|",[208,1188,1189],{"class":1162}," undefined\n",[208,1191,1192],{"class":210,"line":270},[208,1193,1194],{"class":214},") {\n",[208,1196,1197,1200,1203,1205,1208,1211,1214,1217,1220,1223,1226,1228],{"class":210,"line":295},[208,1198,1199],{"class":218},"  const",[208,1201,1202],{"class":214}," first ",[208,1204,229],{"class":218},[208,1206,1207],{"class":214}," Array.",[208,1209,1210],{"class":256},"isArray",[208,1212,1213],{"class":214},"(value) ",[208,1215,1216],{"class":218},"?",[208,1218,1219],{"class":214}," value[",[208,1221,1222],{"class":444},"0",[208,1224,1225],{"class":214},"] ",[208,1227,308],{"class":218},[208,1229,1230],{"class":214}," value;\n",[208,1232,1233,1236,1238,1241,1244],{"class":210,"line":300},[208,1234,1235],{"class":218},"  return",[208,1237,1202],{"class":214},[208,1239,1240],{"class":218},"??",[208,1242,1243],{"class":444}," undefined",[208,1245,1246],{"class":214},";\n",[208,1248,1249],{"class":210,"line":325},[208,1250,488],{"class":214},[119,1252,1254],{"className":202,"code":1253,"language":204,"meta":125,"style":125},"\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",[19,1255,1256,1276,1288,1292,1326,1346],{"__ignoreMap":125},[208,1257,1258,1260,1262,1264,1266,1268,1270,1272,1274],{"class":210,"line":211},[208,1259,215],{"class":214},[208,1261,219],{"class":218},[208,1263,223],{"class":222},[208,1265,226],{"class":222},[208,1267,229],{"class":218},[208,1269,233],{"class":232},[208,1271,237],{"class":236},[208,1273,233],{"class":232},[208,1275,242],{"class":214},[208,1277,1278,1280,1282,1284,1286],{"class":210,"line":245},[208,1279,248],{"class":218},[208,1281,251],{"class":214},[208,1283,229],{"class":218},[208,1285,257],{"class":256},[208,1287,260],{"class":214},[208,1289,1290],{"class":210,"line":263},[208,1291,267],{"emptyLinePlaceholder":266},[208,1293,1294,1296,1299,1301,1303,1305,1307,1310,1312,1315,1318,1321,1324],{"class":210,"line":270},[208,1295,248],{"class":218},[208,1297,1298],{"class":214}," page ",[208,1300,229],{"class":218},[208,1302,280],{"class":256},[208,1304,283],{"class":214},[208,1306,286],{"class":218},[208,1308,1309],{"class":256}," Number",[208,1311,361],{"class":214},[208,1313,1314],{"class":256},"firstQueryValue",[208,1316,1317],{"class":214},"(route.query.page)) ",[208,1319,1320],{"class":218},"||",[208,1322,1323],{"class":444}," 1",[208,1325,401],{"class":214},[208,1327,1328,1330,1333,1335,1337,1339,1341,1343],{"class":210,"line":295},[208,1329,248],{"class":218},[208,1331,1332],{"class":214}," color ",[208,1334,229],{"class":218},[208,1336,280],{"class":256},[208,1338,283],{"class":214},[208,1340,286],{"class":218},[208,1342,1150],{"class":256},[208,1344,1345],{"class":214},"(route.query.color));\n",[208,1347,1348,1350,1352],{"class":210,"line":300},[208,1349,494],{"class":214},[208,1351,219],{"class":218},[208,1353,242],{"class":214},[15,1355,1356,1357,1360],{},"Nuxt auto-imports the helper from ",[19,1358,1359],{},"app\u002Futils",", and both values update when the query changes.",[607,1362,1364],{"id":1363},"query-strings-and-seo","Query Strings and SEO",[15,1366,1367],{},"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:",[119,1369,1371],{"className":202,"code":1370,"language":204,"meta":125,"style":125},"\u003Cscript setup lang=\"ts\">\nuseHead({\n  link: [{ rel: \"canonical\", href: \"https:\u002F\u002Fonlinestore.com\u002Ftshirts\" }]\n});\n\u003C\u002Fscript>\n",[19,1372,1373,1393,1400,1434,1438],{"__ignoreMap":125},[208,1374,1375,1377,1379,1381,1383,1385,1387,1389,1391],{"class":210,"line":211},[208,1376,215],{"class":214},[208,1378,219],{"class":218},[208,1380,223],{"class":222},[208,1382,226],{"class":222},[208,1384,229],{"class":218},[208,1386,233],{"class":232},[208,1388,237],{"class":236},[208,1390,233],{"class":232},[208,1392,242],{"class":214},[208,1394,1395,1398],{"class":210,"line":245},[208,1396,1397],{"class":256},"useHead",[208,1399,433],{"class":214},[208,1401,1402,1405,1407,1410,1412,1414,1417,1419,1422,1424,1426,1429,1431],{"class":210,"line":263},[208,1403,1404],{"class":214},"  link",[208,1406,308],{"class":218},[208,1408,1409],{"class":214}," [{ rel",[208,1411,308],{"class":218},[208,1413,458],{"class":232},[208,1415,1416],{"class":236},"canonical",[208,1418,233],{"class":232},[208,1420,1421],{"class":214},", href",[208,1423,308],{"class":218},[208,1425,458],{"class":232},[208,1427,1428],{"class":236},"https:\u002F\u002Fonlinestore.com\u002Ftshirts",[208,1430,233],{"class":232},[208,1432,1433],{"class":214}," }]\n",[208,1435,1436],{"class":210,"line":270},[208,1437,717],{"class":214},[208,1439,1440,1442,1444],{"class":210,"line":295},[208,1441,494],{"class":214},[208,1443,219],{"class":218},[208,1445,242],{"class":214},[15,1447,1448,1449,1452,1453,1456],{},"Paginated pages are a different case: each page of a list has its own content, so ",[19,1450,1451],{},"?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 (",[19,1454,1455],{},"\u002Ftshirts\u002Fblue",") than as a query string.",[24,1458,1460],{"id":1459},"navigating-between-routes","Navigating Between Routes",[15,1462,1463,1464,1466,1467,308],{},"Use ",[19,1465,792],{}," 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 ",[19,1468,1469],{},"navigateTo",[119,1471,1473],{"className":202,"code":1472,"language":204,"meta":125,"style":125},"\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",[19,1474,1475,1495,1508,1560,1564,1572,1576,1584,1602,1610,1615,1641],{"__ignoreMap":125},[208,1476,1477,1479,1481,1483,1485,1487,1489,1491,1493],{"class":210,"line":211},[208,1478,215],{"class":214},[208,1480,219],{"class":218},[208,1482,223],{"class":222},[208,1484,226],{"class":222},[208,1486,229],{"class":218},[208,1488,233],{"class":232},[208,1490,237],{"class":236},[208,1492,233],{"class":232},[208,1494,242],{"class":214},[208,1496,1497,1500,1502,1505],{"class":210,"line":245},[208,1498,1499],{"class":218},"async",[208,1501,1147],{"class":218},[208,1503,1504],{"class":256}," applyFilter",[208,1506,1507],{"class":214},"() {\n",[208,1509,1510,1513,1516,1519,1521,1523,1526,1528,1531,1533,1536,1538,1540,1543,1545,1548,1550,1552,1555,1557],{"class":210,"line":263},[208,1511,1512],{"class":218},"  await",[208,1514,1515],{"class":256}," navigateTo",[208,1517,1518],{"class":214},"({ path",[208,1520,308],{"class":218},[208,1522,458],{"class":232},[208,1524,1525],{"class":236},"\u002Ftshirts",[208,1527,233],{"class":232},[208,1529,1530],{"class":214},", query",[208,1532,308],{"class":218},[208,1534,1535],{"class":214}," { color",[208,1537,308],{"class":218},[208,1539,458],{"class":232},[208,1541,1542],{"class":236},"blue",[208,1544,233],{"class":232},[208,1546,1547],{"class":214},", size",[208,1549,308],{"class":218},[208,1551,458],{"class":232},[208,1553,1554],{"class":236},"m",[208,1556,233],{"class":232},[208,1558,1559],{"class":214}," } });\n",[208,1561,1562],{"class":210,"line":270},[208,1563,488],{"class":214},[208,1565,1566,1568,1570],{"class":210,"line":295},[208,1567,494],{"class":214},[208,1569,219],{"class":218},[208,1571,242],{"class":214},[208,1573,1574],{"class":210,"line":300},[208,1575,267],{"emptyLinePlaceholder":266},[208,1577,1578,1580,1582],{"class":210,"line":325},[208,1579,215],{"class":214},[208,1581,511],{"class":218},[208,1583,242],{"class":214},[208,1585,1586,1588,1590,1592,1594,1596,1599],{"class":210,"line":351},[208,1587,519],{"class":214},[208,1589,792],{"class":218},[208,1591,795],{"class":222},[208,1593,229],{"class":218},[208,1595,233],{"class":232},[208,1597,1598],{"class":236},"\u002Fblog\u002Fhow-the-routing-system-works-in-nuxt",[208,1600,1601],{"class":232},"\"\n",[208,1603,1604,1607],{"class":210,"line":398},[208,1605,1606],{"class":214},"    >Read the post\u003C\u002F",[208,1608,1609],{"class":218},"NuxtLink\n",[208,1611,1612],{"class":210,"line":404},[208,1613,1614],{"class":214},"  >\n",[208,1616,1617,1619,1622,1625,1627,1629,1632,1634,1637,1639],{"class":210,"line":409},[208,1618,519],{"class":214},[208,1620,1621],{"class":218},"button",[208,1623,1624],{"class":222}," @click",[208,1626,229],{"class":218},[208,1628,233],{"class":232},[208,1630,1631],{"class":236},"applyFilter",[208,1633,233],{"class":232},[208,1635,1636],{"class":214},">Blue, size M\u003C\u002F",[208,1638,1621],{"class":218},[208,1640,242],{"class":214},[208,1642,1643,1645,1647],{"class":210,"line":424},[208,1644,494],{"class":214},[208,1646,511],{"class":218},[208,1648,242],{"class":214},[15,1650,1651,1652,1654,1655,138,1658,1661,1662,1665,1666,1668],{},"When a rule applies to many routes, such as requiring a login, use route middleware in ",[19,1653,99],{},". Inside middleware, read the ",[19,1656,1657],{},"to",[19,1659,1660],{},"from"," arguments instead of calling ",[19,1663,1664],{},"useRoute()",". The Nuxt documentation advises against ",[19,1667,1664],{}," there because middleware has no current route yet.",[24,1670,1672],{"id":1671},"common-routing-mistakes","Common Routing Mistakes",[917,1674,1675,1680,1686,1693,1696],{},[920,1676,1677,1678,701],{},"Using a layout as the parent of nested routes instead of a parent page with ",[19,1679,752],{},[920,1681,1682,1683,1685],{},"Reading ",[19,1684,191],{}," once instead of deriving data from it reactively.",[920,1687,1688,1689,1692],{},"Treating ",[19,1690,1691],{},"route.query"," values as plain strings.",[920,1694,1695],{},"Returning an empty page with a 200 status for a slug that does not exist.",[920,1697,1698],{},"Creating filter URLs without deciding which version search engines should index.",[24,1700,1702],{"id":1701},"frequently-asked-questions","Frequently Asked Questions",[607,1704,1706],{"id":1705},"what-is-file-based-routing-in-nuxt","What is file-based routing in Nuxt?",[15,1708,1709,1710,1712,1713,1716,1717,701],{},"Nuxt creates routes from the files and folders inside the ",[19,1711,21],{}," directory. For example, ",[19,1714,1715],{},"app\u002Fpages\u002Fabout.vue"," becomes ",[19,1718,1719],{},"\u002Fabout",[607,1721,1723],{"id":1722},"where-do-pages-go-in-nuxt-4","Where do pages go in Nuxt 4?",[15,1725,1726,1727,1729,1730,1732],{},"New Nuxt 4 projects use ",[19,1728,91],{},". Projects that still have a top-level ",[19,1731,103],{}," folder keep working, because Nuxt detects the old structure.",[607,1734,1736],{"id":1735},"how-do-i-create-a-dynamic-route-in-nuxt","How do I create a dynamic route in Nuxt?",[15,1738,1739,1740,1742,1743,1746,1747,1749],{},"Name the file with a bracketed parameter, such as ",[19,1741,172],{},", and read the value with ",[19,1744,1745],{},"useRoute().params.slug",". Derive anything that depends on it from a ",[19,1748,577],{}," so it updates when the slug changes.",[607,1751,1753],{"id":1752},"what-is-the-difference-between-nested-routes-and-layouts","What is the difference between nested routes and layouts?",[15,1755,1756,1757,1759,1760,1762,1763,701],{},"A nested route uses a parent page with ",[19,1758,752],{}," to render child routes. A layout lives in ",[19,1761,95],{}," and wraps pages with a reusable shell, selected with ",[19,1764,1765],{},"definePageMeta({ layout })",[607,1767,1769],{"id":1768},"how-do-i-show-a-404-for-an-invalid-slug","How do I show a 404 for an invalid slug?",[15,1771,1772,1773,1775,1776,1779,1780,1782,1783,1785,1786,1788],{},"Throw ",[19,1774,604],{}," with ",[19,1777,1778],{},"status: 404"," when the data does not exist, or return ",[19,1781,623],{}," from ",[19,1784,619],{}," in ",[19,1787,615],{}," when the parameter has an invalid format.",[24,1790,1792],{"id":1791},"conclusion","Conclusion",[15,1794,1795,1796,701],{},"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 ",[1797,1798,1802],"a",{"href":1799,"rel":1800},"https:\u002F\u002Fnuxt.com\u002Fdocs\u002F4.x\u002Fgetting-started\u002Frouting",[1801],"nofollow","Nuxt routing documentation",[1804,1805,1806],"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":125,"searchDepth":245,"depth":245,"links":1808},[1809,1810,1811,1814,1817,1818,1819,1822,1823,1824,1831],{"id":26,"depth":245,"text":27},{"id":113,"depth":245,"text":114},{"id":145,"depth":245,"text":146,"children":1812},[1813],{"id":609,"depth":263,"text":610},{"id":733,"depth":245,"text":734,"children":1815},[1816],{"id":903,"depth":263,"text":904},{"id":941,"depth":245,"text":942},{"id":988,"depth":245,"text":989},{"id":1087,"depth":245,"text":1088,"children":1820},[1821],{"id":1363,"depth":263,"text":1364},{"id":1459,"depth":245,"text":1460},{"id":1671,"depth":245,"text":1672},{"id":1701,"depth":245,"text":1702,"children":1825},[1826,1827,1828,1829,1830],{"id":1705,"depth":263,"text":1706},{"id":1722,"depth":263,"text":1723},{"id":1735,"depth":263,"text":1736},{"id":1752,"depth":263,"text":1753},{"id":1768,"depth":263,"text":1769},{"id":1791,"depth":245,"text":1792},[1833,1834],"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":5,"description":1835},{"loc":1838,"lastmod":1843},"2026-10-06","en\u002Fblog\u002Fhow-the-routing-system-works-in-nuxt",1378,"oB2IGmtiBUJowzUsIjQWjxG2dLcySN1PQ6tQYBgEXNU",[1848,1857,1865,1867,1874,1882,1889,1897,1904,1912,1918,1926,1933,1939,1946],{"id":1849,"path":1850,"title":1851,"articleCover":1852,"categoriesIds":1853,"publishedAt":1856},"blogEn\u002Fen\u002Fblog\u002Fcore-web-vitals-how-user-experience-affects-seo.md","\u002Fen\u002Fblog\u002Fcore-web-vitals-how-user-experience-affects-seo","Core Web Vitals and SEO: How User Experience Affects Your Site","\u002Fimages\u002Fen\u002Fblog\u002Fseo\u002Fweb-vitals\u002Fcore-web-vitals-how-user-experience-affects-seo.webp",[1854,1855],"seo","web-vitals","2024-10-25",{"id":1858,"path":1859,"title":1860,"articleCover":1861,"categoriesIds":1862,"publishedAt":1864},"blogEn\u002Fen\u002Fblog\u002Ffree-developer-tools-hub.md","\u002Fen\u002Fblog\u002Ffree-developer-tools-hub","I Built a Free Web Tools Hub for Developers","\u002Fimages\u002Fen\u002Fblog\u002Ftools\u002Ffree-developer-tools-hub.webp",[1863],"tools","2026-01-06",{"id":4,"path":1838,"title":5,"articleCover":6,"categoriesIds":1866,"publishedAt":1840},[1833,1834],{"id":1868,"path":1869,"title":1870,"articleCover":1871,"categoriesIds":1872,"publishedAt":1873},"blogEn\u002Fen\u002Fblog\u002Fhow-to-add-special-characters-on-i18n.md","\u002Fen\u002Fblog\u002Fhow-to-add-special-characters-on-i18n","How to Add Special Characters in i18N Translations","\u002Fimages\u002Fblog\u002Fi18n\u002Fi18n-special-characters-on-nuxtjs-thumb.webp",[1833,1834],"2025-02-02",{"id":1875,"path":1876,"title":1877,"articleCover":1878,"categoriesIds":1879,"publishedAt":1881},"blogEn\u002Fen\u002Fblog\u002Fhow-to-create-and-set-up-ssh-key-github.md","\u002Fen\u002Fblog\u002Fhow-to-create-and-set-up-ssh-key-github","SSH Key in Git: How to Set Up an SSH Key for GitHub","\u002Fimages\u002Fen\u002Fblog\u002Fgit\u002Fhow-to-create-and-set-up-ssh-key-github-thumb.webp",[1880],"git","2025-02-04",{"id":1883,"path":1884,"title":1885,"articleCover":1886,"categoriesIds":1887,"publishedAt":1888},"blogEn\u002Fen\u002Fblog\u002Fhow-to-create-structured-data-with-nuxtjs.md","\u002Fen\u002Fblog\u002Fhow-to-create-structured-data-with-nuxtjs","What is Structured Data and How to Add it in Nuxt.JS","\u002Fimages\u002Fen\u002Fblog\u002Fseo\u002Fstructured-data\u002Fstructured-data-thumb-1.webp",[1833,1834,1854],"2025-01-22",{"id":1890,"path":1891,"title":1892,"articleCover":1893,"categoriesIds":1894,"publishedAt":1896},"blogEn\u002Fen\u002Fblog\u002Fhow-to-deal-with-envy-from-other-programmers.md","\u002Fen\u002Fblog\u002Fhow-to-deal-with-envy-from-other-programmers","How to Deal with Envy from Other Programmers in Your Career","\u002Fimages\u002Fen\u002Fblog\u002Fcareer\u002Fa-nail-that-stands-out-gets-hammered.webp",[1895],"career","2025-08-29",{"id":1898,"path":1899,"title":1900,"articleCover":1901,"categoriesIds":1902,"publishedAt":1903},"blogEn\u002Fen\u002Fblog\u002Fhow-to-use-middlewares-nuxtjs.md","\u002Fen\u002Fblog\u002Fhow-to-use-middlewares-nuxtjs","Middlewares: What They Are and How to Use Them in Nuxt.JS","\u002Fimages\u002Fblog\u002Fnuxt\u002Ffront-end-middlewares-thumb.webp",[1833,1834],"2025-02-03",{"id":1905,"path":1906,"title":1907,"articleCover":1908,"categoriesIds":1909,"publishedAt":1911},"blogEn\u002Fen\u002Fblog\u002Fhow-to-validate-cpf-with-javascript.md","\u002Fen\u002Fblog\u002Fhow-to-validate-cpf-with-javascript","How to validate a CPF with JavaScript","\u002Fimages\u002Fen\u002Fblog\u002Fjavascript\u002Fhow-to-validate-cpf-with-javascript.webp",[1910,1863],"javascript","2026-10-04",{"id":1913,"path":1914,"title":1915,"articleCover":1916,"categoriesIds":1917,"publishedAt":1911},"blogEn\u002Fen\u002Fblog\u002Fhow-to-validate-credit-card-with-javascript.md","\u002Fen\u002Fblog\u002Fhow-to-validate-credit-card-with-javascript","How to validate a credit card with JavaScript (Luhn algorithm)","\u002Fimages\u002Fen\u002Fblog\u002Fjavascript\u002Fhow-to-validate-credit-card-with-javascript.webp",[1910,1863],{"id":1919,"path":1920,"title":1921,"articleCover":1922,"categoriesIds":1923,"publishedAt":1925},"blogEn\u002Fen\u002Fblog\u002Frecovering-lost-files-in-git-with-vscode.md","\u002Fen\u002Fblog\u002Frecovering-lost-files-in-git-with-vscode","Recover Lost Files in Git Without a Commit Using VS Code Local History","\u002Fimages\u002Fen\u002Fblog\u002Fvscode\u002Fhow-to-recover-lost-files-in-git-using-vscode.webp",[1880,1924],"vscode","2025-01-21",{"id":1927,"path":1928,"title":1929,"articleCover":1930,"categoriesIds":1931,"publishedAt":1932},"blogEn\u002Fen\u002Fblog\u002Fvercel-buys-nuxt-labs.md","\u002Fen\u002Fblog\u002Fvercel-buys-nuxt-labs","Vercel Acquired Nuxt Labs! What Does This Mean for Nuxt?","\u002Fimages\u002Fen\u002Fblog\u002Fnuxt\u002Fvercel-buys-nuxt-labs.webp",[1833],"2025-07-17",{"id":1934,"path":1935,"title":1936,"articleCover":1937,"categoriesIds":1938,"publishedAt":1843},"blogEn\u002Fen\u002Fblog\u002Fvue-vapor-mode-what-it-is-and-how-to-use-it.md","\u002Fen\u002Fblog\u002Fvue-vapor-mode-what-it-is-and-how-to-use-it","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",[1834,1833],{"id":1940,"path":1941,"title":1942,"articleCover":1943,"categoriesIds":1944,"publishedAt":1945},"blogEn\u002Fen\u002Fblog\u002Fwhat-are-directives-in-vue-and-how-to-implement-them.md","\u002Fen\u002Fblog\u002Fwhat-are-directives-in-vue-and-how-to-implement-them","Vue 3 Directives: Built-in and Custom","\u002Fimages\u002Fen\u002Fblog\u002Fvue\u002Fwhat-are-directives-in-vue-and-how-to-implement-them.webp",[1834],"2025-03-21",{"id":1947,"path":1948,"title":1949,"articleCover":1950,"categoriesIds":1951,"publishedAt":1952},"blogEn\u002Fen\u002Fblog\u002Fwhat-is-the-difference-between-composition-api-and-options-api-in-vuejs.md","\u002Fen\u002Fblog\u002Fwhat-is-the-difference-between-composition-api-and-options-api-in-vuejs","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",[1834],"2025-02-20",1791682334755]