[{"data":1,"prerenderedAt":8380},["ShallowReactive",2],{"post-sending-email-from-your-nuxt-3-app-with-mailgun":3,"writing-order":1997},{"id":4,"title":5,"body":6,"brief":12,"createdAt":1987,"description":1988,"extension":1989,"meta":1990,"navigation":50,"path":1991,"readTime":1992,"seo":1993,"slug":1994,"stem":1995,"__hash__":1996},"writings\u002Fwritings\u002Fsending-email-from-your-nuxt-3-app-with-mailgun.md","Sending email from your Nuxt 3 app with Mailgun",{"type":7,"value":8,"toc":1985},"minimark",[9,13,16,159,162,172,1080,1098,1101,1104,1179,1182,1256,1276,1502,1517,1520,1546,1571,1577,1610,1637,1669,1979,1982],[10,11,12],"p",{},"Have you found yourself in a situation where you needed to send emails from your Nuxt application? With Nuxt's server and Mailgun, sending emails directly from your Nuxt 3 app becomes a breeze.",[10,14,15],{},"To get started, create a Nuxt 3 app using the following command. Ensure that you have Node.js version 16 or newer installed, along with a text editor and a terminal for running these commands:",[17,18,23],"pre",{"className":19,"code":20,"language":21,"meta":22,"style":22},"language-bash shiki shiki-themes vitesse-light vitesse-black","npx nuxi@latest init my-app\n\n# select preferred package manager\nafter the selection, the necessary dependencies will be installed\n\n# initialize git repository\nyou'll be prompted to initialize a git respository or not\n\n# navigate into your newly created project\ncd my-app\n\n# open with vscode\ncode .\n\n# start the server\nyarn dev or npm run dev\n","bash","",[24,25,26,45,52,59,88,93,99,108,113,119,125,130,136,142,147,153],"code",{"__ignoreMap":22},[27,28,31,35,39,42],"span",{"class":29,"line":30},"line",1,[27,32,34],{"class":33},"s_xSY","npx",[27,36,38],{"class":37},"spP0B"," nuxi@latest",[27,40,41],{"class":37}," init",[27,43,44],{"class":37}," my-app\n",[27,46,48],{"class":29,"line":47},2,[27,49,51],{"emptyLinePlaceholder":50},true,"\n",[27,53,55],{"class":29,"line":54},3,[27,56,58],{"class":57},"snYqZ","# select preferred package manager\n",[27,60,62,65,68,71,73,76,79,82,85],{"class":29,"line":61},4,[27,63,64],{"class":33},"after",[27,66,67],{"class":37}," the",[27,69,70],{"class":37}," selection,",[27,72,67],{"class":37},[27,74,75],{"class":37}," necessary",[27,77,78],{"class":37}," dependencies",[27,80,81],{"class":37}," will",[27,83,84],{"class":37}," be",[27,86,87],{"class":37}," installed\n",[27,89,91],{"class":29,"line":90},5,[27,92,51],{"emptyLinePlaceholder":50},[27,94,96],{"class":29,"line":95},6,[27,97,98],{"class":57},"# initialize git repository\n",[27,100,102,105],{"class":29,"line":101},7,[27,103,104],{"class":33},"you",[27,106,107],{"class":33},"'ll be prompted to initialize a git respository or not\n",[27,109,111],{"class":29,"line":110},8,[27,112,51],{"emptyLinePlaceholder":50},[27,114,116],{"class":29,"line":115},9,[27,117,118],{"class":33},"# navigate into your newly created project\n",[27,120,122],{"class":29,"line":121},10,[27,123,124],{"class":33},"cd my-app\n",[27,126,128],{"class":29,"line":127},11,[27,129,51],{"emptyLinePlaceholder":50},[27,131,133],{"class":29,"line":132},12,[27,134,135],{"class":33},"# open with vscode\n",[27,137,139],{"class":29,"line":138},13,[27,140,141],{"class":33},"code .\n",[27,143,145],{"class":29,"line":144},14,[27,146,51],{"emptyLinePlaceholder":50},[27,148,150],{"class":29,"line":149},15,[27,151,152],{"class":33},"# start the server\n",[27,154,156],{"class":29,"line":155},16,[27,157,158],{"class":33},"yarn dev or npm run dev\n",[10,160,161],{},"Now that our application is up and running, in this tutorial, we'll implement a straightforward sign-up process and send a welcome email to new users upon successful registration. To achieve this, we'll create a signup form where users can enter their information and submit it.",[10,163,164,165,168,169,171],{},"For this tutorial, we will work with the ",[24,166,167],{},"app.vue"," file in our application. First, let's clean up the content of ",[24,170,167],{},". Within this file, we will define a reactive form state to store the data entered by the user. We will also create and style a basic form to gather user inputs.",[17,173,177],{"className":174,"code":175,"language":176,"meta":22,"style":22},"language-vue shiki shiki-themes vitesse-light vitesse-black","\u003Cscript lang=\"ts\" setup>\nconst form = reactive({\n    name: \"\",\n    email: \"\",\n    password: \"\",\n});\n\nconst handleSubmission = () => {\n    console.log(form);\n    alert(\"Sign up successful!\");\n};\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n    \u003Cform @submit.prevent=\"handleSubmission\">\n        \u003Clabel for=\"name\">\n            Name\n            \u003Cinput v-model=\"form.name\" id=\"name\" type=\"text\" placeholder=\"Enter your name\" required \u002F>\n        \u003C\u002Flabel>\n        \u003Clabel for=\"email\">\n            Email\n            \u003Cinput v-model=\"form.email\" id=\"email\" type=\"email\" placeholder=\"Enter your email\" required \u002F>\n        \u003C\u002Flabel>\n        \u003Clabel for=\"password\">\n            Password\n            \u003Cinput v-model=\"form.password\" id=\"password\" type=\"password\" placeholder=\"Enter your password\" required \u002F>\n        \u003C\u002Flabel>\n        \u003Cbutton type=\"submit\">Sign up\u003C\u002Fbutton>\n    \u003C\u002Fform>\n\u003C\u002Ftemplate>\n\n\u003Cstyle scoped>\nform {\n    padding: 20px;\n    display: flex;\n    flex-direction: column;\n    gap: 10px;\n    width: 100%;\n    max-width: 400px;\n    margin: 0 auto;\n    margin-top: 30px;\n}\n\nform label input {\n    box-sizing: border-box;\n    width: 100%;\n    margin-top: 10px;\n    margin-bottom: 15px;\n    padding: 15px;\n    border: 1px solid #333333;\n    border-radius: 10px;\n}\n\nform button {\n    width: 100%;\n    padding: 15px;\n    background: #333333;\n    color: #ffffff;\n    border: none;\n}\n\u003C\u002Fstyle>\n","vue",[24,178,179,211,229,244,255,266,271,275,293,312,328,333,342,346,355,375,397,404,466,476,496,502,555,564,584,590,643,652,683,693,702,707,720,727,746,760,773,788,804,819,835,850,856,861,874,887,900,913,928,941,965,979,984,989,999,1012,1025,1039,1054,1066,1071],{"__ignoreMap":22},[27,180,181,185,189,193,196,200,203,205,208],{"class":29,"line":30},[27,182,184],{"class":183},"stPdD","\u003C",[27,186,188],{"class":187},"sTPum","script",[27,190,192],{"class":191},"s9nN2"," lang",[27,194,195],{"class":183},"=",[27,197,199],{"class":198},"scnC2","\"",[27,201,202],{"class":37},"ts",[27,204,199],{"class":198},[27,206,207],{"class":191}," setup",[27,209,210],{"class":183},">\n",[27,212,213,217,220,223,226],{"class":29,"line":47},[27,214,216],{"class":215},"s5TCs","const ",[27,218,219],{"class":191},"form",[27,221,222],{"class":183}," =",[27,224,225],{"class":33}," reactive",[27,227,228],{"class":183},"({\n",[27,230,231,235,238,241],{"class":29,"line":54},[27,232,234],{"class":233},"sHLBJ","    name",[27,236,237],{"class":183},": ",[27,239,240],{"class":198},"\"\"",[27,242,243],{"class":183},",\n",[27,245,246,249,251,253],{"class":29,"line":61},[27,247,248],{"class":233},"    email",[27,250,237],{"class":183},[27,252,240],{"class":198},[27,254,243],{"class":183},[27,256,257,260,262,264],{"class":29,"line":90},[27,258,259],{"class":233},"    password",[27,261,237],{"class":183},[27,263,240],{"class":198},[27,265,243],{"class":183},[27,267,268],{"class":29,"line":95},[27,269,270],{"class":183},"});\n",[27,272,273],{"class":29,"line":101},[27,274,51],{"emptyLinePlaceholder":50},[27,276,277,279,282,284,287,290],{"class":29,"line":110},[27,278,216],{"class":215},[27,280,281],{"class":33},"handleSubmission",[27,283,222],{"class":183},[27,285,286],{"class":183}," ()",[27,288,289],{"class":183}," =>",[27,291,292],{"class":183}," {\n",[27,294,295,298,301,304,307,309],{"class":29,"line":115},[27,296,297],{"class":191},"    console",[27,299,300],{"class":183},".",[27,302,303],{"class":33},"log",[27,305,306],{"class":183},"(",[27,308,219],{"class":191},[27,310,311],{"class":183},");\n",[27,313,314,317,319,321,324,326],{"class":29,"line":121},[27,315,316],{"class":33},"    alert",[27,318,306],{"class":183},[27,320,199],{"class":198},[27,322,323],{"class":37},"Sign up successful!",[27,325,199],{"class":198},[27,327,311],{"class":183},[27,329,330],{"class":29,"line":127},[27,331,332],{"class":183},"};\n",[27,334,335,338,340],{"class":29,"line":132},[27,336,337],{"class":183},"\u003C\u002F",[27,339,188],{"class":187},[27,341,210],{"class":183},[27,343,344],{"class":29,"line":138},[27,345,51],{"emptyLinePlaceholder":50},[27,347,348,350,353],{"class":29,"line":144},[27,349,184],{"class":183},[27,351,352],{"class":187},"template",[27,354,210],{"class":183},[27,356,357,360,362,365,367,369,371,373],{"class":29,"line":149},[27,358,359],{"class":183},"    \u003C",[27,361,219],{"class":187},[27,363,364],{"class":191}," @submit.prevent",[27,366,195],{"class":183},[27,368,199],{"class":198},[27,370,281],{"class":37},[27,372,199],{"class":198},[27,374,210],{"class":183},[27,376,377,380,383,386,388,390,393,395],{"class":29,"line":155},[27,378,379],{"class":183},"        \u003C",[27,381,382],{"class":187},"label",[27,384,385],{"class":191}," for",[27,387,195],{"class":183},[27,389,199],{"class":198},[27,391,392],{"class":37},"name",[27,394,199],{"class":198},[27,396,210],{"class":183},[27,398,400],{"class":29,"line":399},17,[27,401,403],{"class":402},"slSvp","            Name\n",[27,405,407,410,413,416,418,420,423,425,428,430,432,434,436,439,441,443,446,448,451,453,455,458,460,463],{"class":29,"line":406},18,[27,408,409],{"class":183},"            \u003C",[27,411,412],{"class":187},"input",[27,414,415],{"class":191}," v-model",[27,417,195],{"class":183},[27,419,199],{"class":198},[27,421,422],{"class":37},"form.name",[27,424,199],{"class":198},[27,426,427],{"class":191}," id",[27,429,195],{"class":183},[27,431,199],{"class":198},[27,433,392],{"class":37},[27,435,199],{"class":198},[27,437,438],{"class":191}," type",[27,440,195],{"class":183},[27,442,199],{"class":198},[27,444,445],{"class":37},"text",[27,447,199],{"class":198},[27,449,450],{"class":191}," placeholder",[27,452,195],{"class":183},[27,454,199],{"class":198},[27,456,457],{"class":37},"Enter your name",[27,459,199],{"class":198},[27,461,462],{"class":191}," required",[27,464,465],{"class":183}," \u002F>\n",[27,467,469,472,474],{"class":29,"line":468},19,[27,470,471],{"class":183},"        \u003C\u002F",[27,473,382],{"class":187},[27,475,210],{"class":183},[27,477,479,481,483,485,487,489,492,494],{"class":29,"line":478},20,[27,480,379],{"class":183},[27,482,382],{"class":187},[27,484,385],{"class":191},[27,486,195],{"class":183},[27,488,199],{"class":198},[27,490,491],{"class":37},"email",[27,493,199],{"class":198},[27,495,210],{"class":183},[27,497,499],{"class":29,"line":498},21,[27,500,501],{"class":402},"            Email\n",[27,503,505,507,509,511,513,515,518,520,522,524,526,528,530,532,534,536,538,540,542,544,546,549,551,553],{"class":29,"line":504},22,[27,506,409],{"class":183},[27,508,412],{"class":187},[27,510,415],{"class":191},[27,512,195],{"class":183},[27,514,199],{"class":198},[27,516,517],{"class":37},"form.email",[27,519,199],{"class":198},[27,521,427],{"class":191},[27,523,195],{"class":183},[27,525,199],{"class":198},[27,527,491],{"class":37},[27,529,199],{"class":198},[27,531,438],{"class":191},[27,533,195],{"class":183},[27,535,199],{"class":198},[27,537,491],{"class":37},[27,539,199],{"class":198},[27,541,450],{"class":191},[27,543,195],{"class":183},[27,545,199],{"class":198},[27,547,548],{"class":37},"Enter your email",[27,550,199],{"class":198},[27,552,462],{"class":191},[27,554,465],{"class":183},[27,556,558,560,562],{"class":29,"line":557},23,[27,559,471],{"class":183},[27,561,382],{"class":187},[27,563,210],{"class":183},[27,565,567,569,571,573,575,577,580,582],{"class":29,"line":566},24,[27,568,379],{"class":183},[27,570,382],{"class":187},[27,572,385],{"class":191},[27,574,195],{"class":183},[27,576,199],{"class":198},[27,578,579],{"class":37},"password",[27,581,199],{"class":198},[27,583,210],{"class":183},[27,585,587],{"class":29,"line":586},25,[27,588,589],{"class":402},"            Password\n",[27,591,593,595,597,599,601,603,606,608,610,612,614,616,618,620,622,624,626,628,630,632,634,637,639,641],{"class":29,"line":592},26,[27,594,409],{"class":183},[27,596,412],{"class":187},[27,598,415],{"class":191},[27,600,195],{"class":183},[27,602,199],{"class":198},[27,604,605],{"class":37},"form.password",[27,607,199],{"class":198},[27,609,427],{"class":191},[27,611,195],{"class":183},[27,613,199],{"class":198},[27,615,579],{"class":37},[27,617,199],{"class":198},[27,619,438],{"class":191},[27,621,195],{"class":183},[27,623,199],{"class":198},[27,625,579],{"class":37},[27,627,199],{"class":198},[27,629,450],{"class":191},[27,631,195],{"class":183},[27,633,199],{"class":198},[27,635,636],{"class":37},"Enter your password",[27,638,199],{"class":198},[27,640,462],{"class":191},[27,642,465],{"class":183},[27,644,646,648,650],{"class":29,"line":645},27,[27,647,471],{"class":183},[27,649,382],{"class":187},[27,651,210],{"class":183},[27,653,655,657,660,662,664,666,669,671,674,677,679,681],{"class":29,"line":654},28,[27,656,379],{"class":183},[27,658,659],{"class":187},"button",[27,661,438],{"class":191},[27,663,195],{"class":183},[27,665,199],{"class":198},[27,667,668],{"class":37},"submit",[27,670,199],{"class":198},[27,672,673],{"class":183},">",[27,675,676],{"class":402},"Sign up",[27,678,337],{"class":183},[27,680,659],{"class":187},[27,682,210],{"class":183},[27,684,686,689,691],{"class":29,"line":685},29,[27,687,688],{"class":183},"    \u003C\u002F",[27,690,219],{"class":187},[27,692,210],{"class":183},[27,694,696,698,700],{"class":29,"line":695},30,[27,697,337],{"class":183},[27,699,352],{"class":187},[27,701,210],{"class":183},[27,703,705],{"class":29,"line":704},31,[27,706,51],{"emptyLinePlaceholder":50},[27,708,710,712,715,718],{"class":29,"line":709},32,[27,711,184],{"class":183},[27,713,714],{"class":187},"style",[27,716,717],{"class":191}," scoped",[27,719,210],{"class":183},[27,721,723,725],{"class":29,"line":722},33,[27,724,219],{"class":187},[27,726,292],{"class":183},[27,728,730,733,736,740,743],{"class":29,"line":729},34,[27,731,732],{"class":233},"    padding",[27,734,735],{"class":183},":",[27,737,739],{"class":738},"sqbOQ"," 20",[27,741,742],{"class":215},"px",[27,744,745],{"class":183},";\n",[27,747,749,752,754,758],{"class":29,"line":748},35,[27,750,751],{"class":233},"    display",[27,753,735],{"class":183},[27,755,757],{"class":756},"sfsYZ"," flex",[27,759,745],{"class":183},[27,761,763,766,768,771],{"class":29,"line":762},36,[27,764,765],{"class":233},"    flex-direction",[27,767,735],{"class":183},[27,769,770],{"class":756}," column",[27,772,745],{"class":183},[27,774,776,779,781,784,786],{"class":29,"line":775},37,[27,777,778],{"class":233},"    gap",[27,780,735],{"class":183},[27,782,783],{"class":738}," 10",[27,785,742],{"class":215},[27,787,745],{"class":183},[27,789,791,794,796,799,802],{"class":29,"line":790},38,[27,792,793],{"class":233},"    width",[27,795,735],{"class":183},[27,797,798],{"class":738}," 100",[27,800,801],{"class":215},"%",[27,803,745],{"class":183},[27,805,807,810,812,815,817],{"class":29,"line":806},39,[27,808,809],{"class":233},"    max-width",[27,811,735],{"class":183},[27,813,814],{"class":738}," 400",[27,816,742],{"class":215},[27,818,745],{"class":183},[27,820,822,825,827,830,833],{"class":29,"line":821},40,[27,823,824],{"class":233},"    margin",[27,826,735],{"class":183},[27,828,829],{"class":738}," 0",[27,831,832],{"class":756}," auto",[27,834,745],{"class":183},[27,836,838,841,843,846,848],{"class":29,"line":837},41,[27,839,840],{"class":233},"    margin-top",[27,842,735],{"class":183},[27,844,845],{"class":738}," 30",[27,847,742],{"class":215},[27,849,745],{"class":183},[27,851,853],{"class":29,"line":852},42,[27,854,855],{"class":183},"}\n",[27,857,859],{"class":29,"line":858},43,[27,860,51],{"emptyLinePlaceholder":50},[27,862,864,866,869,872],{"class":29,"line":863},44,[27,865,219],{"class":187},[27,867,868],{"class":187}," label",[27,870,871],{"class":187}," input",[27,873,292],{"class":183},[27,875,877,880,882,885],{"class":29,"line":876},45,[27,878,879],{"class":233},"    box-sizing",[27,881,735],{"class":183},[27,883,884],{"class":756}," border-box",[27,886,745],{"class":183},[27,888,890,892,894,896,898],{"class":29,"line":889},46,[27,891,793],{"class":233},[27,893,735],{"class":183},[27,895,798],{"class":738},[27,897,801],{"class":215},[27,899,745],{"class":183},[27,901,903,905,907,909,911],{"class":29,"line":902},47,[27,904,840],{"class":233},[27,906,735],{"class":183},[27,908,783],{"class":738},[27,910,742],{"class":215},[27,912,745],{"class":183},[27,914,916,919,921,924,926],{"class":29,"line":915},48,[27,917,918],{"class":233},"    margin-bottom",[27,920,735],{"class":183},[27,922,923],{"class":738}," 15",[27,925,742],{"class":215},[27,927,745],{"class":183},[27,929,931,933,935,937,939],{"class":29,"line":930},49,[27,932,732],{"class":233},[27,934,735],{"class":183},[27,936,923],{"class":738},[27,938,742],{"class":215},[27,940,745],{"class":183},[27,942,944,947,949,952,954,957,960,963],{"class":29,"line":943},50,[27,945,946],{"class":233},"    border",[27,948,735],{"class":183},[27,950,951],{"class":738}," 1",[27,953,742],{"class":215},[27,955,956],{"class":756}," solid",[27,958,959],{"class":183}," #",[27,961,962],{"class":756},"333333",[27,964,745],{"class":183},[27,966,968,971,973,975,977],{"class":29,"line":967},51,[27,969,970],{"class":233},"    border-radius",[27,972,735],{"class":183},[27,974,783],{"class":738},[27,976,742],{"class":215},[27,978,745],{"class":183},[27,980,982],{"class":29,"line":981},52,[27,983,855],{"class":183},[27,985,987],{"class":29,"line":986},53,[27,988,51],{"emptyLinePlaceholder":50},[27,990,992,994,997],{"class":29,"line":991},54,[27,993,219],{"class":187},[27,995,996],{"class":187}," button",[27,998,292],{"class":183},[27,1000,1002,1004,1006,1008,1010],{"class":29,"line":1001},55,[27,1003,793],{"class":233},[27,1005,735],{"class":183},[27,1007,798],{"class":738},[27,1009,801],{"class":215},[27,1011,745],{"class":183},[27,1013,1015,1017,1019,1021,1023],{"class":29,"line":1014},56,[27,1016,732],{"class":233},[27,1018,735],{"class":183},[27,1020,923],{"class":738},[27,1022,742],{"class":215},[27,1024,745],{"class":183},[27,1026,1028,1031,1033,1035,1037],{"class":29,"line":1027},57,[27,1029,1030],{"class":233},"    background",[27,1032,735],{"class":183},[27,1034,959],{"class":183},[27,1036,962],{"class":756},[27,1038,745],{"class":183},[27,1040,1042,1045,1047,1049,1052],{"class":29,"line":1041},58,[27,1043,1044],{"class":233},"    color",[27,1046,735],{"class":183},[27,1048,959],{"class":183},[27,1050,1051],{"class":756},"ffffff",[27,1053,745],{"class":183},[27,1055,1057,1059,1061,1064],{"class":29,"line":1056},59,[27,1058,946],{"class":233},[27,1060,735],{"class":183},[27,1062,1063],{"class":756}," none",[27,1065,745],{"class":183},[27,1067,1069],{"class":29,"line":1068},60,[27,1070,855],{"class":183},[27,1072,1074,1076,1078],{"class":29,"line":1073},61,[27,1075,337],{"class":183},[27,1077,714],{"class":187},[27,1079,210],{"class":183},[10,1081,1082,1083,1085,1086,1089,1090,1093,1094,1097],{},"In the code block above, we declared a reactive ",[24,1084,219],{}," state to store user inputs. Additionally, we designed a basic form to collect the user's name, email, and password. Each input field is binded to a ",[24,1087,1088],{},"v-model"," to ensure the form data is synchronized with the state. We've also defined a function to handle the form submission, and this function is attached to the form element using the ",[24,1091,1092],{},"@submit.prevent"," directive. The ",[24,1095,1096],{},"prevent"," modifier is used to prevent the page from reloading when the form is submitted. You can customize the submission handling according to your specific needs, such as sending the data to a server. However, for this tutorial, we will keep it simple and only log the data to the console and display an alert.",[10,1099,1100],{},"Now, if you fill out the form and click the \"Sign up\" button, your form details will be logged to the console, and an alert dialog will appear, displaying a success message.",[10,1102,1103],{},"To send an email to the user's email after a successful signup, we will use Nuxt 3's server directory to create an API endpoint. This endpoint will handle email sending. Follow these steps:",[1105,1106,1107,1119,1141,1151,1165],"ul",{},[1108,1109,1110,1111,1114,1115,1118],"li",{},"Create an ",[24,1112,1113],{},"api"," folder inside the ",[24,1116,1117],{},"server"," folder at the root of the application.",[1108,1120,1121,1122,1124,1125,1128,1129,1132,1133,1136,1137,1140],{},"In Nuxt 3, to create an API endpoint, you create a new file in the ",[24,1123,1113],{}," folder with the method attached to the name of the file. For instance, if you want to create a ",[24,1126,1127],{},"GET"," endpoint named \"hello,\" you would create a file named ",[24,1130,1131],{},"hello.get.ts"," if you're using TypeScript or ",[24,1134,1135],{},"hello.get.js"," if you're using JavaScript. In this case, we're creating an email endpoint with the ",[24,1138,1139],{},"POST"," method since we're sending data.",[1108,1142,1143,1144,1147,1148,1150],{},"Create a file named ",[24,1145,1146],{},"email.post.ts"," in the ",[24,1149,1113],{}," folder.",[1108,1152,1153,1154,1156,1157,1160,1161,1164],{},"In the ",[24,1155,1146],{}," endpoint file, we'll export a default function defined with ",[24,1158,1159],{},"defineEventHandler()",". This function will be an asynchronous arrow function that defines the event handler. It'll have an ",[24,1162,1163],{},"event"," object as its argument, which represents an incoming event or request.",[1108,1166,1167,1168,1171,1172,1175,1176,1178],{},"We'll asynchronously read the body content of the event by calling a ",[24,1169,1170],{},"readBody"," function. Then we assign the content of the event's body to a ",[24,1173,1174],{},"body"," variable. This ",[24,1177,1174],{}," variable will contain the data that we receive in the request body.",[10,1180,1181],{},"This endpoint we just created will eventually be responsible for processing the data from our sign-up form and sending an email to the user's email address.",[17,1183,1187],{"className":1184,"code":1185,"language":1186,"meta":22,"style":22},"language-typescript shiki shiki-themes vitesse-light vitesse-black","export default defineEventHandler(async (event) => {\n    const body = await readBody(event);\n    console.log(body);\n});\n","typescript",[24,1188,1189,1217,1238,1252],{"__ignoreMap":22},[27,1190,1191,1194,1197,1200,1202,1205,1208,1210,1213,1215],{"class":29,"line":30},[27,1192,1193],{"class":187},"export",[27,1195,1196],{"class":187}," default",[27,1198,1199],{"class":33}," defineEventHandler",[27,1201,306],{"class":183},[27,1203,1204],{"class":215},"async",[27,1206,1207],{"class":183}," (",[27,1209,1163],{"class":191},[27,1211,1212],{"class":183},")",[27,1214,289],{"class":183},[27,1216,292],{"class":183},[27,1218,1219,1222,1224,1226,1229,1232,1234,1236],{"class":29,"line":47},[27,1220,1221],{"class":215},"    const ",[27,1223,1174],{"class":191},[27,1225,222],{"class":183},[27,1227,1228],{"class":187}," await",[27,1230,1231],{"class":33}," readBody",[27,1233,306],{"class":183},[27,1235,1163],{"class":191},[27,1237,311],{"class":183},[27,1239,1240,1242,1244,1246,1248,1250],{"class":29,"line":54},[27,1241,297],{"class":191},[27,1243,300],{"class":183},[27,1245,303],{"class":33},[27,1247,306],{"class":183},[27,1249,1174],{"class":191},[27,1251,311],{"class":183},[27,1253,1254],{"class":29,"line":61},[27,1255,270],{"class":183},[10,1257,1258,1259,1262,1263,1265,1266,1268,1269,1271,1272,1275],{},"Now that we have created an API endpoint accessible at ",[24,1260,1261],{},"\u002Fapi\u002Femail",", let's test it by returning to ",[24,1264,167],{},". In this step, we will make a ",[24,1267,1139],{}," request to the endpoint using the ",[24,1270,219],{},"'s email and name state as the request body. To make this request, we'll utilize ",[24,1273,1274],{},"useFetch",", which is a composable provided by Nuxt.",[17,1277,1279],{"className":174,"code":1278,"language":176,"meta":22,"style":22},"\u003Cscript lang=\"ts\" setup>\nconst form = reactive({\n    name: \"\",\n    email: \"\",\n    password: \"\",\n});\n\nconst handleSubmission = async () => {\n    await useFetch(\"\u002Fapi\u002Femail\", {\n        method: \"POST\",\n        body: {\n            name: form.name,\n            email: form.email,\n        },\n    });\n    console.log(form);\n    alert(\"Sign up successful!\");\n};\n\u003C\u002Fscript>\n\n\u002F\u002F rest of the code......\n",[24,1280,1281,1301,1313,1323,1333,1343,1347,1351,1369,1390,1405,1413,1428,1443,1448,1453,1467,1481,1485,1493,1497],{"__ignoreMap":22},[27,1282,1283,1285,1287,1289,1291,1293,1295,1297,1299],{"class":29,"line":30},[27,1284,184],{"class":183},[27,1286,188],{"class":187},[27,1288,192],{"class":191},[27,1290,195],{"class":183},[27,1292,199],{"class":198},[27,1294,202],{"class":37},[27,1296,199],{"class":198},[27,1298,207],{"class":191},[27,1300,210],{"class":183},[27,1302,1303,1305,1307,1309,1311],{"class":29,"line":47},[27,1304,216],{"class":215},[27,1306,219],{"class":191},[27,1308,222],{"class":183},[27,1310,225],{"class":33},[27,1312,228],{"class":183},[27,1314,1315,1317,1319,1321],{"class":29,"line":54},[27,1316,234],{"class":233},[27,1318,237],{"class":183},[27,1320,240],{"class":198},[27,1322,243],{"class":183},[27,1324,1325,1327,1329,1331],{"class":29,"line":61},[27,1326,248],{"class":233},[27,1328,237],{"class":183},[27,1330,240],{"class":198},[27,1332,243],{"class":183},[27,1334,1335,1337,1339,1341],{"class":29,"line":90},[27,1336,259],{"class":233},[27,1338,237],{"class":183},[27,1340,240],{"class":198},[27,1342,243],{"class":183},[27,1344,1345],{"class":29,"line":95},[27,1346,270],{"class":183},[27,1348,1349],{"class":29,"line":101},[27,1350,51],{"emptyLinePlaceholder":50},[27,1352,1353,1355,1357,1359,1362,1365,1367],{"class":29,"line":110},[27,1354,216],{"class":215},[27,1356,281],{"class":33},[27,1358,222],{"class":183},[27,1360,1361],{"class":215}," async ",[27,1363,1364],{"class":183},"()",[27,1366,289],{"class":183},[27,1368,292],{"class":183},[27,1370,1371,1374,1377,1379,1381,1383,1385,1388],{"class":29,"line":115},[27,1372,1373],{"class":187},"    await",[27,1375,1376],{"class":33}," useFetch",[27,1378,306],{"class":183},[27,1380,199],{"class":198},[27,1382,1261],{"class":37},[27,1384,199],{"class":198},[27,1386,1387],{"class":183},",",[27,1389,292],{"class":183},[27,1391,1392,1395,1397,1399,1401,1403],{"class":29,"line":121},[27,1393,1394],{"class":233},"        method",[27,1396,237],{"class":183},[27,1398,199],{"class":198},[27,1400,1139],{"class":37},[27,1402,199],{"class":198},[27,1404,243],{"class":183},[27,1406,1407,1410],{"class":29,"line":127},[27,1408,1409],{"class":233},"        body",[27,1411,1412],{"class":183},": {\n",[27,1414,1415,1418,1420,1422,1424,1426],{"class":29,"line":132},[27,1416,1417],{"class":233},"            name",[27,1419,237],{"class":183},[27,1421,219],{"class":191},[27,1423,300],{"class":183},[27,1425,392],{"class":191},[27,1427,243],{"class":183},[27,1429,1430,1433,1435,1437,1439,1441],{"class":29,"line":138},[27,1431,1432],{"class":233},"            email",[27,1434,237],{"class":183},[27,1436,219],{"class":191},[27,1438,300],{"class":183},[27,1440,491],{"class":191},[27,1442,243],{"class":183},[27,1444,1445],{"class":29,"line":144},[27,1446,1447],{"class":183},"        },\n",[27,1449,1450],{"class":29,"line":149},[27,1451,1452],{"class":183},"    });\n",[27,1454,1455,1457,1459,1461,1463,1465],{"class":29,"line":155},[27,1456,297],{"class":191},[27,1458,300],{"class":183},[27,1460,303],{"class":33},[27,1462,306],{"class":183},[27,1464,219],{"class":191},[27,1466,311],{"class":183},[27,1468,1469,1471,1473,1475,1477,1479],{"class":29,"line":399},[27,1470,316],{"class":33},[27,1472,306],{"class":183},[27,1474,199],{"class":198},[27,1476,323],{"class":37},[27,1478,199],{"class":198},[27,1480,311],{"class":183},[27,1482,1483],{"class":29,"line":406},[27,1484,332],{"class":183},[27,1486,1487,1489,1491],{"class":29,"line":468},[27,1488,337],{"class":183},[27,1490,188],{"class":187},[27,1492,210],{"class":183},[27,1494,1495],{"class":29,"line":478},[27,1496,51],{"emptyLinePlaceholder":50},[27,1498,1499],{"class":29,"line":498},[27,1500,1501],{"class":402},"\u002F\u002F rest of the code......\n",[10,1503,1504,1505,1508,1509,1516],{},"Upon submitting the form, you will notice that the name and email are logged to the terminal. Now, let's proceed to send an email to the user's provided email address. To do this, we need to install ",[24,1506,1507],{},"mailgun.js"," since we will be using Mailgun for sending our emails. If you haven't already, you can create an account with ",[1510,1511,1515],"a",{"href":1512,"rel":1513},"https:\u002F\u002Fsignup.mailgun.com\u002Fnew\u002Fsignup",[1514],"nofollow","Mailgun"," to get started with their email services.",[10,1518,1519],{},"In this tutorial, we will utilize Mailgun's sandbox domain, which has limitations allowing only authorized email addresses to receive emails. If you wish to send emails to all user email addresses and also connect your domain, consider upgrading your Mailgun account.",[1105,1521,1522,1531,1538],{},[1108,1523,1524,1525,1530],{},"Visit your ",[1510,1526,1529],{"href":1527,"rel":1528},"https:\u002F\u002Fapp.mailgun.com\u002Fapp\u002Fsending\u002Fdomains",[1514],"Mailgun dashboard"," to copy the domain associated with your Sandbox account.",[1108,1532,1533,1534,1537],{},"We then create a ",[24,1535,1536],{},".env"," file in the root of our application and paste the copied domain into it.",[1108,1539,1540,1541,300],{},"To send emails from our application, we'll need an API key from Mailgun. You can create an API key by following this ",[1510,1542,1545],{"href":1543,"rel":1544},"https:\u002F\u002Fapp.mailgun.com\u002Fsettings\u002Fapi_security",[1514],"link",[17,1547,1549],{"className":19,"code":1548,"language":21,"meta":22,"style":22},"MAILGUN_DOMAIN=sandboxxxxxxxxxxxxxxxxxxxxxc.mailgun.org\nMAILGUN_API_KEY=f23xxxxxxxxxxxxxxxxxxx-xxxxx-xxxxxx\n",[24,1550,1551,1561],{"__ignoreMap":22},[27,1552,1553,1556,1558],{"class":29,"line":30},[27,1554,1555],{"class":191},"MAILGUN_DOMAIN",[27,1557,195],{"class":183},[27,1559,1560],{"class":37},"sandboxxxxxxxxxxxxxxxxxxxxxc.mailgun.org\n",[27,1562,1563,1566,1568],{"class":29,"line":47},[27,1564,1565],{"class":191},"MAILGUN_API_KEY",[27,1567,195],{"class":183},[27,1569,1570],{"class":37},"f23xxxxxxxxxxxxxxxxxxx-xxxxx-xxxxxx\n",[10,1572,1573,1574,1576],{},"Next, we'll need to install ",[24,1575,1507],{}," to handle the email-sending process.",[17,1578,1580],{"className":19,"code":1579,"language":21,"meta":22,"style":22},"# Install mailgun.js\nnpm i mailgun.js or yarn add mailgun.js\n",[24,1581,1582,1587],{"__ignoreMap":22},[27,1583,1584],{"class":29,"line":30},[27,1585,1586],{"class":57},"# Install mailgun.js\n",[27,1588,1589,1592,1595,1598,1601,1604,1607],{"class":29,"line":47},[27,1590,1591],{"class":33},"npm",[27,1593,1594],{"class":37}," i",[27,1596,1597],{"class":37}," mailgun.js",[27,1599,1600],{"class":37}," or",[27,1602,1603],{"class":37}," yarn",[27,1605,1606],{"class":37}," add",[27,1608,1609],{"class":37}," mailgun.js\n",[10,1611,1612,1613,1616,1617,1620,1621,1623,1624,1626,1627,1629,1630,1632,1633,1636],{},"In the endpoint file, we will begin by importing ",[24,1614,1615],{},"formData"," from the ",[24,1618,1619],{},"form-data"," package in Node.js, as well as ",[24,1622,1515],{}," from ",[24,1625,1507],{},". After the imports, we'll instantiate a Mailgun client by creating a new instance of ",[24,1628,1515],{}," and passing ",[24,1631,1615],{}," as an argument. Then, using the ",[24,1634,1635],{},"mailgun.client"," method, we will set up the client with basic authentication credentials, which include the username as 'api' and the API key. The configured client will be assigned to a variable.",[10,1638,1639,1640,1643,1644,1647,1648,1651,1652,1655,1656,1659,1660,1662,1663,1665,1666,1668],{},"Following that, we will create a ",[24,1641,1642],{},"data"," object that includes the properties to be sent in the email, such as the sender (",[24,1645,1646],{},"from","), recipient (",[24,1649,1650],{},"to","), the subject, and the body of the email. Subsequently, we will call the ",[24,1653,1654],{},"create"," method of the ",[24,1657,1658],{},"message"," object within the ",[24,1661,1635],{}," that we assigned to a variable earlier. The ",[24,1664,1654],{}," method will receive the domain and the ",[24,1667,1642],{}," object as its arguments.",[17,1670,1672],{"className":1184,"code":1671,"language":1186,"meta":22,"style":22},"import formData from \"form-data\";\nimport Mailgun from \"mailgun.js\";\n\nexport default defineEventHandler(async (event) => {\n    const body = await readBody(event);\n\n    const mailgun = new Mailgun(formData);\n    const mg = mailgun.client({\n        username: \"api\",\n        key: process.env.MAILGUN_API_KEY,\n    });\n\n    const data = {\n        from: \"User \u003Cadmin@test.test>\",\n        to: body.email,\n        subject: \"Welcome to test app!\",\n        text: `Hi ${body.name}! Welcome to our test app! We're glad to have you onBoard!.`,\n    };\n\n    await mg.messages.create(process.env.MAILGUN_DOMAIN, data);\n});\n",[24,1673,1674,1694,1711,1715,1737,1755,1759,1779,1798,1813,1834,1838,1842,1852,1868,1883,1899,1931,1936,1940,1975],{"__ignoreMap":22},[27,1675,1676,1679,1682,1685,1688,1690,1692],{"class":29,"line":30},[27,1677,1678],{"class":187},"import",[27,1680,1681],{"class":191}," formData",[27,1683,1684],{"class":187}," from",[27,1686,1687],{"class":198}," \"",[27,1689,1619],{"class":37},[27,1691,199],{"class":198},[27,1693,745],{"class":183},[27,1695,1696,1698,1701,1703,1705,1707,1709],{"class":29,"line":47},[27,1697,1678],{"class":187},[27,1699,1700],{"class":191}," Mailgun",[27,1702,1684],{"class":187},[27,1704,1687],{"class":198},[27,1706,1507],{"class":37},[27,1708,199],{"class":198},[27,1710,745],{"class":183},[27,1712,1713],{"class":29,"line":54},[27,1714,51],{"emptyLinePlaceholder":50},[27,1716,1717,1719,1721,1723,1725,1727,1729,1731,1733,1735],{"class":29,"line":61},[27,1718,1193],{"class":187},[27,1720,1196],{"class":187},[27,1722,1199],{"class":33},[27,1724,306],{"class":183},[27,1726,1204],{"class":215},[27,1728,1207],{"class":183},[27,1730,1163],{"class":191},[27,1732,1212],{"class":183},[27,1734,289],{"class":183},[27,1736,292],{"class":183},[27,1738,1739,1741,1743,1745,1747,1749,1751,1753],{"class":29,"line":90},[27,1740,1221],{"class":215},[27,1742,1174],{"class":191},[27,1744,222],{"class":183},[27,1746,1228],{"class":187},[27,1748,1231],{"class":33},[27,1750,306],{"class":183},[27,1752,1163],{"class":191},[27,1754,311],{"class":183},[27,1756,1757],{"class":29,"line":95},[27,1758,51],{"emptyLinePlaceholder":50},[27,1760,1761,1763,1766,1768,1771,1773,1775,1777],{"class":29,"line":101},[27,1762,1221],{"class":215},[27,1764,1765],{"class":191},"mailgun",[27,1767,222],{"class":183},[27,1769,1770],{"class":215}," new ",[27,1772,1515],{"class":33},[27,1774,306],{"class":183},[27,1776,1615],{"class":191},[27,1778,311],{"class":183},[27,1780,1781,1783,1786,1788,1791,1793,1796],{"class":29,"line":110},[27,1782,1221],{"class":215},[27,1784,1785],{"class":191},"mg",[27,1787,222],{"class":183},[27,1789,1790],{"class":191}," mailgun",[27,1792,300],{"class":183},[27,1794,1795],{"class":33},"client",[27,1797,228],{"class":183},[27,1799,1800,1803,1805,1807,1809,1811],{"class":29,"line":115},[27,1801,1802],{"class":233},"        username",[27,1804,237],{"class":183},[27,1806,199],{"class":198},[27,1808,1113],{"class":37},[27,1810,199],{"class":198},[27,1812,243],{"class":183},[27,1814,1815,1818,1820,1823,1825,1828,1830,1832],{"class":29,"line":121},[27,1816,1817],{"class":233},"        key",[27,1819,237],{"class":183},[27,1821,1822],{"class":191},"process",[27,1824,300],{"class":183},[27,1826,1827],{"class":191},"env",[27,1829,300],{"class":183},[27,1831,1565],{"class":191},[27,1833,243],{"class":183},[27,1835,1836],{"class":29,"line":127},[27,1837,1452],{"class":183},[27,1839,1840],{"class":29,"line":132},[27,1841,51],{"emptyLinePlaceholder":50},[27,1843,1844,1846,1848,1850],{"class":29,"line":138},[27,1845,1221],{"class":215},[27,1847,1642],{"class":191},[27,1849,222],{"class":183},[27,1851,292],{"class":183},[27,1853,1854,1857,1859,1861,1864,1866],{"class":29,"line":144},[27,1855,1856],{"class":233},"        from",[27,1858,237],{"class":183},[27,1860,199],{"class":198},[27,1862,1863],{"class":37},"User \u003Cadmin@test.test>",[27,1865,199],{"class":198},[27,1867,243],{"class":183},[27,1869,1870,1873,1875,1877,1879,1881],{"class":29,"line":149},[27,1871,1872],{"class":233},"        to",[27,1874,237],{"class":183},[27,1876,1174],{"class":191},[27,1878,300],{"class":183},[27,1880,491],{"class":191},[27,1882,243],{"class":183},[27,1884,1885,1888,1890,1892,1895,1897],{"class":29,"line":155},[27,1886,1887],{"class":233},"        subject",[27,1889,237],{"class":183},[27,1891,199],{"class":198},[27,1893,1894],{"class":37},"Welcome to test app!",[27,1896,199],{"class":198},[27,1898,243],{"class":183},[27,1900,1901,1904,1906,1909,1912,1915,1917,1919,1921,1924,1927,1929],{"class":29,"line":399},[27,1902,1903],{"class":233},"        text",[27,1905,237],{"class":183},[27,1907,1908],{"class":198},"`",[27,1910,1911],{"class":37},"Hi ",[27,1913,1914],{"class":187},"${",[27,1916,1174],{"class":37},[27,1918,300],{"class":183},[27,1920,392],{"class":37},[27,1922,1923],{"class":187},"}",[27,1925,1926],{"class":37},"! Welcome to our test app! We're glad to have you onBoard!.",[27,1928,1908],{"class":198},[27,1930,243],{"class":183},[27,1932,1933],{"class":29,"line":406},[27,1934,1935],{"class":183},"    };\n",[27,1937,1938],{"class":29,"line":468},[27,1939,51],{"emptyLinePlaceholder":50},[27,1941,1942,1944,1947,1949,1952,1954,1956,1958,1960,1962,1964,1966,1968,1970,1973],{"class":29,"line":478},[27,1943,1373],{"class":187},[27,1945,1946],{"class":191}," mg",[27,1948,300],{"class":183},[27,1950,1951],{"class":191},"messages",[27,1953,300],{"class":183},[27,1955,1654],{"class":33},[27,1957,306],{"class":183},[27,1959,1822],{"class":191},[27,1961,300],{"class":183},[27,1963,1827],{"class":191},[27,1965,300],{"class":183},[27,1967,1555],{"class":191},[27,1969,1387],{"class":183},[27,1971,1972],{"class":191}," data",[27,1974,311],{"class":183},[27,1976,1977],{"class":29,"line":498},[27,1978,270],{"class":183},[10,1980,1981],{},"With these, every time the signup form is submitted successfully, a welcome email will be sent to the user's provided email address.",[714,1983,1984],{},"html pre.shiki code .stPdD, html code.shiki .stPdD{--shiki-default:#999999;--shiki-dark:#444444}html pre.shiki code .sTPum, html code.shiki .sTPum{--shiki-default:#1E754F;--shiki-dark:#4D9375}html pre.shiki code .s9nN2, html code.shiki .s9nN2{--shiki-default:#B07D48;--shiki-dark:#BD976A}html pre.shiki code .scnC2, html code.shiki .scnC2{--shiki-default:#B5695977;--shiki-dark:#C98A7D77}html pre.shiki code .spP0B, html code.shiki .spP0B{--shiki-default:#B56959;--shiki-dark:#C98A7D}html pre.shiki code .s5TCs, html code.shiki .s5TCs{--shiki-default:#AB5959;--shiki-dark:#CB7676}html pre.shiki code .s_xSY, html code.shiki .s_xSY{--shiki-default:#59873A;--shiki-dark:#80A665}html pre.shiki code .sHLBJ, html code.shiki .sHLBJ{--shiki-default:#998418;--shiki-dark:#B8A965}html pre.shiki code .slSvp, html code.shiki .slSvp{--shiki-default:#393A34;--shiki-dark:#DBD7CACC}html pre.shiki code .sqbOQ, html code.shiki .sqbOQ{--shiki-default:#2F798A;--shiki-dark:#4C9A91}html pre.shiki code .sfsYZ, html code.shiki .sfsYZ{--shiki-default:#A65E2B;--shiki-dark:#C99076}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .snYqZ, html code.shiki .snYqZ{--shiki-default:#A0ADA0;--shiki-dark:#758575DD}",{"title":22,"searchDepth":47,"depth":47,"links":1986},[],"2023-10-14T11:03:23.290Z","A practical guide to sending email from a Nuxt 3 application with Mailgun and a server-side API endpoint.","md",{},"\u002Fwritings\u002Fsending-email-from-your-nuxt-3-app-with-mailgun","6",{"title":5,"description":1988},"sending-email-from-your-nuxt-3-app-with-mailgun","writings\u002Fsending-email-from-your-nuxt-3-app-with-mailgun","wL3Td6qSCEjbuMk3-fLL8CLkMTm4pDd2Pk9Up1gJtsQ",[1998,4930,6520],{"id":1999,"title":2000,"body":2001,"brief":4920,"createdAt":4921,"description":4922,"extension":1989,"meta":4923,"navigation":50,"path":4924,"readTime":4925,"seo":4926,"slug":4927,"stem":4928,"__hash__":4929},"writings\u002Fwritings\u002Ftesting-a-nuxt-monorepo-without-losing-your-mind.md","Testing a Nuxt Monorepo Without Losing Your Mind",{"type":7,"value":2002,"toc":4918},[2003,2006,2009,2012,2015,2021,2024,2027,2069,2080,2083,2088,2091,2138,2145,2150,2153,2156,2180,2186,3220,3225,3257,3262,3277,3696,3702,3707,3716,3722,4033,4038,4054,4057,4062,4079,4082,4085,4091,4101,4106,4109,4112,4118,4123,4126,4129,4136,4142,4825,4829,4832,4869,4876,4879,4884,4887,4907,4910,4915],[10,2004,2005],{},"Testing a standard, single-package app is pretty straightforward. But a Nuxt monorepo? That can get messy very fast.",[10,2007,2008],{},"When you're dealing with multiple shared packages, utilities, auto-imports, and all that framework-specific magic, it’s easy to end up with test suites that are either completely shallow or just annoyingly brittle. I recently had to set up the tests for a monorepo I maintain, and to be honest, I just wanted a setup that made sense. I needed something easy to run, easy to understand, and solid enough to give me actual confidence as the codebase grows.",[10,2010,2011],{},"My plan was simple, just nail the foundation first. Unit tests for the logic, component tests for the UI, and Playwright for the end-to-end stuff.",[10,2013,2014],{},"Here’s a walkthrough of the setup I ended up building with Vitest and Playwright.",[10,2016,2017],{},[2018,2019,2020],"strong",{},"The Lay of the Land",[10,2022,2023],{},"Before I start throwing configs around, let's talk about the shape of the codebase I'm working with.",[10,2025,2026],{},"I'm running a pnpm workspace. The codebase is split pretty traditionally, looking something like this:",[17,2028,2032],{"className":2029,"code":2030,"language":2031,"meta":22,"style":22},"language-plaintext shiki shiki-themes vitesse-light vitesse-black","├── apps\u002F                 # Where the actual Nuxt applications live\n├── packages\u002F\n│   └── ui-layer\u002F         # Shared UI components consumed by the apps\n├── package.json\n├── pnpm-workspace.yaml\n├── playwright.config.mts\n└── vitest.config.ts\n","plaintext",[24,2033,2034,2039,2044,2049,2054,2059,2064],{"__ignoreMap":22},[27,2035,2036],{"class":29,"line":30},[27,2037,2038],{},"├── apps\u002F                 # Where the actual Nuxt applications live\n",[27,2040,2041],{"class":29,"line":47},[27,2042,2043],{},"├── packages\u002F\n",[27,2045,2046],{"class":29,"line":54},[27,2047,2048],{},"│   └── ui-layer\u002F         # Shared UI components consumed by the apps\n",[27,2050,2051],{"class":29,"line":61},[27,2052,2053],{},"├── package.json\n",[27,2055,2056],{"class":29,"line":90},[27,2057,2058],{},"├── pnpm-workspace.yaml\n",[27,2060,2061],{"class":29,"line":95},[27,2062,2063],{},"├── playwright.config.mts\n",[27,2065,2066],{"class":29,"line":101},[27,2067,2068],{},"└── vitest.config.ts\n",[10,2070,2071,2072,2075,2076,2079],{},"The most important guys here are the ",[24,2073,2074],{},"vitest.config.ts"," and ",[24,2077,2078],{},"playwright.config.mts",". And do you notice where they are at? Right at the root of the repo.",[10,2081,2082],{},"I didn't want an approach where I had to scatter separate test configurations inside every single app and package folders. I wanted it to be from the top-down. A centralised config makes it way easier to run everything at once, keeps the tooling dependencies clean and ensures no part of the codebase is doing weird things in its own isolated test environment. It basically just guarantees consistent behaviour across the board.",[10,2084,2085],{},[2018,2086,2087],{},"The Gear",[10,2089,2090],{},"Let's not bore ourselves with a step-by-step installation guide like you see in most posts. You know how to use your package manger. But for this setup to work, here is the tooling l installed at the root of my workspace:",[1105,2092,2093,2099,2105,2115,2120,2126,2132],{},[1108,2094,2095,2098],{},[24,2096,2097],{},"vitest"," The core runner.",[1108,2100,2101,2104],{},[24,2102,2103],{},"@nuxt\u002Ftest-utils"," Nuxt's official testing utility (crucial for the component side of things).",[1108,2106,2107,2110,2111,2114],{},[24,2108,2109],{},"@vue\u002Ftest-utils"," & ",[24,2112,2113],{},"happy-dom"," For mounting and interacting with the UI in Vitest.",[1108,2116,2117],{},[24,2118,2119],{},"@testing-library\u002Fvue",[1108,2121,2122,2125],{},[24,2123,2124],{},"@vitejs\u002Fplugin-vue"," Needed so Vitest knows what to do with ⁠.vue⁠ files when running outside of the Nuxt environment.",[1108,2127,2128,2131],{},[24,2129,2130],{},"@playwright\u002Ftest"," For the end-to-end stuff.",[1108,2133,2134,2137],{},[24,2135,2136],{},"@vitest\u002Fcoverage-v8"," For when you want to see those coverage metrics.",[10,2139,2140,2141,2144],{},"Just make sure these are installed as ",[24,2142,2143],{},"devDependencies⁠"," at your monorepo root, not in the individual apps.",[10,2146,2147],{},[2018,2148,2149],{},"Phase 1: The Root Vitest Config (One Config to Rule Them All)",[10,2151,2152],{},"Here is where the magic happens, as mentioned earlier, I'm setting up in the root, so I wrote one single dynamic config to rule all the apps and packages.",[10,2154,2155],{},"The idea here is strict separation of concerns. I split the test runner into two distinct project types for every single workspace:",[2157,2158,2159,2166],"ol",{},[1108,2160,2161,2162,2165],{},"Unit Tests (",[24,2163,2164],{},"*.unit.spec.ts","): Runs purely in Node. Super fast, meant for business logic, utilities and composables.",[1108,2167,2168,2169,2172,2173,2175,2176,2179],{},"Component Tests (",[24,2170,2171],{},"*.component.spec.ts","): Runs in ",[24,2174,2113],{}," with the Vue plugin enabled. Meant for testing actual ",[24,2177,2178],{},".vue"," files and UI behaviour.",[10,2181,2182,2183,2185],{},"Here is the ",[24,2184,2074],{}," in its full glory lol:",[17,2187,2189],{"className":1184,"code":2188,"language":1186,"meta":22,"style":22},"import vue from \"@vitejs\u002Fplugin-vue\";\nimport { resolve } from \"node:path\";\nimport { defineConfig } from \"vitest\u002Fconfig\";\n\nconst workspaceRoot = process.cwd();\nconst uiLayerRoot = resolve(workspaceRoot, \"packages\u002Fui-layer\");\n\n\u002F\u002F nuxt loads the layer config while resolving each runtime project.\nconst workspaces = [\n  { name: \"admin\", root: resolve(workspaceRoot, \"apps\u002Fadmin\") },\n  { name: \"distributor\", root: resolve(workspaceRoot, \"apps\u002Fdistributor\") },\n  { name: \"provider\", root: resolve(workspaceRoot, \"apps\u002Fprovider\") },\n  { name: \"ui-layer\", root: uiLayerRoot },\n];\n\nexport default defineConfig(() => {\n  const unitProjects = workspaces.map(({ name, root }) => ({\n    extends: true as const,\n    root,\n    resolve: {\n      alias: {\n        \"@mca\u002Fui-layer\": uiLayerRoot,\n        \"@\": root,\n        \"~\": root,\n      },\n    },\n    test: {\n      name: `unit-${name}`,\n      include: [\"**\u002F*.unit.spec.ts\"],\n      environment: \"node\" as const,\n      setupFiles: [resolve(workspaceRoot, \"test\u002Fsetup.ts\")],\n    },\n  }));\n\n  const componentProjects = workspaces.map(({ name, root }) => ({\n    extends: true as const,\n    root,\n    plugins: [vue({})],\n    resolve: {\n      alias: {\n        \"@mca\u002Fui-layer\": uiLayerRoot,\n        \"@\": root,\n        \"~\": root,\n      },\n    },\n    test: {\n      name: `component-${name}`,\n      include: [\"**\u002F*.component.spec.ts\"],\n      environment: \"happy-dom\" as const,\n      setupFiles: [resolve(workspaceRoot, \"test\u002Fsetup.ts\")],\n    },\n  }));\n\n  return {\n    test: {\n      globals: true,\n      clearMocks: true,\n      restoreMocks: true,\n      projects: [...unitProjects, ...componentProjects],\n      coverage: {\n        provider: \"v8\" as const,\n        reporter: [\"text\", \"html\", \"lcov\"],\n        reportsDirectory: \".\u002Fcoverage\",\n        exclude: [\n          \"**\u002F*.unit.spec.ts\", \n          \"**\u002F*.component.spec.ts\", \n          \"**\u002F*.d.ts\", \n          \"**\u002F.nuxt\u002F**\", \n          \"**\u002F.output\u002F**\", \n          \"**\u002Fnode_modules\u002F**\", \n          \"**\u002F*.config.*\", \n          \"test\u002F**\"\n        ],\n      },\n    },\n  };\n});\n",[24,2190,2191,2208,2232,2254,2258,2278,2304,2308,2313,2325,2368,2406,2444,2470,2475,2479,2494,2531,2549,2556,2563,2570,2586,2601,2616,2621,2626,2633,2655,2673,2693,2718,2722,2727,2731,2760,2774,2780,2792,2798,2804,2818,2832,2846,2850,2854,2860,2881,2896,2914,2936,2940,2944,2948,2955,2961,2972,2983,2994,3011,3018,3038,3072,3089,3098,3111,3122,3134,3146,3158,3170,3182,3193,3199,3204,3209,3215],{"__ignoreMap":22},[27,2192,2193,2195,2198,2200,2202,2204,2206],{"class":29,"line":30},[27,2194,1678],{"class":187},[27,2196,2197],{"class":191}," vue",[27,2199,1684],{"class":187},[27,2201,1687],{"class":198},[27,2203,2124],{"class":37},[27,2205,199],{"class":198},[27,2207,745],{"class":183},[27,2209,2210,2212,2215,2218,2221,2223,2225,2228,2230],{"class":29,"line":47},[27,2211,1678],{"class":187},[27,2213,2214],{"class":183}," {",[27,2216,2217],{"class":191}," resolve",[27,2219,2220],{"class":183}," }",[27,2222,1684],{"class":187},[27,2224,1687],{"class":198},[27,2226,2227],{"class":37},"node:path",[27,2229,199],{"class":198},[27,2231,745],{"class":183},[27,2233,2234,2236,2238,2241,2243,2245,2247,2250,2252],{"class":29,"line":54},[27,2235,1678],{"class":187},[27,2237,2214],{"class":183},[27,2239,2240],{"class":191}," defineConfig",[27,2242,2220],{"class":183},[27,2244,1684],{"class":187},[27,2246,1687],{"class":198},[27,2248,2249],{"class":37},"vitest\u002Fconfig",[27,2251,199],{"class":198},[27,2253,745],{"class":183},[27,2255,2256],{"class":29,"line":61},[27,2257,51],{"emptyLinePlaceholder":50},[27,2259,2260,2262,2265,2267,2270,2272,2275],{"class":29,"line":90},[27,2261,216],{"class":215},[27,2263,2264],{"class":191},"workspaceRoot",[27,2266,222],{"class":183},[27,2268,2269],{"class":191}," process",[27,2271,300],{"class":183},[27,2273,2274],{"class":33},"cwd",[27,2276,2277],{"class":183},"();\n",[27,2279,2280,2282,2285,2287,2289,2291,2293,2295,2297,2300,2302],{"class":29,"line":95},[27,2281,216],{"class":215},[27,2283,2284],{"class":191},"uiLayerRoot",[27,2286,222],{"class":183},[27,2288,2217],{"class":33},[27,2290,306],{"class":183},[27,2292,2264],{"class":191},[27,2294,1387],{"class":183},[27,2296,1687],{"class":198},[27,2298,2299],{"class":37},"packages\u002Fui-layer",[27,2301,199],{"class":198},[27,2303,311],{"class":183},[27,2305,2306],{"class":29,"line":101},[27,2307,51],{"emptyLinePlaceholder":50},[27,2309,2310],{"class":29,"line":110},[27,2311,2312],{"class":57},"\u002F\u002F nuxt loads the layer config while resolving each runtime project.\n",[27,2314,2315,2317,2320,2322],{"class":29,"line":115},[27,2316,216],{"class":215},[27,2318,2319],{"class":191},"workspaces",[27,2321,222],{"class":183},[27,2323,2324],{"class":183}," [\n",[27,2326,2327,2330,2332,2334,2336,2339,2341,2344,2347,2349,2352,2354,2356,2358,2360,2363,2365],{"class":29,"line":121},[27,2328,2329],{"class":183},"  { ",[27,2331,392],{"class":233},[27,2333,237],{"class":183},[27,2335,199],{"class":198},[27,2337,2338],{"class":37},"admin",[27,2340,199],{"class":198},[27,2342,2343],{"class":183},", ",[27,2345,2346],{"class":233},"root",[27,2348,237],{"class":183},[27,2350,2351],{"class":33},"resolve",[27,2353,306],{"class":183},[27,2355,2264],{"class":191},[27,2357,2343],{"class":183},[27,2359,199],{"class":198},[27,2361,2362],{"class":37},"apps\u002Fadmin",[27,2364,199],{"class":198},[27,2366,2367],{"class":183},") },\n",[27,2369,2370,2372,2374,2376,2378,2381,2383,2385,2387,2389,2391,2393,2395,2397,2399,2402,2404],{"class":29,"line":127},[27,2371,2329],{"class":183},[27,2373,392],{"class":233},[27,2375,237],{"class":183},[27,2377,199],{"class":198},[27,2379,2380],{"class":37},"distributor",[27,2382,199],{"class":198},[27,2384,2343],{"class":183},[27,2386,2346],{"class":233},[27,2388,237],{"class":183},[27,2390,2351],{"class":33},[27,2392,306],{"class":183},[27,2394,2264],{"class":191},[27,2396,2343],{"class":183},[27,2398,199],{"class":198},[27,2400,2401],{"class":37},"apps\u002Fdistributor",[27,2403,199],{"class":198},[27,2405,2367],{"class":183},[27,2407,2408,2410,2412,2414,2416,2419,2421,2423,2425,2427,2429,2431,2433,2435,2437,2440,2442],{"class":29,"line":132},[27,2409,2329],{"class":183},[27,2411,392],{"class":233},[27,2413,237],{"class":183},[27,2415,199],{"class":198},[27,2417,2418],{"class":37},"provider",[27,2420,199],{"class":198},[27,2422,2343],{"class":183},[27,2424,2346],{"class":233},[27,2426,237],{"class":183},[27,2428,2351],{"class":33},[27,2430,306],{"class":183},[27,2432,2264],{"class":191},[27,2434,2343],{"class":183},[27,2436,199],{"class":198},[27,2438,2439],{"class":37},"apps\u002Fprovider",[27,2441,199],{"class":198},[27,2443,2367],{"class":183},[27,2445,2446,2448,2450,2452,2454,2457,2459,2461,2463,2465,2467],{"class":29,"line":138},[27,2447,2329],{"class":183},[27,2449,392],{"class":233},[27,2451,237],{"class":183},[27,2453,199],{"class":198},[27,2455,2456],{"class":37},"ui-layer",[27,2458,199],{"class":198},[27,2460,2343],{"class":183},[27,2462,2346],{"class":233},[27,2464,237],{"class":183},[27,2466,2284],{"class":191},[27,2468,2469],{"class":183}," },\n",[27,2471,2472],{"class":29,"line":144},[27,2473,2474],{"class":183},"];\n",[27,2476,2477],{"class":29,"line":149},[27,2478,51],{"emptyLinePlaceholder":50},[27,2480,2481,2483,2485,2487,2490,2492],{"class":29,"line":155},[27,2482,1193],{"class":187},[27,2484,1196],{"class":187},[27,2486,2240],{"class":33},[27,2488,2489],{"class":183},"(()",[27,2491,289],{"class":183},[27,2493,292],{"class":183},[27,2495,2496,2499,2502,2504,2507,2509,2512,2515,2518,2520,2523,2526,2528],{"class":29,"line":399},[27,2497,2498],{"class":215},"  const ",[27,2500,2501],{"class":191},"unitProjects",[27,2503,222],{"class":183},[27,2505,2506],{"class":191}," workspaces",[27,2508,300],{"class":183},[27,2510,2511],{"class":33},"map",[27,2513,2514],{"class":183},"(({",[27,2516,2517],{"class":191}," name",[27,2519,1387],{"class":183},[27,2521,2522],{"class":191}," root",[27,2524,2525],{"class":183}," })",[27,2527,289],{"class":183},[27,2529,2530],{"class":183}," ({\n",[27,2532,2533,2536,2538,2541,2544,2547],{"class":29,"line":406},[27,2534,2535],{"class":233},"    extends",[27,2537,237],{"class":183},[27,2539,2540],{"class":187},"true",[27,2542,2543],{"class":187}," as",[27,2545,2546],{"class":215}," const",[27,2548,243],{"class":183},[27,2550,2551,2554],{"class":29,"line":468},[27,2552,2553],{"class":191},"    root",[27,2555,243],{"class":183},[27,2557,2558,2561],{"class":29,"line":478},[27,2559,2560],{"class":233},"    resolve",[27,2562,1412],{"class":183},[27,2564,2565,2568],{"class":29,"line":498},[27,2566,2567],{"class":233},"      alias",[27,2569,1412],{"class":183},[27,2571,2572,2575,2578,2580,2582,2584],{"class":29,"line":504},[27,2573,2574],{"class":198},"        \"",[27,2576,2577],{"class":37},"@mca\u002Fui-layer",[27,2579,199],{"class":198},[27,2581,237],{"class":183},[27,2583,2284],{"class":191},[27,2585,243],{"class":183},[27,2587,2588,2590,2593,2595,2597,2599],{"class":29,"line":557},[27,2589,2574],{"class":198},[27,2591,2592],{"class":37},"@",[27,2594,199],{"class":198},[27,2596,237],{"class":183},[27,2598,2346],{"class":191},[27,2600,243],{"class":183},[27,2602,2603,2605,2608,2610,2612,2614],{"class":29,"line":566},[27,2604,2574],{"class":198},[27,2606,2607],{"class":37},"~",[27,2609,199],{"class":198},[27,2611,237],{"class":183},[27,2613,2346],{"class":191},[27,2615,243],{"class":183},[27,2617,2618],{"class":29,"line":586},[27,2619,2620],{"class":183},"      },\n",[27,2622,2623],{"class":29,"line":592},[27,2624,2625],{"class":183},"    },\n",[27,2627,2628,2631],{"class":29,"line":645},[27,2629,2630],{"class":233},"    test",[27,2632,1412],{"class":183},[27,2634,2635,2638,2640,2642,2645,2647,2649,2651,2653],{"class":29,"line":654},[27,2636,2637],{"class":233},"      name",[27,2639,237],{"class":183},[27,2641,1908],{"class":198},[27,2643,2644],{"class":37},"unit-",[27,2646,1914],{"class":187},[27,2648,392],{"class":37},[27,2650,1923],{"class":187},[27,2652,1908],{"class":198},[27,2654,243],{"class":183},[27,2656,2657,2660,2663,2665,2668,2670],{"class":29,"line":685},[27,2658,2659],{"class":233},"      include",[27,2661,2662],{"class":183},": [",[27,2664,199],{"class":198},[27,2666,2667],{"class":37},"**\u002F*.unit.spec.ts",[27,2669,199],{"class":198},[27,2671,2672],{"class":183},"],\n",[27,2674,2675,2678,2680,2682,2685,2687,2689,2691],{"class":29,"line":695},[27,2676,2677],{"class":233},"      environment",[27,2679,237],{"class":183},[27,2681,199],{"class":198},[27,2683,2684],{"class":37},"node",[27,2686,199],{"class":198},[27,2688,2543],{"class":187},[27,2690,2546],{"class":215},[27,2692,243],{"class":183},[27,2694,2695,2698,2700,2702,2704,2706,2708,2710,2713,2715],{"class":29,"line":704},[27,2696,2697],{"class":233},"      setupFiles",[27,2699,2662],{"class":183},[27,2701,2351],{"class":33},[27,2703,306],{"class":183},[27,2705,2264],{"class":191},[27,2707,2343],{"class":183},[27,2709,199],{"class":198},[27,2711,2712],{"class":37},"test\u002Fsetup.ts",[27,2714,199],{"class":198},[27,2716,2717],{"class":183},")],\n",[27,2719,2720],{"class":29,"line":709},[27,2721,2625],{"class":183},[27,2723,2724],{"class":29,"line":722},[27,2725,2726],{"class":183},"  }));\n",[27,2728,2729],{"class":29,"line":729},[27,2730,51],{"emptyLinePlaceholder":50},[27,2732,2733,2735,2738,2740,2742,2744,2746,2748,2750,2752,2754,2756,2758],{"class":29,"line":748},[27,2734,2498],{"class":215},[27,2736,2737],{"class":191},"componentProjects",[27,2739,222],{"class":183},[27,2741,2506],{"class":191},[27,2743,300],{"class":183},[27,2745,2511],{"class":33},[27,2747,2514],{"class":183},[27,2749,2517],{"class":191},[27,2751,1387],{"class":183},[27,2753,2522],{"class":191},[27,2755,2525],{"class":183},[27,2757,289],{"class":183},[27,2759,2530],{"class":183},[27,2761,2762,2764,2766,2768,2770,2772],{"class":29,"line":762},[27,2763,2535],{"class":233},[27,2765,237],{"class":183},[27,2767,2540],{"class":187},[27,2769,2543],{"class":187},[27,2771,2546],{"class":215},[27,2773,243],{"class":183},[27,2775,2776,2778],{"class":29,"line":775},[27,2777,2553],{"class":191},[27,2779,243],{"class":183},[27,2781,2782,2785,2787,2789],{"class":29,"line":790},[27,2783,2784],{"class":233},"    plugins",[27,2786,2662],{"class":183},[27,2788,176],{"class":33},[27,2790,2791],{"class":183},"({})],\n",[27,2793,2794,2796],{"class":29,"line":806},[27,2795,2560],{"class":233},[27,2797,1412],{"class":183},[27,2799,2800,2802],{"class":29,"line":821},[27,2801,2567],{"class":233},[27,2803,1412],{"class":183},[27,2805,2806,2808,2810,2812,2814,2816],{"class":29,"line":837},[27,2807,2574],{"class":198},[27,2809,2577],{"class":37},[27,2811,199],{"class":198},[27,2813,237],{"class":183},[27,2815,2284],{"class":191},[27,2817,243],{"class":183},[27,2819,2820,2822,2824,2826,2828,2830],{"class":29,"line":852},[27,2821,2574],{"class":198},[27,2823,2592],{"class":37},[27,2825,199],{"class":198},[27,2827,237],{"class":183},[27,2829,2346],{"class":191},[27,2831,243],{"class":183},[27,2833,2834,2836,2838,2840,2842,2844],{"class":29,"line":858},[27,2835,2574],{"class":198},[27,2837,2607],{"class":37},[27,2839,199],{"class":198},[27,2841,237],{"class":183},[27,2843,2346],{"class":191},[27,2845,243],{"class":183},[27,2847,2848],{"class":29,"line":863},[27,2849,2620],{"class":183},[27,2851,2852],{"class":29,"line":876},[27,2853,2625],{"class":183},[27,2855,2856,2858],{"class":29,"line":889},[27,2857,2630],{"class":233},[27,2859,1412],{"class":183},[27,2861,2862,2864,2866,2868,2871,2873,2875,2877,2879],{"class":29,"line":902},[27,2863,2637],{"class":233},[27,2865,237],{"class":183},[27,2867,1908],{"class":198},[27,2869,2870],{"class":37},"component-",[27,2872,1914],{"class":187},[27,2874,392],{"class":37},[27,2876,1923],{"class":187},[27,2878,1908],{"class":198},[27,2880,243],{"class":183},[27,2882,2883,2885,2887,2889,2892,2894],{"class":29,"line":915},[27,2884,2659],{"class":233},[27,2886,2662],{"class":183},[27,2888,199],{"class":198},[27,2890,2891],{"class":37},"**\u002F*.component.spec.ts",[27,2893,199],{"class":198},[27,2895,2672],{"class":183},[27,2897,2898,2900,2902,2904,2906,2908,2910,2912],{"class":29,"line":930},[27,2899,2677],{"class":233},[27,2901,237],{"class":183},[27,2903,199],{"class":198},[27,2905,2113],{"class":37},[27,2907,199],{"class":198},[27,2909,2543],{"class":187},[27,2911,2546],{"class":215},[27,2913,243],{"class":183},[27,2915,2916,2918,2920,2922,2924,2926,2928,2930,2932,2934],{"class":29,"line":943},[27,2917,2697],{"class":233},[27,2919,2662],{"class":183},[27,2921,2351],{"class":33},[27,2923,306],{"class":183},[27,2925,2264],{"class":191},[27,2927,2343],{"class":183},[27,2929,199],{"class":198},[27,2931,2712],{"class":37},[27,2933,199],{"class":198},[27,2935,2717],{"class":183},[27,2937,2938],{"class":29,"line":967},[27,2939,2625],{"class":183},[27,2941,2942],{"class":29,"line":981},[27,2943,2726],{"class":183},[27,2945,2946],{"class":29,"line":986},[27,2947,51],{"emptyLinePlaceholder":50},[27,2949,2950,2953],{"class":29,"line":991},[27,2951,2952],{"class":187},"  return",[27,2954,292],{"class":183},[27,2956,2957,2959],{"class":29,"line":1001},[27,2958,2630],{"class":233},[27,2960,1412],{"class":183},[27,2962,2963,2966,2968,2970],{"class":29,"line":1014},[27,2964,2965],{"class":233},"      globals",[27,2967,237],{"class":183},[27,2969,2540],{"class":187},[27,2971,243],{"class":183},[27,2973,2974,2977,2979,2981],{"class":29,"line":1027},[27,2975,2976],{"class":233},"      clearMocks",[27,2978,237],{"class":183},[27,2980,2540],{"class":187},[27,2982,243],{"class":183},[27,2984,2985,2988,2990,2992],{"class":29,"line":1041},[27,2986,2987],{"class":233},"      restoreMocks",[27,2989,237],{"class":183},[27,2991,2540],{"class":187},[27,2993,243],{"class":183},[27,2995,2996,2999,3002,3004,3007,3009],{"class":29,"line":1056},[27,2997,2998],{"class":233},"      projects",[27,3000,3001],{"class":183},": [...",[27,3003,2501],{"class":191},[27,3005,3006],{"class":183},", ...",[27,3008,2737],{"class":191},[27,3010,2672],{"class":183},[27,3012,3013,3016],{"class":29,"line":1068},[27,3014,3015],{"class":233},"      coverage",[27,3017,1412],{"class":183},[27,3019,3020,3023,3025,3027,3030,3032,3034,3036],{"class":29,"line":1073},[27,3021,3022],{"class":233},"        provider",[27,3024,237],{"class":183},[27,3026,199],{"class":198},[27,3028,3029],{"class":37},"v8",[27,3031,199],{"class":198},[27,3033,2543],{"class":187},[27,3035,2546],{"class":215},[27,3037,243],{"class":183},[27,3039,3041,3044,3046,3048,3050,3052,3054,3056,3059,3061,3063,3065,3068,3070],{"class":29,"line":3040},62,[27,3042,3043],{"class":233},"        reporter",[27,3045,2662],{"class":183},[27,3047,199],{"class":198},[27,3049,445],{"class":37},[27,3051,199],{"class":198},[27,3053,2343],{"class":183},[27,3055,199],{"class":198},[27,3057,3058],{"class":37},"html",[27,3060,199],{"class":198},[27,3062,2343],{"class":183},[27,3064,199],{"class":198},[27,3066,3067],{"class":37},"lcov",[27,3069,199],{"class":198},[27,3071,2672],{"class":183},[27,3073,3075,3078,3080,3082,3085,3087],{"class":29,"line":3074},63,[27,3076,3077],{"class":233},"        reportsDirectory",[27,3079,237],{"class":183},[27,3081,199],{"class":198},[27,3083,3084],{"class":37},".\u002Fcoverage",[27,3086,199],{"class":198},[27,3088,243],{"class":183},[27,3090,3092,3095],{"class":29,"line":3091},64,[27,3093,3094],{"class":233},"        exclude",[27,3096,3097],{"class":183},": [\n",[27,3099,3101,3104,3106,3108],{"class":29,"line":3100},65,[27,3102,3103],{"class":198},"          \"",[27,3105,2667],{"class":37},[27,3107,199],{"class":198},[27,3109,3110],{"class":183},", \n",[27,3112,3114,3116,3118,3120],{"class":29,"line":3113},66,[27,3115,3103],{"class":198},[27,3117,2891],{"class":37},[27,3119,199],{"class":198},[27,3121,3110],{"class":183},[27,3123,3125,3127,3130,3132],{"class":29,"line":3124},67,[27,3126,3103],{"class":198},[27,3128,3129],{"class":37},"**\u002F*.d.ts",[27,3131,199],{"class":198},[27,3133,3110],{"class":183},[27,3135,3137,3139,3142,3144],{"class":29,"line":3136},68,[27,3138,3103],{"class":198},[27,3140,3141],{"class":37},"**\u002F.nuxt\u002F**",[27,3143,199],{"class":198},[27,3145,3110],{"class":183},[27,3147,3149,3151,3154,3156],{"class":29,"line":3148},69,[27,3150,3103],{"class":198},[27,3152,3153],{"class":37},"**\u002F.output\u002F**",[27,3155,199],{"class":198},[27,3157,3110],{"class":183},[27,3159,3161,3163,3166,3168],{"class":29,"line":3160},70,[27,3162,3103],{"class":198},[27,3164,3165],{"class":37},"**\u002Fnode_modules\u002F**",[27,3167,199],{"class":198},[27,3169,3110],{"class":183},[27,3171,3173,3175,3178,3180],{"class":29,"line":3172},71,[27,3174,3103],{"class":198},[27,3176,3177],{"class":37},"**\u002F*.config.*",[27,3179,199],{"class":198},[27,3181,3110],{"class":183},[27,3183,3185,3187,3190],{"class":29,"line":3184},72,[27,3186,3103],{"class":198},[27,3188,3189],{"class":37},"test\u002F**",[27,3191,3192],{"class":198},"\"\n",[27,3194,3196],{"class":29,"line":3195},73,[27,3197,3198],{"class":183},"        ],\n",[27,3200,3202],{"class":29,"line":3201},74,[27,3203,2620],{"class":183},[27,3205,3207],{"class":29,"line":3206},75,[27,3208,2625],{"class":183},[27,3210,3212],{"class":29,"line":3211},76,[27,3213,3214],{"class":183},"  };\n",[27,3216,3218],{"class":29,"line":3217},77,[27,3219,270],{"class":183},[10,3221,3222],{},[2018,3223,3224],{},"Why this works so well",[2157,3226,3227,3237],{},[1108,3228,3229,3230,3232,3233,3236],{},"No DOM overhead for pure logic: Notice how the ",[24,3231,2501],{}," use ",[24,3234,3235],{},"environment: \"node\""," and doesn't load the Vue plugin? If I'm testing a utility function that formats a currency string, I don't need to boot up a simulated browser environment. It keeps the unit tests blazing fast.",[1108,3238,3239,3240,3242,3243,3245,3246,3249,3250,3253,3254,3256],{},"Handling Cross-Workspace Aliasing: When you test outside of the Nuxt context, Vitest has no idea what ",[24,3241,2607],{},"⁠ or ",[24,3244,2592],{},"⁠ means. By explicitly mapping ",[24,3247,3248],{},"@mca\u002Fui-layer⁠"," and the Nuxt aliases (",[24,3251,3252],{},"⁠@⁠,"," ",[24,3255,2607],{},"⁠) dynamically to each workspace's root, imports resolve seamlessly without Nuxt having to hold our hand.",[10,3258,3259],{},[2018,3260,3261],{},"Pro-Tip: The Setup File",[10,3263,3264,3265,3268,3269,3272,3273,3276],{},"You might notice this line ",[24,3266,3267],{},"setupFiles: [resolve(workspaceRoot, \"test\u002Fsetup.ts\")]"," in the config. Because my unit tests runs in Node, they don't have access to browser APIs. I use this to dynamically polyfill things like ",[24,3270,3271],{},"localStorage⁠"," only when the environment needs it, without overwriting ",[24,3274,3275],{},"happy-dom⁠'s"," native implementation.",[17,3278,3280],{"className":1184,"code":3279,"language":1186,"meta":22,"style":22},"import { vi } from \"vitest\";\n\nconst createStorage = () => {\n  const values = new Map\u003Cstring, string>();\n\n  return {\n    getItem: (key: string) => values.get(key) ?? null,\n    setItem: (key: string, value: string) => values.set(key, String(value)),\n    removeItem: (key: string) => values.delete(key),\n    clear: () => values.clear(),\n    key: (index: number) => Array.from(values.keys())[index] ?? null,\n    get length() {\n      return values.size;\n    },\n  };\n};\n\nfor (const key of [\"localStorage\", \"sessionStorage\"] as const) {\n  if (typeof globalThis[key]?.getItem !== \"function\") {\n    vi.stubGlobal(key, createStorage());\n  }\n}\n",[24,3281,3282,3303,3307,3322,3350,3354,3360,3400,3447,3476,3494,3541,3552,3567,3571,3575,3579,3583,3627,3665,3687,3692],{"__ignoreMap":22},[27,3283,3284,3286,3288,3291,3293,3295,3297,3299,3301],{"class":29,"line":30},[27,3285,1678],{"class":187},[27,3287,2214],{"class":183},[27,3289,3290],{"class":191}," vi",[27,3292,2220],{"class":183},[27,3294,1684],{"class":187},[27,3296,1687],{"class":198},[27,3298,2097],{"class":37},[27,3300,199],{"class":198},[27,3302,745],{"class":183},[27,3304,3305],{"class":29,"line":47},[27,3306,51],{"emptyLinePlaceholder":50},[27,3308,3309,3311,3314,3316,3318,3320],{"class":29,"line":54},[27,3310,216],{"class":215},[27,3312,3313],{"class":33},"createStorage",[27,3315,222],{"class":183},[27,3317,286],{"class":183},[27,3319,289],{"class":183},[27,3321,292],{"class":183},[27,3323,3324,3326,3329,3331,3333,3336,3338,3342,3344,3347],{"class":29,"line":61},[27,3325,2498],{"class":215},[27,3327,3328],{"class":191},"values",[27,3330,222],{"class":183},[27,3332,1770],{"class":215},[27,3334,3335],{"class":33},"Map",[27,3337,184],{"class":183},[27,3339,3341],{"class":3340},"s_NWU","string",[27,3343,1387],{"class":183},[27,3345,3346],{"class":3340}," string",[27,3348,3349],{"class":183},">();\n",[27,3351,3352],{"class":29,"line":90},[27,3353,51],{"emptyLinePlaceholder":50},[27,3355,3356,3358],{"class":29,"line":95},[27,3357,2952],{"class":187},[27,3359,292],{"class":183},[27,3361,3362,3365,3368,3371,3373,3375,3378,3380,3382,3385,3387,3389,3392,3395,3398],{"class":29,"line":101},[27,3363,3364],{"class":33},"    getItem",[27,3366,3367],{"class":183},": (",[27,3369,3370],{"class":191},"key",[27,3372,237],{"class":183},[27,3374,3341],{"class":3340},[27,3376,3377],{"class":183},") => ",[27,3379,3328],{"class":191},[27,3381,300],{"class":183},[27,3383,3384],{"class":33},"get",[27,3386,306],{"class":183},[27,3388,3370],{"class":191},[27,3390,3391],{"class":183},") ",[27,3393,3394],{"class":215},"??",[27,3396,3397],{"class":215}," null",[27,3399,243],{"class":183},[27,3401,3402,3405,3407,3409,3411,3413,3415,3418,3420,3422,3424,3426,3428,3431,3433,3435,3437,3440,3442,3444],{"class":29,"line":110},[27,3403,3404],{"class":33},"    setItem",[27,3406,3367],{"class":183},[27,3408,3370],{"class":191},[27,3410,237],{"class":183},[27,3412,3341],{"class":3340},[27,3414,2343],{"class":183},[27,3416,3417],{"class":191},"value",[27,3419,237],{"class":183},[27,3421,3341],{"class":3340},[27,3423,3377],{"class":183},[27,3425,3328],{"class":191},[27,3427,300],{"class":183},[27,3429,3430],{"class":33},"set",[27,3432,306],{"class":183},[27,3434,3370],{"class":191},[27,3436,2343],{"class":183},[27,3438,3439],{"class":33},"String",[27,3441,306],{"class":183},[27,3443,3417],{"class":191},[27,3445,3446],{"class":183},")),\n",[27,3448,3449,3452,3454,3456,3458,3460,3462,3464,3466,3469,3471,3473],{"class":29,"line":115},[27,3450,3451],{"class":33},"    removeItem",[27,3453,3367],{"class":183},[27,3455,3370],{"class":191},[27,3457,237],{"class":183},[27,3459,3341],{"class":3340},[27,3461,3377],{"class":183},[27,3463,3328],{"class":191},[27,3465,300],{"class":183},[27,3467,3468],{"class":33},"delete",[27,3470,306],{"class":183},[27,3472,3370],{"class":191},[27,3474,3475],{"class":183},"),\n",[27,3477,3478,3481,3484,3486,3488,3491],{"class":29,"line":121},[27,3479,3480],{"class":33},"    clear",[27,3482,3483],{"class":183},": () => ",[27,3485,3328],{"class":191},[27,3487,300],{"class":183},[27,3489,3490],{"class":33},"clear",[27,3492,3493],{"class":183},"(),\n",[27,3495,3496,3499,3501,3504,3506,3509,3511,3514,3516,3518,3520,3522,3524,3527,3530,3532,3535,3537,3539],{"class":29,"line":127},[27,3497,3498],{"class":33},"    key",[27,3500,3367],{"class":183},[27,3502,3503],{"class":191},"index",[27,3505,237],{"class":183},[27,3507,3508],{"class":3340},"number",[27,3510,3377],{"class":183},[27,3512,3513],{"class":191},"Array",[27,3515,300],{"class":183},[27,3517,1646],{"class":33},[27,3519,306],{"class":183},[27,3521,3328],{"class":191},[27,3523,300],{"class":183},[27,3525,3526],{"class":33},"keys",[27,3528,3529],{"class":183},"())[",[27,3531,3503],{"class":191},[27,3533,3534],{"class":183},"] ",[27,3536,3394],{"class":215},[27,3538,3397],{"class":215},[27,3540,243],{"class":183},[27,3542,3543,3546,3549],{"class":29,"line":132},[27,3544,3545],{"class":215},"    get",[27,3547,3548],{"class":33}," length",[27,3550,3551],{"class":183},"() {\n",[27,3553,3554,3557,3560,3562,3565],{"class":29,"line":138},[27,3555,3556],{"class":187},"      return",[27,3558,3559],{"class":191}," values",[27,3561,300],{"class":183},[27,3563,3564],{"class":191},"size",[27,3566,745],{"class":183},[27,3568,3569],{"class":29,"line":144},[27,3570,2625],{"class":183},[27,3572,3573],{"class":29,"line":149},[27,3574,3214],{"class":183},[27,3576,3577],{"class":29,"line":155},[27,3578,332],{"class":183},[27,3580,3581],{"class":29,"line":399},[27,3582,51],{"emptyLinePlaceholder":50},[27,3584,3585,3588,3590,3592,3594,3597,3600,3602,3605,3607,3609,3611,3614,3616,3619,3621,3623,3625],{"class":29,"line":406},[27,3586,3587],{"class":187},"for",[27,3589,1207],{"class":183},[27,3591,216],{"class":215},[27,3593,3370],{"class":191},[27,3595,3596],{"class":215}," of",[27,3598,3599],{"class":183}," [",[27,3601,199],{"class":198},[27,3603,3604],{"class":37},"localStorage",[27,3606,199],{"class":198},[27,3608,1387],{"class":183},[27,3610,1687],{"class":198},[27,3612,3613],{"class":37},"sessionStorage",[27,3615,199],{"class":198},[27,3617,3618],{"class":183},"]",[27,3620,2543],{"class":187},[27,3622,2546],{"class":215},[27,3624,1212],{"class":183},[27,3626,292],{"class":183},[27,3628,3629,3632,3634,3637,3640,3643,3645,3648,3651,3654,3656,3659,3661,3663],{"class":29,"line":468},[27,3630,3631],{"class":187},"  if",[27,3633,1207],{"class":183},[27,3635,3636],{"class":215},"typeof",[27,3638,3639],{"class":191}," globalThis",[27,3641,3642],{"class":183},"[",[27,3644,3370],{"class":191},[27,3646,3647],{"class":183},"]?.",[27,3649,3650],{"class":191},"getItem",[27,3652,3653],{"class":215}," !==",[27,3655,1687],{"class":198},[27,3657,3658],{"class":37},"function",[27,3660,199],{"class":198},[27,3662,1212],{"class":183},[27,3664,292],{"class":183},[27,3666,3667,3670,3672,3675,3677,3679,3681,3684],{"class":29,"line":478},[27,3668,3669],{"class":191},"    vi",[27,3671,300],{"class":183},[27,3673,3674],{"class":33},"stubGlobal",[27,3676,306],{"class":183},[27,3678,3370],{"class":191},[27,3680,1387],{"class":183},[27,3682,3683],{"class":33}," createStorage",[27,3685,3686],{"class":183},"());\n",[27,3688,3689],{"class":29,"line":498},[27,3690,3691],{"class":183},"  }\n",[27,3693,3694],{"class":29,"line":504},[27,3695,855],{"class":183},[10,3697,3698],{},[3699,3700,3701],"em",{},"This simple check makes the setup file completely polymorphic across both environments.",[10,3703,3704],{},[2018,3705,3706],{},"Phase 2: UI Components",[10,3708,3709,3710,3712,3713,3715],{},"Testing Vue components in a monorepo could get messy when you try to boot up Nuxt just to test a simple button. But because I separated my ",[24,3711,2737],{}," to run in ",[24,3714,2113],{}," in the Vitest config, I can test my shared UI layer instantly.",[10,3717,3718,3719,3721],{},"Let's look at a component from the ",[24,3720,2299],{}," folder. Here is a standard test for a Badge.vue component:",[17,3723,3725],{"className":1184,"code":3724,"language":1186,"meta":22,"style":22},"import { render, screen } from \"@testing-library\u002Fvue\";\nimport { describe, expect, it } from \"vitest\";\nimport Badge from \".\u002Fbadge.vue\";\n\ndescribe(\"Badge\", () => {\n  it(\"renders its label from props\", async () => {\n    render(Badge, { props: { label: \"Active\" } });\n\n    \u002F\u002F Testing what the user actually sees\n    expect(screen.getByText(\"Active\")).toBeInTheDocument();\n  });\n\n  it(\"renders a named label slot when provided\", async () => {\n    render(Badge, {\n      slots: { label: \"Custom label\" },\n    });\n\n    expect(screen.getByText(\"Custom label\")).toBeInTheDocument();\n  });\n});\n",[24,3726,3727,3753,3784,3802,3806,3828,3853,3887,3891,3896,3927,3932,3936,3959,3971,3991,3995,3999,4025,4029],{"__ignoreMap":22},[27,3728,3729,3731,3733,3736,3738,3741,3743,3745,3747,3749,3751],{"class":29,"line":30},[27,3730,1678],{"class":187},[27,3732,2214],{"class":183},[27,3734,3735],{"class":191}," render",[27,3737,1387],{"class":183},[27,3739,3740],{"class":191}," screen",[27,3742,2220],{"class":183},[27,3744,1684],{"class":187},[27,3746,1687],{"class":198},[27,3748,2119],{"class":37},[27,3750,199],{"class":198},[27,3752,745],{"class":183},[27,3754,3755,3757,3759,3762,3764,3767,3769,3772,3774,3776,3778,3780,3782],{"class":29,"line":47},[27,3756,1678],{"class":187},[27,3758,2214],{"class":183},[27,3760,3761],{"class":191}," describe",[27,3763,1387],{"class":183},[27,3765,3766],{"class":191}," expect",[27,3768,1387],{"class":183},[27,3770,3771],{"class":191}," it",[27,3773,2220],{"class":183},[27,3775,1684],{"class":187},[27,3777,1687],{"class":198},[27,3779,2097],{"class":37},[27,3781,199],{"class":198},[27,3783,745],{"class":183},[27,3785,3786,3788,3791,3793,3795,3798,3800],{"class":29,"line":54},[27,3787,1678],{"class":187},[27,3789,3790],{"class":191}," Badge",[27,3792,1684],{"class":187},[27,3794,1687],{"class":198},[27,3796,3797],{"class":37},".\u002Fbadge.vue",[27,3799,199],{"class":198},[27,3801,745],{"class":183},[27,3803,3804],{"class":29,"line":61},[27,3805,51],{"emptyLinePlaceholder":50},[27,3807,3808,3811,3813,3815,3818,3820,3822,3824,3826],{"class":29,"line":90},[27,3809,3810],{"class":33},"describe",[27,3812,306],{"class":183},[27,3814,199],{"class":198},[27,3816,3817],{"class":37},"Badge",[27,3819,199],{"class":198},[27,3821,1387],{"class":183},[27,3823,286],{"class":183},[27,3825,289],{"class":183},[27,3827,292],{"class":183},[27,3829,3830,3833,3835,3837,3840,3842,3844,3847,3849,3851],{"class":29,"line":95},[27,3831,3832],{"class":33},"  it",[27,3834,306],{"class":183},[27,3836,199],{"class":198},[27,3838,3839],{"class":37},"renders its label from props",[27,3841,199],{"class":198},[27,3843,1387],{"class":183},[27,3845,3846],{"class":215}," async",[27,3848,286],{"class":183},[27,3850,289],{"class":183},[27,3852,292],{"class":183},[27,3854,3855,3858,3860,3862,3864,3867,3870,3873,3875,3877,3879,3882,3884],{"class":29,"line":101},[27,3856,3857],{"class":33},"    render",[27,3859,306],{"class":183},[27,3861,3817],{"class":191},[27,3863,1387],{"class":183},[27,3865,3866],{"class":183}," { ",[27,3868,3869],{"class":233},"props",[27,3871,3872],{"class":183},": { ",[27,3874,382],{"class":233},[27,3876,237],{"class":183},[27,3878,199],{"class":198},[27,3880,3881],{"class":37},"Active",[27,3883,199],{"class":198},[27,3885,3886],{"class":183}," } });\n",[27,3888,3889],{"class":29,"line":110},[27,3890,51],{"emptyLinePlaceholder":50},[27,3892,3893],{"class":29,"line":115},[27,3894,3895],{"class":57},"    \u002F\u002F Testing what the user actually sees\n",[27,3897,3898,3901,3903,3906,3908,3911,3913,3915,3917,3919,3922,3925],{"class":29,"line":121},[27,3899,3900],{"class":33},"    expect",[27,3902,306],{"class":183},[27,3904,3905],{"class":191},"screen",[27,3907,300],{"class":183},[27,3909,3910],{"class":33},"getByText",[27,3912,306],{"class":183},[27,3914,199],{"class":198},[27,3916,3881],{"class":37},[27,3918,199],{"class":198},[27,3920,3921],{"class":183},")).",[27,3923,3924],{"class":33},"toBeInTheDocument",[27,3926,2277],{"class":183},[27,3928,3929],{"class":29,"line":127},[27,3930,3931],{"class":183},"  });\n",[27,3933,3934],{"class":29,"line":132},[27,3935,51],{"emptyLinePlaceholder":50},[27,3937,3938,3940,3942,3944,3947,3949,3951,3953,3955,3957],{"class":29,"line":138},[27,3939,3832],{"class":33},[27,3941,306],{"class":183},[27,3943,199],{"class":198},[27,3945,3946],{"class":37},"renders a named label slot when provided",[27,3948,199],{"class":198},[27,3950,1387],{"class":183},[27,3952,3846],{"class":215},[27,3954,286],{"class":183},[27,3956,289],{"class":183},[27,3958,292],{"class":183},[27,3960,3961,3963,3965,3967,3969],{"class":29,"line":144},[27,3962,3857],{"class":33},[27,3964,306],{"class":183},[27,3966,3817],{"class":191},[27,3968,1387],{"class":183},[27,3970,292],{"class":183},[27,3972,3973,3976,3978,3980,3982,3984,3987,3989],{"class":29,"line":149},[27,3974,3975],{"class":233},"      slots",[27,3977,3872],{"class":183},[27,3979,382],{"class":233},[27,3981,237],{"class":183},[27,3983,199],{"class":198},[27,3985,3986],{"class":37},"Custom label",[27,3988,199],{"class":198},[27,3990,2469],{"class":183},[27,3992,3993],{"class":29,"line":155},[27,3994,1452],{"class":183},[27,3996,3997],{"class":29,"line":399},[27,3998,51],{"emptyLinePlaceholder":50},[27,4000,4001,4003,4005,4007,4009,4011,4013,4015,4017,4019,4021,4023],{"class":29,"line":406},[27,4002,3900],{"class":33},[27,4004,306],{"class":183},[27,4006,3905],{"class":191},[27,4008,300],{"class":183},[27,4010,3910],{"class":33},[27,4012,306],{"class":183},[27,4014,199],{"class":198},[27,4016,3986],{"class":37},[27,4018,199],{"class":198},[27,4020,3921],{"class":183},[27,4022,3924],{"class":33},[27,4024,2277],{"class":183},[27,4026,4027],{"class":29,"line":468},[27,4028,3931],{"class":183},[27,4030,4031],{"class":29,"line":478},[27,4032,270],{"class":183},[10,4034,4035],{},[2018,4036,4037],{},"The Beauty of Isolation",[10,4039,4040,4041,4044,4045,4047,4048,4050,4051,4053],{},"Look at the imports. Notice what's missing? ",[2018,4042,4043],{},"Nuxt."," This is a pure Vue component test using ",[24,4046,2119],{},". Because this component lives in my shared ",[24,4049,2456],{}," package and relies on standard Vue props and slots, it is completely framework-agnostic. The Vitest config mounts it in ",[24,4052,2113],{},", verifies the DOM output, and gets out of the way fast.",[10,4055,4056],{},"By choosing to test these presentational components in isolation and focusing strictly on what the user actually sees on the screen, I avoid dealing with high maintenance tests altogether..",[10,4058,4059],{},[2018,4060,4061],{},"What about Nuxt's auto-imports?",[10,4063,4064,4065,1274,4068,4071,4072,4075,4076],{},"If you’re looking at that ",[24,4066,4067],{},"vitest.config.ts&#x60; and thinking, *\"How does this handle components that actually use Nuxt stuffs like*&#x20;",[24,4069,4070],{},"*,*&#x20;","useRoute",[24,4073,4074],{},"*, or*&#x20;","useRuntimeConfig`",[3699,4077,4078],{},"?\"",[10,4080,4081],{},"The honest answer? It doesn't.",[10,4083,4084],{},"I really just don’t want to test those components in Vitest. And honestly, I don't think you should either.",[10,4086,4087,4088,300],{},"Trying to component-test \"smart\" components that are tightly coupled to frameworks could be a trap. The moment you try to run them in Vitest, you'll be having ",[24,4089,4090],{},"ReferenceError: useFetch is not defined",[10,4092,4093,4094,4097,4098,4100],{},"To fix that, you have to mock the router. Then mock the fetch interceptors. Then mock the whole Nuxt context. By the time you write 50 lines of setup code just to get a single button to render, ",[2018,4095,4096],{},"you aren't testing your component anymore, you are just testing your mocks."," If Nuxt changes how ",[24,4099,1274],{}," behaves under the hood in a minor update, your tests will instantly break, even if the actual app runs perfectly fine in the browser. It’s just a massive waste of time in my opinion.",[10,4102,4103],{},[2018,4104,4105],{},"Enter Playwright",[10,4107,4108],{},"This is why my setup is split. I let Vitest do what it’s actually good at: testing pure business logic and the dumb UI components in my shared layer. It does this in milliseconds without any overhead.",[10,4110,4111],{},"But for the framework-heavy components? That is a job for Playwright.",[10,4113,4114,4115,4117],{},"Playwright doesn't care about mocking ",[24,4116,1274],{}," because it actually boots up the real Nuxt server in a real browser. You test the app exactly how a user interacts with it, and you don't have to write a single mock to do it.",[10,4119,4120],{},[2018,4121,4122],{},"Phase 3: The Playwright Setup",[10,4124,4125],{},"So if I'm not testing Nuxt heavy components in Vitest, how do I actually test them?",[10,4127,4128],{},"I run them in a real browser using Playwright.",[10,4130,4131,4132,4135],{},"The codebase has multiple apps sitting in the ",[24,4133,4134],{},"apps\u002F"," directory. I'm definitely not going to be opening three terminal tabs to manually boot up dev servers every time I want to run E2E tests.",[10,4137,4138,4139,4141],{},"Instead, I just let the root ",[24,4140,2078],{}," handle the orchestration.",[17,4143,4145],{"className":1184,"code":4144,"language":1186,"meta":22,"style":22},"import { fileURLToPath } from \"node:url\";\nimport { defineConfig, devices } from \"@playwright\u002Ftest\";\nimport type { ConfigOptions } from \"@nuxt\u002Ftest-utils\u002Fplaywright\";\n\nexport default defineConfig\u003CConfigOptions>({\n  testDir: \".\u002Ftests\u002Fe2e\",\n  testMatch: \"**\u002F*.spec.ts\",\n  fullyParallel: true,\n  forbidOnly: !!process.env.CI,\n  retries: process.env.CI ? 2 : 0,\n  reporter: process.env.CI ? \"github\" : \"list\",\n  outputDir: \".\u002Ftest-results\u002Fplaywright\",\n  use: {\n    ...devices[\"Desktop Chrome\"],\n    baseURL: \"http:\u002F\u002Flocalhost:3000\",\n    trace: \"on-first-retry\",\n    screenshot: \"only-on-failure\",\n    video: \"retain-on-failure\",\n  },\n  projects: [\n    {\n      name: \"admin-chromium\",\n      testMatch: \"admin\u002F**\u002F*.spec.ts\",\n      use: {\n        ...devices[\"Desktop Chrome\"],\n        nuxt: { rootDir: fileURLToPath(new URL(\".\u002Fapps\u002Fadmin\", import.meta.url)) },\n      },\n    },\n    {\n      name: \"distributor-chromium\",\n      testMatch: \"distributor\u002F**\u002F*.spec.ts\",\n      use: {\n        ...devices[\"Desktop Chrome\"],\n        nuxt: { rootDir: fileURLToPath(new URL(\".\u002Fapps\u002Fdistributor\", import.meta.url)) },\n      },\n    },\n    {\n      name: \"provider-chromium\",\n      testMatch: \"provider\u002F**\u002F*.spec.ts\",\n      use: {\n        ...devices[\"Desktop Chrome\"],\n        nuxt: { rootDir: fileURLToPath(new URL(\".\u002Fapps\u002Fprovider\", import.meta.url)) },\n      },\n    },\n  ],\n});\n",[24,4146,4147,4169,4194,4218,4222,4238,4254,4270,4281,4304,4334,4371,4387,4394,4413,4429,4445,4461,4477,4482,4489,4494,4509,4525,4532,4549,4598,4602,4606,4610,4625,4640,4646,4662,4703,4707,4711,4715,4730,4745,4751,4767,4808,4812,4816,4821],{"__ignoreMap":22},[27,4148,4149,4151,4153,4156,4158,4160,4162,4165,4167],{"class":29,"line":30},[27,4150,1678],{"class":187},[27,4152,2214],{"class":183},[27,4154,4155],{"class":191}," fileURLToPath",[27,4157,2220],{"class":183},[27,4159,1684],{"class":187},[27,4161,1687],{"class":198},[27,4163,4164],{"class":37},"node:url",[27,4166,199],{"class":198},[27,4168,745],{"class":183},[27,4170,4171,4173,4175,4177,4179,4182,4184,4186,4188,4190,4192],{"class":29,"line":47},[27,4172,1678],{"class":187},[27,4174,2214],{"class":183},[27,4176,2240],{"class":191},[27,4178,1387],{"class":183},[27,4180,4181],{"class":191}," devices",[27,4183,2220],{"class":183},[27,4185,1684],{"class":187},[27,4187,1687],{"class":198},[27,4189,2130],{"class":37},[27,4191,199],{"class":198},[27,4193,745],{"class":183},[27,4195,4196,4198,4200,4202,4205,4207,4209,4211,4214,4216],{"class":29,"line":54},[27,4197,1678],{"class":187},[27,4199,438],{"class":187},[27,4201,2214],{"class":183},[27,4203,4204],{"class":191}," ConfigOptions",[27,4206,2220],{"class":183},[27,4208,1684],{"class":187},[27,4210,1687],{"class":198},[27,4212,4213],{"class":37},"@nuxt\u002Ftest-utils\u002Fplaywright",[27,4215,199],{"class":198},[27,4217,745],{"class":183},[27,4219,4220],{"class":29,"line":61},[27,4221,51],{"emptyLinePlaceholder":50},[27,4223,4224,4226,4228,4230,4232,4235],{"class":29,"line":90},[27,4225,1193],{"class":187},[27,4227,1196],{"class":187},[27,4229,2240],{"class":33},[27,4231,184],{"class":183},[27,4233,4234],{"class":3340},"ConfigOptions",[27,4236,4237],{"class":183},">({\n",[27,4239,4240,4243,4245,4247,4250,4252],{"class":29,"line":95},[27,4241,4242],{"class":233},"  testDir",[27,4244,237],{"class":183},[27,4246,199],{"class":198},[27,4248,4249],{"class":37},".\u002Ftests\u002Fe2e",[27,4251,199],{"class":198},[27,4253,243],{"class":183},[27,4255,4256,4259,4261,4263,4266,4268],{"class":29,"line":101},[27,4257,4258],{"class":233},"  testMatch",[27,4260,237],{"class":183},[27,4262,199],{"class":198},[27,4264,4265],{"class":37},"**\u002F*.spec.ts",[27,4267,199],{"class":198},[27,4269,243],{"class":183},[27,4271,4272,4275,4277,4279],{"class":29,"line":110},[27,4273,4274],{"class":233},"  fullyParallel",[27,4276,237],{"class":183},[27,4278,2540],{"class":187},[27,4280,243],{"class":183},[27,4282,4283,4286,4288,4291,4293,4295,4297,4299,4302],{"class":29,"line":115},[27,4284,4285],{"class":233},"  forbidOnly",[27,4287,237],{"class":183},[27,4289,4290],{"class":215},"!!",[27,4292,1822],{"class":191},[27,4294,300],{"class":183},[27,4296,1827],{"class":191},[27,4298,300],{"class":183},[27,4300,4301],{"class":191},"CI",[27,4303,243],{"class":183},[27,4305,4306,4309,4311,4313,4315,4317,4319,4321,4324,4327,4330,4332],{"class":29,"line":121},[27,4307,4308],{"class":233},"  retries",[27,4310,237],{"class":183},[27,4312,1822],{"class":191},[27,4314,300],{"class":183},[27,4316,1827],{"class":191},[27,4318,300],{"class":183},[27,4320,4301],{"class":191},[27,4322,4323],{"class":215}," ?",[27,4325,4326],{"class":738}," 2",[27,4328,4329],{"class":215}," :",[27,4331,829],{"class":738},[27,4333,243],{"class":183},[27,4335,4336,4339,4341,4343,4345,4347,4349,4351,4353,4355,4358,4360,4362,4364,4367,4369],{"class":29,"line":127},[27,4337,4338],{"class":233},"  reporter",[27,4340,237],{"class":183},[27,4342,1822],{"class":191},[27,4344,300],{"class":183},[27,4346,1827],{"class":191},[27,4348,300],{"class":183},[27,4350,4301],{"class":191},[27,4352,4323],{"class":215},[27,4354,1687],{"class":198},[27,4356,4357],{"class":37},"github",[27,4359,199],{"class":198},[27,4361,4329],{"class":215},[27,4363,1687],{"class":198},[27,4365,4366],{"class":37},"list",[27,4368,199],{"class":198},[27,4370,243],{"class":183},[27,4372,4373,4376,4378,4380,4383,4385],{"class":29,"line":132},[27,4374,4375],{"class":233},"  outputDir",[27,4377,237],{"class":183},[27,4379,199],{"class":198},[27,4381,4382],{"class":37},".\u002Ftest-results\u002Fplaywright",[27,4384,199],{"class":198},[27,4386,243],{"class":183},[27,4388,4389,4392],{"class":29,"line":138},[27,4390,4391],{"class":233},"  use",[27,4393,1412],{"class":183},[27,4395,4396,4399,4402,4404,4406,4409,4411],{"class":29,"line":144},[27,4397,4398],{"class":183},"    ...",[27,4400,4401],{"class":191},"devices",[27,4403,3642],{"class":183},[27,4405,199],{"class":198},[27,4407,4408],{"class":37},"Desktop Chrome",[27,4410,199],{"class":198},[27,4412,2672],{"class":183},[27,4414,4415,4418,4420,4422,4425,4427],{"class":29,"line":149},[27,4416,4417],{"class":233},"    baseURL",[27,4419,237],{"class":183},[27,4421,199],{"class":198},[27,4423,4424],{"class":37},"http:\u002F\u002Flocalhost:3000",[27,4426,199],{"class":198},[27,4428,243],{"class":183},[27,4430,4431,4434,4436,4438,4441,4443],{"class":29,"line":155},[27,4432,4433],{"class":233},"    trace",[27,4435,237],{"class":183},[27,4437,199],{"class":198},[27,4439,4440],{"class":37},"on-first-retry",[27,4442,199],{"class":198},[27,4444,243],{"class":183},[27,4446,4447,4450,4452,4454,4457,4459],{"class":29,"line":399},[27,4448,4449],{"class":233},"    screenshot",[27,4451,237],{"class":183},[27,4453,199],{"class":198},[27,4455,4456],{"class":37},"only-on-failure",[27,4458,199],{"class":198},[27,4460,243],{"class":183},[27,4462,4463,4466,4468,4470,4473,4475],{"class":29,"line":406},[27,4464,4465],{"class":233},"    video",[27,4467,237],{"class":183},[27,4469,199],{"class":198},[27,4471,4472],{"class":37},"retain-on-failure",[27,4474,199],{"class":198},[27,4476,243],{"class":183},[27,4478,4479],{"class":29,"line":468},[27,4480,4481],{"class":183},"  },\n",[27,4483,4484,4487],{"class":29,"line":478},[27,4485,4486],{"class":233},"  projects",[27,4488,3097],{"class":183},[27,4490,4491],{"class":29,"line":498},[27,4492,4493],{"class":183},"    {\n",[27,4495,4496,4498,4500,4502,4505,4507],{"class":29,"line":504},[27,4497,2637],{"class":233},[27,4499,237],{"class":183},[27,4501,199],{"class":198},[27,4503,4504],{"class":37},"admin-chromium",[27,4506,199],{"class":198},[27,4508,243],{"class":183},[27,4510,4511,4514,4516,4518,4521,4523],{"class":29,"line":557},[27,4512,4513],{"class":233},"      testMatch",[27,4515,237],{"class":183},[27,4517,199],{"class":198},[27,4519,4520],{"class":37},"admin\u002F**\u002F*.spec.ts",[27,4522,199],{"class":198},[27,4524,243],{"class":183},[27,4526,4527,4530],{"class":29,"line":566},[27,4528,4529],{"class":233},"      use",[27,4531,1412],{"class":183},[27,4533,4534,4537,4539,4541,4543,4545,4547],{"class":29,"line":586},[27,4535,4536],{"class":183},"        ...",[27,4538,4401],{"class":191},[27,4540,3642],{"class":183},[27,4542,199],{"class":198},[27,4544,4408],{"class":37},[27,4546,199],{"class":198},[27,4548,2672],{"class":183},[27,4550,4551,4554,4556,4559,4561,4564,4566,4569,4572,4574,4576,4579,4581,4583,4585,4587,4590,4592,4595],{"class":29,"line":592},[27,4552,4553],{"class":233},"        nuxt",[27,4555,3872],{"class":183},[27,4557,4558],{"class":233},"rootDir",[27,4560,237],{"class":183},[27,4562,4563],{"class":33},"fileURLToPath",[27,4565,306],{"class":183},[27,4567,4568],{"class":215},"new",[27,4570,4571],{"class":33}," URL",[27,4573,306],{"class":183},[27,4575,199],{"class":198},[27,4577,4578],{"class":37},".\u002Fapps\u002Fadmin",[27,4580,199],{"class":198},[27,4582,2343],{"class":183},[27,4584,1678],{"class":187},[27,4586,300],{"class":183},[27,4588,4589],{"class":233},"meta",[27,4591,300],{"class":183},[27,4593,4594],{"class":191},"url",[27,4596,4597],{"class":183},")) },\n",[27,4599,4600],{"class":29,"line":645},[27,4601,2620],{"class":183},[27,4603,4604],{"class":29,"line":654},[27,4605,2625],{"class":183},[27,4607,4608],{"class":29,"line":685},[27,4609,4493],{"class":183},[27,4611,4612,4614,4616,4618,4621,4623],{"class":29,"line":695},[27,4613,2637],{"class":233},[27,4615,237],{"class":183},[27,4617,199],{"class":198},[27,4619,4620],{"class":37},"distributor-chromium",[27,4622,199],{"class":198},[27,4624,243],{"class":183},[27,4626,4627,4629,4631,4633,4636,4638],{"class":29,"line":704},[27,4628,4513],{"class":233},[27,4630,237],{"class":183},[27,4632,199],{"class":198},[27,4634,4635],{"class":37},"distributor\u002F**\u002F*.spec.ts",[27,4637,199],{"class":198},[27,4639,243],{"class":183},[27,4641,4642,4644],{"class":29,"line":709},[27,4643,4529],{"class":233},[27,4645,1412],{"class":183},[27,4647,4648,4650,4652,4654,4656,4658,4660],{"class":29,"line":722},[27,4649,4536],{"class":183},[27,4651,4401],{"class":191},[27,4653,3642],{"class":183},[27,4655,199],{"class":198},[27,4657,4408],{"class":37},[27,4659,199],{"class":198},[27,4661,2672],{"class":183},[27,4663,4664,4666,4668,4670,4672,4674,4676,4678,4680,4682,4684,4687,4689,4691,4693,4695,4697,4699,4701],{"class":29,"line":729},[27,4665,4553],{"class":233},[27,4667,3872],{"class":183},[27,4669,4558],{"class":233},[27,4671,237],{"class":183},[27,4673,4563],{"class":33},[27,4675,306],{"class":183},[27,4677,4568],{"class":215},[27,4679,4571],{"class":33},[27,4681,306],{"class":183},[27,4683,199],{"class":198},[27,4685,4686],{"class":37},".\u002Fapps\u002Fdistributor",[27,4688,199],{"class":198},[27,4690,2343],{"class":183},[27,4692,1678],{"class":187},[27,4694,300],{"class":183},[27,4696,4589],{"class":233},[27,4698,300],{"class":183},[27,4700,4594],{"class":191},[27,4702,4597],{"class":183},[27,4704,4705],{"class":29,"line":748},[27,4706,2620],{"class":183},[27,4708,4709],{"class":29,"line":762},[27,4710,2625],{"class":183},[27,4712,4713],{"class":29,"line":775},[27,4714,4493],{"class":183},[27,4716,4717,4719,4721,4723,4726,4728],{"class":29,"line":790},[27,4718,2637],{"class":233},[27,4720,237],{"class":183},[27,4722,199],{"class":198},[27,4724,4725],{"class":37},"provider-chromium",[27,4727,199],{"class":198},[27,4729,243],{"class":183},[27,4731,4732,4734,4736,4738,4741,4743],{"class":29,"line":806},[27,4733,4513],{"class":233},[27,4735,237],{"class":183},[27,4737,199],{"class":198},[27,4739,4740],{"class":37},"provider\u002F**\u002F*.spec.ts",[27,4742,199],{"class":198},[27,4744,243],{"class":183},[27,4746,4747,4749],{"class":29,"line":821},[27,4748,4529],{"class":233},[27,4750,1412],{"class":183},[27,4752,4753,4755,4757,4759,4761,4763,4765],{"class":29,"line":837},[27,4754,4536],{"class":183},[27,4756,4401],{"class":191},[27,4758,3642],{"class":183},[27,4760,199],{"class":198},[27,4762,4408],{"class":37},[27,4764,199],{"class":198},[27,4766,2672],{"class":183},[27,4768,4769,4771,4773,4775,4777,4779,4781,4783,4785,4787,4789,4792,4794,4796,4798,4800,4802,4804,4806],{"class":29,"line":852},[27,4770,4553],{"class":233},[27,4772,3872],{"class":183},[27,4774,4558],{"class":233},[27,4776,237],{"class":183},[27,4778,4563],{"class":33},[27,4780,306],{"class":183},[27,4782,4568],{"class":215},[27,4784,4571],{"class":33},[27,4786,306],{"class":183},[27,4788,199],{"class":198},[27,4790,4791],{"class":37},".\u002Fapps\u002Fprovider",[27,4793,199],{"class":198},[27,4795,2343],{"class":183},[27,4797,1678],{"class":187},[27,4799,300],{"class":183},[27,4801,4589],{"class":233},[27,4803,300],{"class":183},[27,4805,4594],{"class":191},[27,4807,4597],{"class":183},[27,4809,4810],{"class":29,"line":858},[27,4811,2620],{"class":183},[27,4813,4814],{"class":29,"line":863},[27,4815,2625],{"class":183},[27,4817,4818],{"class":29,"line":876},[27,4819,4820],{"class":183},"  ],\n",[27,4822,4823],{"class":29,"line":889},[27,4824,270],{"class":183},[10,4826,4827],{},[2018,4828,3224],{},[10,4830,4831],{},"There are two details in this config that save me from doing manual setup:",[2157,4833,4834,4857],{},[1108,4835,4836,3253,4839,3253,4841,4844,4845,4848,4849,4852,4853,4856],{},[2018,4837,4838],{},"The",[24,4840,4213],{},[2018,4842,4843],{},"Integration:"," Instead of standard Playwright types, I typed the config with ",[24,4846,4847],{},"\u003CConfigOptions>"," from Nuxt's official test utilities. This exposes the ",[24,4850,4851],{},"nuxt"," key in the ",[24,4854,4855],{},"use"," block.",[1108,4858,4859,4862,4863,4866,4867,300],{},[2018,4860,4861],{},"Project-Scoped Roots:"," In the ",[24,4864,4865],{},"projects"," array, I explicitly pass the path to each app's directory using ",[24,4868,4563],{},[10,4870,4871,4872,4875],{},"Because of this, Playwright is smart. When I run ",[24,4873,4874],{},"npx playwright test",", it looks at the project, maps it to the correct directory in my monorepo, automatically builds and boots the local Nuxt server for that specific app, runs the tests inside a real browser, and shuts the server down when it's finished.",[10,4877,4878],{},"I don't have to manage any local ports, I don't have to coordinate server lifecycles in my CI\u002FCD pipeline, and I don't have to manually mock a single API call. It just works.",[10,4880,4881],{},[2018,4882,4883],{},"The Pragmatic Monorepo",[10,4885,4886],{},"Setting up tests in a monorepo usually feels like fighting against your tools. But by letting each tool do exactly what it was built for, testing actually becomes invisible.",[1105,4888,4889,4895,4901],{},[1108,4890,4891,4894],{},[2018,4892,4893],{},"Vitest"," handles the pure logic and presentational UI components in milliseconds.",[1108,4896,4897,4900],{},[2018,4898,4899],{},"Playwright"," boots up the Nuxt apps and tests them like a real user.",[1108,4902,4903,4906],{},[2018,4904,4905],{},"The Root Configs"," act as the orchestrators, meaning I don't have to jump between a terminal tabs to get confidence in my code.",[10,4908,4909],{},"I don't think I need 100% test coverage on every single layer. I just need tests that are fast, reliable, and actually catch bugs without forcing me to write 50 lines of mocks.",[10,4911,4912],{},[2018,4913,4914],{},"At what point do you give up on unit testing a component and just let E2E handle it? Do you mock out your framework utilities in Vitest, or push them straight to Playwright? Let me know what you think.",[714,4916,4917],{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sTPum, html code.shiki .sTPum{--shiki-default:#1E754F;--shiki-dark:#4D9375}html pre.shiki code .s9nN2, html code.shiki .s9nN2{--shiki-default:#B07D48;--shiki-dark:#BD976A}html pre.shiki code .scnC2, html code.shiki .scnC2{--shiki-default:#B5695977;--shiki-dark:#C98A7D77}html pre.shiki code .spP0B, html code.shiki .spP0B{--shiki-default:#B56959;--shiki-dark:#C98A7D}html pre.shiki code .stPdD, html code.shiki .stPdD{--shiki-default:#999999;--shiki-dark:#444444}html pre.shiki code .s5TCs, html code.shiki .s5TCs{--shiki-default:#AB5959;--shiki-dark:#CB7676}html pre.shiki code .s_xSY, html code.shiki .s_xSY{--shiki-default:#59873A;--shiki-dark:#80A665}html pre.shiki code .snYqZ, html code.shiki .snYqZ{--shiki-default:#A0ADA0;--shiki-dark:#758575DD}html pre.shiki code .sHLBJ, html code.shiki .sHLBJ{--shiki-default:#998418;--shiki-dark:#B8A965}html pre.shiki code .s_NWU, html code.shiki .s_NWU{--shiki-default:#2E8F82;--shiki-dark:#5DA994}html pre.shiki code .sqbOQ, html code.shiki .sqbOQ{--shiki-default:#2F798A;--shiki-dark:#4C9A91}",{"title":22,"searchDepth":47,"depth":47,"links":4919},[],"Testing a standard, single-package app is pretty straightforward. But a Nuxt monorepo? That can get messy very fast. When you're dealing with multiple shared packages...","2026-07-15T12:12:54.476Z","Centralised configs, isolated component tests, and reliable E2E with Vitest & Playwright, without the mocking headache.",{},"\u002Fwritings\u002Ftesting-a-nuxt-monorepo-without-losing-your-mind","10",{"title":2000,"description":4922},"testing-a-nuxt-monorepo-without-losing-your-mind","writings\u002Ftesting-a-nuxt-monorepo-without-losing-your-mind","3k7eG301RyF8xug_1OkcrpPEgs8aG_yJdS5EgJmk6-A",{"id":4,"title":5,"body":4931,"brief":12,"createdAt":1987,"description":1988,"extension":1989,"meta":6518,"navigation":50,"path":1991,"readTime":1992,"seo":6519,"slug":1994,"stem":1995,"__hash__":1996},{"type":7,"value":4932,"toc":6516},[4933,4935,4937,5029,5031,5037,5781,5791,5793,5795,5837,5839,5901,5913,6125,6132,6134,6150,6170,6174,6198,6214,6232,6512,6514],[10,4934,12],{},[10,4936,15],{},[17,4938,4939],{"className":19,"code":20,"language":21,"meta":22,"style":22},[24,4940,4941,4951,4955,4959,4979,4983,4987,4993,4997,5001,5005,5009,5013,5017,5021,5025],{"__ignoreMap":22},[27,4942,4943,4945,4947,4949],{"class":29,"line":30},[27,4944,34],{"class":33},[27,4946,38],{"class":37},[27,4948,41],{"class":37},[27,4950,44],{"class":37},[27,4952,4953],{"class":29,"line":47},[27,4954,51],{"emptyLinePlaceholder":50},[27,4956,4957],{"class":29,"line":54},[27,4958,58],{"class":57},[27,4960,4961,4963,4965,4967,4969,4971,4973,4975,4977],{"class":29,"line":61},[27,4962,64],{"class":33},[27,4964,67],{"class":37},[27,4966,70],{"class":37},[27,4968,67],{"class":37},[27,4970,75],{"class":37},[27,4972,78],{"class":37},[27,4974,81],{"class":37},[27,4976,84],{"class":37},[27,4978,87],{"class":37},[27,4980,4981],{"class":29,"line":90},[27,4982,51],{"emptyLinePlaceholder":50},[27,4984,4985],{"class":29,"line":95},[27,4986,98],{"class":57},[27,4988,4989,4991],{"class":29,"line":101},[27,4990,104],{"class":33},[27,4992,107],{"class":33},[27,4994,4995],{"class":29,"line":110},[27,4996,51],{"emptyLinePlaceholder":50},[27,4998,4999],{"class":29,"line":115},[27,5000,118],{"class":33},[27,5002,5003],{"class":29,"line":121},[27,5004,124],{"class":33},[27,5006,5007],{"class":29,"line":127},[27,5008,51],{"emptyLinePlaceholder":50},[27,5010,5011],{"class":29,"line":132},[27,5012,135],{"class":33},[27,5014,5015],{"class":29,"line":138},[27,5016,141],{"class":33},[27,5018,5019],{"class":29,"line":144},[27,5020,51],{"emptyLinePlaceholder":50},[27,5022,5023],{"class":29,"line":149},[27,5024,152],{"class":33},[27,5026,5027],{"class":29,"line":155},[27,5028,158],{"class":33},[10,5030,161],{},[10,5032,164,5033,168,5035,171],{},[24,5034,167],{},[24,5036,167],{},[17,5038,5039],{"className":174,"code":175,"language":176,"meta":22,"style":22},[24,5040,5041,5061,5073,5083,5093,5103,5107,5111,5125,5139,5153,5157,5165,5169,5177,5195,5213,5217,5267,5275,5293,5297,5347,5355,5373,5377,5427,5435,5461,5469,5477,5481,5491,5497,5509,5519,5529,5541,5553,5565,5577,5589,5593,5597,5607,5617,5629,5641,5653,5665,5683,5695,5699,5703,5711,5723,5735,5747,5759,5769,5773],{"__ignoreMap":22},[27,5042,5043,5045,5047,5049,5051,5053,5055,5057,5059],{"class":29,"line":30},[27,5044,184],{"class":183},[27,5046,188],{"class":187},[27,5048,192],{"class":191},[27,5050,195],{"class":183},[27,5052,199],{"class":198},[27,5054,202],{"class":37},[27,5056,199],{"class":198},[27,5058,207],{"class":191},[27,5060,210],{"class":183},[27,5062,5063,5065,5067,5069,5071],{"class":29,"line":47},[27,5064,216],{"class":215},[27,5066,219],{"class":191},[27,5068,222],{"class":183},[27,5070,225],{"class":33},[27,5072,228],{"class":183},[27,5074,5075,5077,5079,5081],{"class":29,"line":54},[27,5076,234],{"class":233},[27,5078,237],{"class":183},[27,5080,240],{"class":198},[27,5082,243],{"class":183},[27,5084,5085,5087,5089,5091],{"class":29,"line":61},[27,5086,248],{"class":233},[27,5088,237],{"class":183},[27,5090,240],{"class":198},[27,5092,243],{"class":183},[27,5094,5095,5097,5099,5101],{"class":29,"line":90},[27,5096,259],{"class":233},[27,5098,237],{"class":183},[27,5100,240],{"class":198},[27,5102,243],{"class":183},[27,5104,5105],{"class":29,"line":95},[27,5106,270],{"class":183},[27,5108,5109],{"class":29,"line":101},[27,5110,51],{"emptyLinePlaceholder":50},[27,5112,5113,5115,5117,5119,5121,5123],{"class":29,"line":110},[27,5114,216],{"class":215},[27,5116,281],{"class":33},[27,5118,222],{"class":183},[27,5120,286],{"class":183},[27,5122,289],{"class":183},[27,5124,292],{"class":183},[27,5126,5127,5129,5131,5133,5135,5137],{"class":29,"line":115},[27,5128,297],{"class":191},[27,5130,300],{"class":183},[27,5132,303],{"class":33},[27,5134,306],{"class":183},[27,5136,219],{"class":191},[27,5138,311],{"class":183},[27,5140,5141,5143,5145,5147,5149,5151],{"class":29,"line":121},[27,5142,316],{"class":33},[27,5144,306],{"class":183},[27,5146,199],{"class":198},[27,5148,323],{"class":37},[27,5150,199],{"class":198},[27,5152,311],{"class":183},[27,5154,5155],{"class":29,"line":127},[27,5156,332],{"class":183},[27,5158,5159,5161,5163],{"class":29,"line":132},[27,5160,337],{"class":183},[27,5162,188],{"class":187},[27,5164,210],{"class":183},[27,5166,5167],{"class":29,"line":138},[27,5168,51],{"emptyLinePlaceholder":50},[27,5170,5171,5173,5175],{"class":29,"line":144},[27,5172,184],{"class":183},[27,5174,352],{"class":187},[27,5176,210],{"class":183},[27,5178,5179,5181,5183,5185,5187,5189,5191,5193],{"class":29,"line":149},[27,5180,359],{"class":183},[27,5182,219],{"class":187},[27,5184,364],{"class":191},[27,5186,195],{"class":183},[27,5188,199],{"class":198},[27,5190,281],{"class":37},[27,5192,199],{"class":198},[27,5194,210],{"class":183},[27,5196,5197,5199,5201,5203,5205,5207,5209,5211],{"class":29,"line":155},[27,5198,379],{"class":183},[27,5200,382],{"class":187},[27,5202,385],{"class":191},[27,5204,195],{"class":183},[27,5206,199],{"class":198},[27,5208,392],{"class":37},[27,5210,199],{"class":198},[27,5212,210],{"class":183},[27,5214,5215],{"class":29,"line":399},[27,5216,403],{"class":402},[27,5218,5219,5221,5223,5225,5227,5229,5231,5233,5235,5237,5239,5241,5243,5245,5247,5249,5251,5253,5255,5257,5259,5261,5263,5265],{"class":29,"line":406},[27,5220,409],{"class":183},[27,5222,412],{"class":187},[27,5224,415],{"class":191},[27,5226,195],{"class":183},[27,5228,199],{"class":198},[27,5230,422],{"class":37},[27,5232,199],{"class":198},[27,5234,427],{"class":191},[27,5236,195],{"class":183},[27,5238,199],{"class":198},[27,5240,392],{"class":37},[27,5242,199],{"class":198},[27,5244,438],{"class":191},[27,5246,195],{"class":183},[27,5248,199],{"class":198},[27,5250,445],{"class":37},[27,5252,199],{"class":198},[27,5254,450],{"class":191},[27,5256,195],{"class":183},[27,5258,199],{"class":198},[27,5260,457],{"class":37},[27,5262,199],{"class":198},[27,5264,462],{"class":191},[27,5266,465],{"class":183},[27,5268,5269,5271,5273],{"class":29,"line":468},[27,5270,471],{"class":183},[27,5272,382],{"class":187},[27,5274,210],{"class":183},[27,5276,5277,5279,5281,5283,5285,5287,5289,5291],{"class":29,"line":478},[27,5278,379],{"class":183},[27,5280,382],{"class":187},[27,5282,385],{"class":191},[27,5284,195],{"class":183},[27,5286,199],{"class":198},[27,5288,491],{"class":37},[27,5290,199],{"class":198},[27,5292,210],{"class":183},[27,5294,5295],{"class":29,"line":498},[27,5296,501],{"class":402},[27,5298,5299,5301,5303,5305,5307,5309,5311,5313,5315,5317,5319,5321,5323,5325,5327,5329,5331,5333,5335,5337,5339,5341,5343,5345],{"class":29,"line":504},[27,5300,409],{"class":183},[27,5302,412],{"class":187},[27,5304,415],{"class":191},[27,5306,195],{"class":183},[27,5308,199],{"class":198},[27,5310,517],{"class":37},[27,5312,199],{"class":198},[27,5314,427],{"class":191},[27,5316,195],{"class":183},[27,5318,199],{"class":198},[27,5320,491],{"class":37},[27,5322,199],{"class":198},[27,5324,438],{"class":191},[27,5326,195],{"class":183},[27,5328,199],{"class":198},[27,5330,491],{"class":37},[27,5332,199],{"class":198},[27,5334,450],{"class":191},[27,5336,195],{"class":183},[27,5338,199],{"class":198},[27,5340,548],{"class":37},[27,5342,199],{"class":198},[27,5344,462],{"class":191},[27,5346,465],{"class":183},[27,5348,5349,5351,5353],{"class":29,"line":557},[27,5350,471],{"class":183},[27,5352,382],{"class":187},[27,5354,210],{"class":183},[27,5356,5357,5359,5361,5363,5365,5367,5369,5371],{"class":29,"line":566},[27,5358,379],{"class":183},[27,5360,382],{"class":187},[27,5362,385],{"class":191},[27,5364,195],{"class":183},[27,5366,199],{"class":198},[27,5368,579],{"class":37},[27,5370,199],{"class":198},[27,5372,210],{"class":183},[27,5374,5375],{"class":29,"line":586},[27,5376,589],{"class":402},[27,5378,5379,5381,5383,5385,5387,5389,5391,5393,5395,5397,5399,5401,5403,5405,5407,5409,5411,5413,5415,5417,5419,5421,5423,5425],{"class":29,"line":592},[27,5380,409],{"class":183},[27,5382,412],{"class":187},[27,5384,415],{"class":191},[27,5386,195],{"class":183},[27,5388,199],{"class":198},[27,5390,605],{"class":37},[27,5392,199],{"class":198},[27,5394,427],{"class":191},[27,5396,195],{"class":183},[27,5398,199],{"class":198},[27,5400,579],{"class":37},[27,5402,199],{"class":198},[27,5404,438],{"class":191},[27,5406,195],{"class":183},[27,5408,199],{"class":198},[27,5410,579],{"class":37},[27,5412,199],{"class":198},[27,5414,450],{"class":191},[27,5416,195],{"class":183},[27,5418,199],{"class":198},[27,5420,636],{"class":37},[27,5422,199],{"class":198},[27,5424,462],{"class":191},[27,5426,465],{"class":183},[27,5428,5429,5431,5433],{"class":29,"line":645},[27,5430,471],{"class":183},[27,5432,382],{"class":187},[27,5434,210],{"class":183},[27,5436,5437,5439,5441,5443,5445,5447,5449,5451,5453,5455,5457,5459],{"class":29,"line":654},[27,5438,379],{"class":183},[27,5440,659],{"class":187},[27,5442,438],{"class":191},[27,5444,195],{"class":183},[27,5446,199],{"class":198},[27,5448,668],{"class":37},[27,5450,199],{"class":198},[27,5452,673],{"class":183},[27,5454,676],{"class":402},[27,5456,337],{"class":183},[27,5458,659],{"class":187},[27,5460,210],{"class":183},[27,5462,5463,5465,5467],{"class":29,"line":685},[27,5464,688],{"class":183},[27,5466,219],{"class":187},[27,5468,210],{"class":183},[27,5470,5471,5473,5475],{"class":29,"line":695},[27,5472,337],{"class":183},[27,5474,352],{"class":187},[27,5476,210],{"class":183},[27,5478,5479],{"class":29,"line":704},[27,5480,51],{"emptyLinePlaceholder":50},[27,5482,5483,5485,5487,5489],{"class":29,"line":709},[27,5484,184],{"class":183},[27,5486,714],{"class":187},[27,5488,717],{"class":191},[27,5490,210],{"class":183},[27,5492,5493,5495],{"class":29,"line":722},[27,5494,219],{"class":187},[27,5496,292],{"class":183},[27,5498,5499,5501,5503,5505,5507],{"class":29,"line":729},[27,5500,732],{"class":233},[27,5502,735],{"class":183},[27,5504,739],{"class":738},[27,5506,742],{"class":215},[27,5508,745],{"class":183},[27,5510,5511,5513,5515,5517],{"class":29,"line":748},[27,5512,751],{"class":233},[27,5514,735],{"class":183},[27,5516,757],{"class":756},[27,5518,745],{"class":183},[27,5520,5521,5523,5525,5527],{"class":29,"line":762},[27,5522,765],{"class":233},[27,5524,735],{"class":183},[27,5526,770],{"class":756},[27,5528,745],{"class":183},[27,5530,5531,5533,5535,5537,5539],{"class":29,"line":775},[27,5532,778],{"class":233},[27,5534,735],{"class":183},[27,5536,783],{"class":738},[27,5538,742],{"class":215},[27,5540,745],{"class":183},[27,5542,5543,5545,5547,5549,5551],{"class":29,"line":790},[27,5544,793],{"class":233},[27,5546,735],{"class":183},[27,5548,798],{"class":738},[27,5550,801],{"class":215},[27,5552,745],{"class":183},[27,5554,5555,5557,5559,5561,5563],{"class":29,"line":806},[27,5556,809],{"class":233},[27,5558,735],{"class":183},[27,5560,814],{"class":738},[27,5562,742],{"class":215},[27,5564,745],{"class":183},[27,5566,5567,5569,5571,5573,5575],{"class":29,"line":821},[27,5568,824],{"class":233},[27,5570,735],{"class":183},[27,5572,829],{"class":738},[27,5574,832],{"class":756},[27,5576,745],{"class":183},[27,5578,5579,5581,5583,5585,5587],{"class":29,"line":837},[27,5580,840],{"class":233},[27,5582,735],{"class":183},[27,5584,845],{"class":738},[27,5586,742],{"class":215},[27,5588,745],{"class":183},[27,5590,5591],{"class":29,"line":852},[27,5592,855],{"class":183},[27,5594,5595],{"class":29,"line":858},[27,5596,51],{"emptyLinePlaceholder":50},[27,5598,5599,5601,5603,5605],{"class":29,"line":863},[27,5600,219],{"class":187},[27,5602,868],{"class":187},[27,5604,871],{"class":187},[27,5606,292],{"class":183},[27,5608,5609,5611,5613,5615],{"class":29,"line":876},[27,5610,879],{"class":233},[27,5612,735],{"class":183},[27,5614,884],{"class":756},[27,5616,745],{"class":183},[27,5618,5619,5621,5623,5625,5627],{"class":29,"line":889},[27,5620,793],{"class":233},[27,5622,735],{"class":183},[27,5624,798],{"class":738},[27,5626,801],{"class":215},[27,5628,745],{"class":183},[27,5630,5631,5633,5635,5637,5639],{"class":29,"line":902},[27,5632,840],{"class":233},[27,5634,735],{"class":183},[27,5636,783],{"class":738},[27,5638,742],{"class":215},[27,5640,745],{"class":183},[27,5642,5643,5645,5647,5649,5651],{"class":29,"line":915},[27,5644,918],{"class":233},[27,5646,735],{"class":183},[27,5648,923],{"class":738},[27,5650,742],{"class":215},[27,5652,745],{"class":183},[27,5654,5655,5657,5659,5661,5663],{"class":29,"line":930},[27,5656,732],{"class":233},[27,5658,735],{"class":183},[27,5660,923],{"class":738},[27,5662,742],{"class":215},[27,5664,745],{"class":183},[27,5666,5667,5669,5671,5673,5675,5677,5679,5681],{"class":29,"line":943},[27,5668,946],{"class":233},[27,5670,735],{"class":183},[27,5672,951],{"class":738},[27,5674,742],{"class":215},[27,5676,956],{"class":756},[27,5678,959],{"class":183},[27,5680,962],{"class":756},[27,5682,745],{"class":183},[27,5684,5685,5687,5689,5691,5693],{"class":29,"line":967},[27,5686,970],{"class":233},[27,5688,735],{"class":183},[27,5690,783],{"class":738},[27,5692,742],{"class":215},[27,5694,745],{"class":183},[27,5696,5697],{"class":29,"line":981},[27,5698,855],{"class":183},[27,5700,5701],{"class":29,"line":986},[27,5702,51],{"emptyLinePlaceholder":50},[27,5704,5705,5707,5709],{"class":29,"line":991},[27,5706,219],{"class":187},[27,5708,996],{"class":187},[27,5710,292],{"class":183},[27,5712,5713,5715,5717,5719,5721],{"class":29,"line":1001},[27,5714,793],{"class":233},[27,5716,735],{"class":183},[27,5718,798],{"class":738},[27,5720,801],{"class":215},[27,5722,745],{"class":183},[27,5724,5725,5727,5729,5731,5733],{"class":29,"line":1014},[27,5726,732],{"class":233},[27,5728,735],{"class":183},[27,5730,923],{"class":738},[27,5732,742],{"class":215},[27,5734,745],{"class":183},[27,5736,5737,5739,5741,5743,5745],{"class":29,"line":1027},[27,5738,1030],{"class":233},[27,5740,735],{"class":183},[27,5742,959],{"class":183},[27,5744,962],{"class":756},[27,5746,745],{"class":183},[27,5748,5749,5751,5753,5755,5757],{"class":29,"line":1041},[27,5750,1044],{"class":233},[27,5752,735],{"class":183},[27,5754,959],{"class":183},[27,5756,1051],{"class":756},[27,5758,745],{"class":183},[27,5760,5761,5763,5765,5767],{"class":29,"line":1056},[27,5762,946],{"class":233},[27,5764,735],{"class":183},[27,5766,1063],{"class":756},[27,5768,745],{"class":183},[27,5770,5771],{"class":29,"line":1068},[27,5772,855],{"class":183},[27,5774,5775,5777,5779],{"class":29,"line":1073},[27,5776,337],{"class":183},[27,5778,714],{"class":187},[27,5780,210],{"class":183},[10,5782,1082,5783,1085,5785,1089,5787,1093,5789,1097],{},[24,5784,219],{},[24,5786,1088],{},[24,5788,1092],{},[24,5790,1096],{},[10,5792,1100],{},[10,5794,1103],{},[1105,5796,5797,5803,5815,5821,5829],{},[1108,5798,1110,5799,1114,5801,1118],{},[24,5800,1113],{},[24,5802,1117],{},[1108,5804,1121,5805,1124,5807,1128,5809,1132,5811,1136,5813,1140],{},[24,5806,1113],{},[24,5808,1127],{},[24,5810,1131],{},[24,5812,1135],{},[24,5814,1139],{},[1108,5816,1143,5817,1147,5819,1150],{},[24,5818,1146],{},[24,5820,1113],{},[1108,5822,1153,5823,1156,5825,1160,5827,1164],{},[24,5824,1146],{},[24,5826,1159],{},[24,5828,1163],{},[1108,5830,1167,5831,1171,5833,1175,5835,1178],{},[24,5832,1170],{},[24,5834,1174],{},[24,5836,1174],{},[10,5838,1181],{},[17,5840,5841],{"className":1184,"code":1185,"language":1186,"meta":22,"style":22},[24,5842,5843,5865,5883,5897],{"__ignoreMap":22},[27,5844,5845,5847,5849,5851,5853,5855,5857,5859,5861,5863],{"class":29,"line":30},[27,5846,1193],{"class":187},[27,5848,1196],{"class":187},[27,5850,1199],{"class":33},[27,5852,306],{"class":183},[27,5854,1204],{"class":215},[27,5856,1207],{"class":183},[27,5858,1163],{"class":191},[27,5860,1212],{"class":183},[27,5862,289],{"class":183},[27,5864,292],{"class":183},[27,5866,5867,5869,5871,5873,5875,5877,5879,5881],{"class":29,"line":47},[27,5868,1221],{"class":215},[27,5870,1174],{"class":191},[27,5872,222],{"class":183},[27,5874,1228],{"class":187},[27,5876,1231],{"class":33},[27,5878,306],{"class":183},[27,5880,1163],{"class":191},[27,5882,311],{"class":183},[27,5884,5885,5887,5889,5891,5893,5895],{"class":29,"line":54},[27,5886,297],{"class":191},[27,5888,300],{"class":183},[27,5890,303],{"class":33},[27,5892,306],{"class":183},[27,5894,1174],{"class":191},[27,5896,311],{"class":183},[27,5898,5899],{"class":29,"line":61},[27,5900,270],{"class":183},[10,5902,1258,5903,1262,5905,1265,5907,1268,5909,1271,5911,1275],{},[24,5904,1261],{},[24,5906,167],{},[24,5908,1139],{},[24,5910,219],{},[24,5912,1274],{},[17,5914,5915],{"className":174,"code":1278,"language":176,"meta":22,"style":22},[24,5916,5917,5937,5949,5959,5969,5979,5983,5987,6003,6021,6035,6041,6055,6069,6073,6077,6091,6105,6109,6117,6121],{"__ignoreMap":22},[27,5918,5919,5921,5923,5925,5927,5929,5931,5933,5935],{"class":29,"line":30},[27,5920,184],{"class":183},[27,5922,188],{"class":187},[27,5924,192],{"class":191},[27,5926,195],{"class":183},[27,5928,199],{"class":198},[27,5930,202],{"class":37},[27,5932,199],{"class":198},[27,5934,207],{"class":191},[27,5936,210],{"class":183},[27,5938,5939,5941,5943,5945,5947],{"class":29,"line":47},[27,5940,216],{"class":215},[27,5942,219],{"class":191},[27,5944,222],{"class":183},[27,5946,225],{"class":33},[27,5948,228],{"class":183},[27,5950,5951,5953,5955,5957],{"class":29,"line":54},[27,5952,234],{"class":233},[27,5954,237],{"class":183},[27,5956,240],{"class":198},[27,5958,243],{"class":183},[27,5960,5961,5963,5965,5967],{"class":29,"line":61},[27,5962,248],{"class":233},[27,5964,237],{"class":183},[27,5966,240],{"class":198},[27,5968,243],{"class":183},[27,5970,5971,5973,5975,5977],{"class":29,"line":90},[27,5972,259],{"class":233},[27,5974,237],{"class":183},[27,5976,240],{"class":198},[27,5978,243],{"class":183},[27,5980,5981],{"class":29,"line":95},[27,5982,270],{"class":183},[27,5984,5985],{"class":29,"line":101},[27,5986,51],{"emptyLinePlaceholder":50},[27,5988,5989,5991,5993,5995,5997,5999,6001],{"class":29,"line":110},[27,5990,216],{"class":215},[27,5992,281],{"class":33},[27,5994,222],{"class":183},[27,5996,1361],{"class":215},[27,5998,1364],{"class":183},[27,6000,289],{"class":183},[27,6002,292],{"class":183},[27,6004,6005,6007,6009,6011,6013,6015,6017,6019],{"class":29,"line":115},[27,6006,1373],{"class":187},[27,6008,1376],{"class":33},[27,6010,306],{"class":183},[27,6012,199],{"class":198},[27,6014,1261],{"class":37},[27,6016,199],{"class":198},[27,6018,1387],{"class":183},[27,6020,292],{"class":183},[27,6022,6023,6025,6027,6029,6031,6033],{"class":29,"line":121},[27,6024,1394],{"class":233},[27,6026,237],{"class":183},[27,6028,199],{"class":198},[27,6030,1139],{"class":37},[27,6032,199],{"class":198},[27,6034,243],{"class":183},[27,6036,6037,6039],{"class":29,"line":127},[27,6038,1409],{"class":233},[27,6040,1412],{"class":183},[27,6042,6043,6045,6047,6049,6051,6053],{"class":29,"line":132},[27,6044,1417],{"class":233},[27,6046,237],{"class":183},[27,6048,219],{"class":191},[27,6050,300],{"class":183},[27,6052,392],{"class":191},[27,6054,243],{"class":183},[27,6056,6057,6059,6061,6063,6065,6067],{"class":29,"line":138},[27,6058,1432],{"class":233},[27,6060,237],{"class":183},[27,6062,219],{"class":191},[27,6064,300],{"class":183},[27,6066,491],{"class":191},[27,6068,243],{"class":183},[27,6070,6071],{"class":29,"line":144},[27,6072,1447],{"class":183},[27,6074,6075],{"class":29,"line":149},[27,6076,1452],{"class":183},[27,6078,6079,6081,6083,6085,6087,6089],{"class":29,"line":155},[27,6080,297],{"class":191},[27,6082,300],{"class":183},[27,6084,303],{"class":33},[27,6086,306],{"class":183},[27,6088,219],{"class":191},[27,6090,311],{"class":183},[27,6092,6093,6095,6097,6099,6101,6103],{"class":29,"line":399},[27,6094,316],{"class":33},[27,6096,306],{"class":183},[27,6098,199],{"class":198},[27,6100,323],{"class":37},[27,6102,199],{"class":198},[27,6104,311],{"class":183},[27,6106,6107],{"class":29,"line":406},[27,6108,332],{"class":183},[27,6110,6111,6113,6115],{"class":29,"line":468},[27,6112,337],{"class":183},[27,6114,188],{"class":187},[27,6116,210],{"class":183},[27,6118,6119],{"class":29,"line":478},[27,6120,51],{"emptyLinePlaceholder":50},[27,6122,6123],{"class":29,"line":498},[27,6124,1501],{"class":402},[10,6126,1504,6127,1508,6129,1516],{},[24,6128,1507],{},[1510,6130,1515],{"href":1512,"rel":6131},[1514],[10,6133,1519],{},[1105,6135,6136,6141,6145],{},[1108,6137,1524,6138,1530],{},[1510,6139,1529],{"href":1527,"rel":6140},[1514],[1108,6142,1533,6143,1537],{},[24,6144,1536],{},[1108,6146,1540,6147,300],{},[1510,6148,1545],{"href":1543,"rel":6149},[1514],[17,6151,6152],{"className":19,"code":1548,"language":21,"meta":22,"style":22},[24,6153,6154,6162],{"__ignoreMap":22},[27,6155,6156,6158,6160],{"class":29,"line":30},[27,6157,1555],{"class":191},[27,6159,195],{"class":183},[27,6161,1560],{"class":37},[27,6163,6164,6166,6168],{"class":29,"line":47},[27,6165,1565],{"class":191},[27,6167,195],{"class":183},[27,6169,1570],{"class":37},[10,6171,1573,6172,1576],{},[24,6173,1507],{},[17,6175,6176],{"className":19,"code":1579,"language":21,"meta":22,"style":22},[24,6177,6178,6182],{"__ignoreMap":22},[27,6179,6180],{"class":29,"line":30},[27,6181,1586],{"class":57},[27,6183,6184,6186,6188,6190,6192,6194,6196],{"class":29,"line":47},[27,6185,1591],{"class":33},[27,6187,1594],{"class":37},[27,6189,1597],{"class":37},[27,6191,1600],{"class":37},[27,6193,1603],{"class":37},[27,6195,1606],{"class":37},[27,6197,1609],{"class":37},[10,6199,1612,6200,1616,6202,1620,6204,1623,6206,1626,6208,1629,6210,1632,6212,1636],{},[24,6201,1615],{},[24,6203,1619],{},[24,6205,1515],{},[24,6207,1507],{},[24,6209,1515],{},[24,6211,1615],{},[24,6213,1635],{},[10,6215,1639,6216,1643,6218,1647,6220,1651,6222,1655,6224,1659,6226,1662,6228,1665,6230,1668],{},[24,6217,1642],{},[24,6219,1646],{},[24,6221,1650],{},[24,6223,1654],{},[24,6225,1658],{},[24,6227,1635],{},[24,6229,1654],{},[24,6231,1642],{},[17,6233,6234],{"className":1184,"code":1671,"language":1186,"meta":22,"style":22},[24,6235,6236,6252,6268,6272,6294,6312,6316,6334,6350,6364,6382,6386,6390,6400,6414,6428,6442,6468,6472,6476,6508],{"__ignoreMap":22},[27,6237,6238,6240,6242,6244,6246,6248,6250],{"class":29,"line":30},[27,6239,1678],{"class":187},[27,6241,1681],{"class":191},[27,6243,1684],{"class":187},[27,6245,1687],{"class":198},[27,6247,1619],{"class":37},[27,6249,199],{"class":198},[27,6251,745],{"class":183},[27,6253,6254,6256,6258,6260,6262,6264,6266],{"class":29,"line":47},[27,6255,1678],{"class":187},[27,6257,1700],{"class":191},[27,6259,1684],{"class":187},[27,6261,1687],{"class":198},[27,6263,1507],{"class":37},[27,6265,199],{"class":198},[27,6267,745],{"class":183},[27,6269,6270],{"class":29,"line":54},[27,6271,51],{"emptyLinePlaceholder":50},[27,6273,6274,6276,6278,6280,6282,6284,6286,6288,6290,6292],{"class":29,"line":61},[27,6275,1193],{"class":187},[27,6277,1196],{"class":187},[27,6279,1199],{"class":33},[27,6281,306],{"class":183},[27,6283,1204],{"class":215},[27,6285,1207],{"class":183},[27,6287,1163],{"class":191},[27,6289,1212],{"class":183},[27,6291,289],{"class":183},[27,6293,292],{"class":183},[27,6295,6296,6298,6300,6302,6304,6306,6308,6310],{"class":29,"line":90},[27,6297,1221],{"class":215},[27,6299,1174],{"class":191},[27,6301,222],{"class":183},[27,6303,1228],{"class":187},[27,6305,1231],{"class":33},[27,6307,306],{"class":183},[27,6309,1163],{"class":191},[27,6311,311],{"class":183},[27,6313,6314],{"class":29,"line":95},[27,6315,51],{"emptyLinePlaceholder":50},[27,6317,6318,6320,6322,6324,6326,6328,6330,6332],{"class":29,"line":101},[27,6319,1221],{"class":215},[27,6321,1765],{"class":191},[27,6323,222],{"class":183},[27,6325,1770],{"class":215},[27,6327,1515],{"class":33},[27,6329,306],{"class":183},[27,6331,1615],{"class":191},[27,6333,311],{"class":183},[27,6335,6336,6338,6340,6342,6344,6346,6348],{"class":29,"line":110},[27,6337,1221],{"class":215},[27,6339,1785],{"class":191},[27,6341,222],{"class":183},[27,6343,1790],{"class":191},[27,6345,300],{"class":183},[27,6347,1795],{"class":33},[27,6349,228],{"class":183},[27,6351,6352,6354,6356,6358,6360,6362],{"class":29,"line":115},[27,6353,1802],{"class":233},[27,6355,237],{"class":183},[27,6357,199],{"class":198},[27,6359,1113],{"class":37},[27,6361,199],{"class":198},[27,6363,243],{"class":183},[27,6365,6366,6368,6370,6372,6374,6376,6378,6380],{"class":29,"line":121},[27,6367,1817],{"class":233},[27,6369,237],{"class":183},[27,6371,1822],{"class":191},[27,6373,300],{"class":183},[27,6375,1827],{"class":191},[27,6377,300],{"class":183},[27,6379,1565],{"class":191},[27,6381,243],{"class":183},[27,6383,6384],{"class":29,"line":127},[27,6385,1452],{"class":183},[27,6387,6388],{"class":29,"line":132},[27,6389,51],{"emptyLinePlaceholder":50},[27,6391,6392,6394,6396,6398],{"class":29,"line":138},[27,6393,1221],{"class":215},[27,6395,1642],{"class":191},[27,6397,222],{"class":183},[27,6399,292],{"class":183},[27,6401,6402,6404,6406,6408,6410,6412],{"class":29,"line":144},[27,6403,1856],{"class":233},[27,6405,237],{"class":183},[27,6407,199],{"class":198},[27,6409,1863],{"class":37},[27,6411,199],{"class":198},[27,6413,243],{"class":183},[27,6415,6416,6418,6420,6422,6424,6426],{"class":29,"line":149},[27,6417,1872],{"class":233},[27,6419,237],{"class":183},[27,6421,1174],{"class":191},[27,6423,300],{"class":183},[27,6425,491],{"class":191},[27,6427,243],{"class":183},[27,6429,6430,6432,6434,6436,6438,6440],{"class":29,"line":155},[27,6431,1887],{"class":233},[27,6433,237],{"class":183},[27,6435,199],{"class":198},[27,6437,1894],{"class":37},[27,6439,199],{"class":198},[27,6441,243],{"class":183},[27,6443,6444,6446,6448,6450,6452,6454,6456,6458,6460,6462,6464,6466],{"class":29,"line":399},[27,6445,1903],{"class":233},[27,6447,237],{"class":183},[27,6449,1908],{"class":198},[27,6451,1911],{"class":37},[27,6453,1914],{"class":187},[27,6455,1174],{"class":37},[27,6457,300],{"class":183},[27,6459,392],{"class":37},[27,6461,1923],{"class":187},[27,6463,1926],{"class":37},[27,6465,1908],{"class":198},[27,6467,243],{"class":183},[27,6469,6470],{"class":29,"line":406},[27,6471,1935],{"class":183},[27,6473,6474],{"class":29,"line":468},[27,6475,51],{"emptyLinePlaceholder":50},[27,6477,6478,6480,6482,6484,6486,6488,6490,6492,6494,6496,6498,6500,6502,6504,6506],{"class":29,"line":478},[27,6479,1373],{"class":187},[27,6481,1946],{"class":191},[27,6483,300],{"class":183},[27,6485,1951],{"class":191},[27,6487,300],{"class":183},[27,6489,1654],{"class":33},[27,6491,306],{"class":183},[27,6493,1822],{"class":191},[27,6495,300],{"class":183},[27,6497,1827],{"class":191},[27,6499,300],{"class":183},[27,6501,1555],{"class":191},[27,6503,1387],{"class":183},[27,6505,1972],{"class":191},[27,6507,311],{"class":183},[27,6509,6510],{"class":29,"line":498},[27,6511,270],{"class":183},[10,6513,1981],{},[714,6515,1984],{},{"title":22,"searchDepth":47,"depth":47,"links":6517},[],{},{"title":5,"description":1988},{"id":6521,"title":6522,"body":6523,"brief":8371,"createdAt":8372,"description":6527,"extension":1989,"meta":8373,"navigation":50,"path":8374,"readTime":8375,"seo":8376,"slug":8377,"stem":8378,"__hash__":8379},"writings\u002Fwritings\u002Fbuilding-a-simple-rating-component-with-vue-3.md","Building a simple rating component with Vue 3",{"type":7,"value":6524,"toc":8369},[6525,6528,6531,6540,6722,6725,6731,6734,6742,6774,6787,6819,6829,7073,7082,7236,7239,7637,7649,7719,7722,7727,7737,8199,8202,8357,8366],[10,6526,6527],{},"A rating component which is sometimes referred to as a star rating component, is a user-facing element that allows users to provide feedback or rate something, typically on a scale, and just to display the rating of something sometimes. It's used in many applications and websites to collect user opinions, feedback, or reviews by assigning a certain number of stars or other symbols to represent a rating.",[10,6529,6530],{},"In this post, we'll be building a basic and simple rating reusable rating component using Vue 3.",[10,6532,6533,6534,6539],{},"We'll begin by creating a new Single File Component (SFC) named \"Rate.vue\" within our current Vue 3 project. If you're exploring, you can also use Vue's ",[1510,6535,6538],{"href":6536,"rel":6537},"https:\u002F\u002Fplay.vuejs.org\u002F",[1514],"playground",". Inside the Rate component, we'll include the necessary HTML elements.",[17,6541,6543],{"className":174,"code":6542,"language":176,"meta":22,"style":22},"\u003Ctemplate>\n    \u003Cdiv class=\"wrapper\">\n        \u003Cspan class=\"star\">★\u003C\u002Fspan>\n        \u003Cspan class=\"star\">★\u003C\u002Fspan>\n        \u003Cspan class=\"star\">★\u003C\u002Fspan>\n        \u003Cspan class=\"star\">★\u003C\u002Fspan>\n        \u003Cspan class=\"star\">★\u003C\u002Fspan>\n    \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n",[24,6544,6545,6553,6574,6602,6628,6654,6680,6706,6714],{"__ignoreMap":22},[27,6546,6547,6549,6551],{"class":29,"line":30},[27,6548,184],{"class":183},[27,6550,352],{"class":187},[27,6552,210],{"class":183},[27,6554,6555,6557,6560,6563,6565,6567,6570,6572],{"class":29,"line":47},[27,6556,359],{"class":183},[27,6558,6559],{"class":187},"div",[27,6561,6562],{"class":191}," class",[27,6564,195],{"class":183},[27,6566,199],{"class":198},[27,6568,6569],{"class":37},"wrapper",[27,6571,199],{"class":198},[27,6573,210],{"class":183},[27,6575,6576,6578,6580,6582,6584,6586,6589,6591,6593,6596,6598,6600],{"class":29,"line":54},[27,6577,379],{"class":183},[27,6579,27],{"class":187},[27,6581,6562],{"class":191},[27,6583,195],{"class":183},[27,6585,199],{"class":198},[27,6587,6588],{"class":37},"star",[27,6590,199],{"class":198},[27,6592,673],{"class":183},[27,6594,6595],{"class":402},"★",[27,6597,337],{"class":183},[27,6599,27],{"class":187},[27,6601,210],{"class":183},[27,6603,6604,6606,6608,6610,6612,6614,6616,6618,6620,6622,6624,6626],{"class":29,"line":61},[27,6605,379],{"class":183},[27,6607,27],{"class":187},[27,6609,6562],{"class":191},[27,6611,195],{"class":183},[27,6613,199],{"class":198},[27,6615,6588],{"class":37},[27,6617,199],{"class":198},[27,6619,673],{"class":183},[27,6621,6595],{"class":402},[27,6623,337],{"class":183},[27,6625,27],{"class":187},[27,6627,210],{"class":183},[27,6629,6630,6632,6634,6636,6638,6640,6642,6644,6646,6648,6650,6652],{"class":29,"line":90},[27,6631,379],{"class":183},[27,6633,27],{"class":187},[27,6635,6562],{"class":191},[27,6637,195],{"class":183},[27,6639,199],{"class":198},[27,6641,6588],{"class":37},[27,6643,199],{"class":198},[27,6645,673],{"class":183},[27,6647,6595],{"class":402},[27,6649,337],{"class":183},[27,6651,27],{"class":187},[27,6653,210],{"class":183},[27,6655,6656,6658,6660,6662,6664,6666,6668,6670,6672,6674,6676,6678],{"class":29,"line":95},[27,6657,379],{"class":183},[27,6659,27],{"class":187},[27,6661,6562],{"class":191},[27,6663,195],{"class":183},[27,6665,199],{"class":198},[27,6667,6588],{"class":37},[27,6669,199],{"class":198},[27,6671,673],{"class":183},[27,6673,6595],{"class":402},[27,6675,337],{"class":183},[27,6677,27],{"class":187},[27,6679,210],{"class":183},[27,6681,6682,6684,6686,6688,6690,6692,6694,6696,6698,6700,6702,6704],{"class":29,"line":101},[27,6683,379],{"class":183},[27,6685,27],{"class":187},[27,6687,6562],{"class":191},[27,6689,195],{"class":183},[27,6691,199],{"class":198},[27,6693,6588],{"class":37},[27,6695,199],{"class":198},[27,6697,673],{"class":183},[27,6699,6595],{"class":402},[27,6701,337],{"class":183},[27,6703,27],{"class":187},[27,6705,210],{"class":183},[27,6707,6708,6710,6712],{"class":29,"line":110},[27,6709,688],{"class":183},[27,6711,6559],{"class":187},[27,6713,210],{"class":183},[27,6715,6716,6718,6720],{"class":29,"line":115},[27,6717,337],{"class":183},[27,6719,352],{"class":187},[27,6721,210],{"class":183},[10,6723,6724],{},"The above code block will render the following UI:",[10,6726,6727],{},[6728,6729],"img",{"alt":22,"src":6730},"https:\u002F\u002Fcdn.hashnode.com\u002Fres\u002Fhashnode\u002Fimage\u002Fupload\u002Fv1697142132842\u002Fb55975cd-b4ca-46fa-9c52-0ac4fca86f83.png",[10,6732,6733],{},"Next, let's introduce some JavaScript code to enhance the interactivity of our user interface. This involves specifying props, emits, and a function to update\u002Femit ratings as needed.",[10,6735,6736,6737,300],{},"We start by declaring props using pure type annotations. Props can be defined using ",[1510,6738,6741],{"href":6739,"rel":6740},"https:\u002F\u002Fvuejs.org\u002Fguide\u002Fcomponents\u002Fprops.html#props-declaration",[1514],"defineProps",[17,6743,6745],{"className":174,"code":6744,"language":176,"meta":22,"style":22},"import { defineProps } from \"vue\"; const props = defineProps\u003C{ value: number; totalRating: number; }>();\n",[24,6746,6747],{"__ignoreMap":22},[27,6748,6749,6752,6754,6757,6760,6763,6766,6768,6770,6772],{"class":29,"line":30},[27,6750,6751],{"class":402},"import { defineProps } from \"vue\"; const props = defineProps",[27,6753,184],{"class":183},[27,6755,6756],{"class":402},"{ ",[27,6758,6759],{"class":187},"value:",[27,6761,6762],{"class":191}," number;",[27,6764,6765],{"class":191}," totalRating:",[27,6767,6762],{"class":191},[27,6769,2220],{"class":191},[27,6771,673],{"class":183},[27,6773,2277],{"class":402},[10,6775,6776,6777,6782,6783,6786],{},"In Vue, a component can explicitly declare the events it will emit using the ",[1510,6778,6781],{"href":6779,"rel":6780},"https:\u002F\u002Fvuejs.org\u002Fguide\u002Fcomponents\u002Fevents.html#declaring-emitted-events",[1514],"defineEmits"," macro, in our case, which is to declare an ",[24,6784,6785],{},"update-rating"," event with an argument. We'll be doing this using pure type annotations like so:",[17,6788,6790],{"className":174,"code":6789,"language":176,"meta":22,"style":22},"import { defineEmits } from \"vue\"; defineEmits\u003C(event: \"update-rating\", value: number) => void>();\n",[24,6791,6792],{"__ignoreMap":22},[27,6793,6794,6797,6799,6801,6804,6808,6811,6814,6816],{"class":29,"line":30},[27,6795,6796],{"class":402},"import { defineEmits } from \"vue\"; defineEmits",[27,6798,184],{"class":183},[27,6800,306],{"class":402},[27,6802,6803],{"class":187},"event:",[27,6805,6807],{"class":6806},"sGGdt"," \"update-rating\",",[27,6809,6810],{"class":191}," value:",[27,6812,6813],{"class":191}," number)",[27,6815,289],{"class":183},[27,6817,6818],{"class":402}," void>();\n",[10,6820,6821,6822,6824,6825,6828],{},"Now, we will use a loop to dynamically render the ",[24,6823,27],{}," element based on the ",[24,6826,6827],{},"totalRating"," prop:",[17,6830,6832],{"className":174,"code":6831,"language":176,"meta":22,"style":22},"\u003Cscript lang=\"ts\" setup>\nimport { defineProps, defineEmits } from \"vue\";\n\nconst props = defineProps\u003C{\n    value: number;\n    totalRating: number;\n}>();\n\nconst emit = defineEmits\u003C(event: \"update-rating\", value: number) => void>();\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n    \u003Cdiv class=\"wrapper\">\n        \u003Cspan v-for=\"star in totalRating\" :key=\"star\" class=\"star\">★\u003C\u002Fspan>\n    \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n",[24,6833,6834,6854,6880,6884,6897,6908,6919,6924,6928,6970,6978,6982,6990,7008,7057,7065],{"__ignoreMap":22},[27,6835,6836,6838,6840,6842,6844,6846,6848,6850,6852],{"class":29,"line":30},[27,6837,184],{"class":183},[27,6839,188],{"class":187},[27,6841,192],{"class":191},[27,6843,195],{"class":183},[27,6845,199],{"class":198},[27,6847,202],{"class":37},[27,6849,199],{"class":198},[27,6851,207],{"class":191},[27,6853,210],{"class":183},[27,6855,6856,6858,6860,6863,6865,6868,6870,6872,6874,6876,6878],{"class":29,"line":47},[27,6857,1678],{"class":187},[27,6859,2214],{"class":183},[27,6861,6862],{"class":191}," defineProps",[27,6864,1387],{"class":183},[27,6866,6867],{"class":191}," defineEmits",[27,6869,2220],{"class":183},[27,6871,1684],{"class":187},[27,6873,1687],{"class":198},[27,6875,176],{"class":37},[27,6877,199],{"class":198},[27,6879,745],{"class":183},[27,6881,6882],{"class":29,"line":54},[27,6883,51],{"emptyLinePlaceholder":50},[27,6885,6886,6888,6890,6892,6894],{"class":29,"line":61},[27,6887,216],{"class":215},[27,6889,3869],{"class":191},[27,6891,222],{"class":183},[27,6893,6862],{"class":33},[27,6895,6896],{"class":183},"\u003C{\n",[27,6898,6899,6902,6904,6906],{"class":29,"line":90},[27,6900,6901],{"class":191},"    value",[27,6903,237],{"class":183},[27,6905,3508],{"class":3340},[27,6907,745],{"class":183},[27,6909,6910,6913,6915,6917],{"class":29,"line":95},[27,6911,6912],{"class":191},"    totalRating",[27,6914,237],{"class":183},[27,6916,3508],{"class":3340},[27,6918,745],{"class":183},[27,6920,6921],{"class":29,"line":101},[27,6922,6923],{"class":183},"}>();\n",[27,6925,6926],{"class":29,"line":110},[27,6927,51],{"emptyLinePlaceholder":50},[27,6929,6930,6932,6935,6937,6939,6942,6944,6946,6948,6950,6952,6954,6957,6959,6961,6963,6965,6968],{"class":29,"line":115},[27,6931,216],{"class":215},[27,6933,6934],{"class":191},"emit",[27,6936,222],{"class":183},[27,6938,6867],{"class":33},[27,6940,6941],{"class":183},"\u003C(",[27,6943,1163],{"class":191},[27,6945,237],{"class":183},[27,6947,199],{"class":198},[27,6949,6785],{"class":37},[27,6951,199],{"class":198},[27,6953,1387],{"class":183},[27,6955,6956],{"class":191}," value",[27,6958,237],{"class":183},[27,6960,3508],{"class":3340},[27,6962,1212],{"class":183},[27,6964,289],{"class":183},[27,6966,6967],{"class":3340}," void",[27,6969,3349],{"class":183},[27,6971,6972,6974,6976],{"class":29,"line":121},[27,6973,337],{"class":183},[27,6975,188],{"class":187},[27,6977,210],{"class":183},[27,6979,6980],{"class":29,"line":127},[27,6981,51],{"emptyLinePlaceholder":50},[27,6983,6984,6986,6988],{"class":29,"line":132},[27,6985,184],{"class":183},[27,6987,352],{"class":187},[27,6989,210],{"class":183},[27,6991,6992,6994,6996,6998,7000,7002,7004,7006],{"class":29,"line":138},[27,6993,359],{"class":183},[27,6995,6559],{"class":187},[27,6997,6562],{"class":191},[27,6999,195],{"class":183},[27,7001,199],{"class":198},[27,7003,6569],{"class":37},[27,7005,199],{"class":198},[27,7007,210],{"class":183},[27,7009,7010,7012,7014,7017,7019,7021,7024,7026,7029,7031,7033,7035,7037,7039,7041,7043,7045,7047,7049,7051,7053,7055],{"class":29,"line":144},[27,7011,379],{"class":183},[27,7013,27],{"class":187},[27,7015,7016],{"class":191}," v-for",[27,7018,195],{"class":183},[27,7020,199],{"class":198},[27,7022,7023],{"class":37},"star in totalRating",[27,7025,199],{"class":198},[27,7027,7028],{"class":191}," :key",[27,7030,195],{"class":183},[27,7032,199],{"class":198},[27,7034,6588],{"class":37},[27,7036,199],{"class":198},[27,7038,6562],{"class":191},[27,7040,195],{"class":183},[27,7042,199],{"class":198},[27,7044,6588],{"class":37},[27,7046,199],{"class":198},[27,7048,673],{"class":183},[27,7050,6595],{"class":402},[27,7052,337],{"class":183},[27,7054,27],{"class":187},[27,7056,210],{"class":183},[27,7058,7059,7061,7063],{"class":29,"line":149},[27,7060,688],{"class":183},[27,7062,6559],{"class":187},[27,7064,210],{"class":183},[27,7066,7067,7069,7071],{"class":29,"line":155},[27,7068,337],{"class":183},[27,7070,352],{"class":187},[27,7072,210],{"class":183},[10,7074,7075,7076,2075,7079,7081],{},"Moving forward, we can now utilize our Rating component in another component, which has the ",[24,7077,7078],{},"rating",[24,7080,6827],{}," states, which will be supplied as props to the Rating component.",[17,7083,7085],{"className":174,"code":7084,"language":176,"meta":22,"style":22},"\u003Cscript setup>\nimport Rating from \".\u002FRating.vue\";\nimport { ref } from \"vue\";\n\nconst rating = ref(3);\nconst totalRating = ref(5);\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n    \u003CRating :value=\"rating\" :total-rating=\"totalRating\" \u002F>\n\u003C\u002Ftemplate>\n",[24,7086,7087,7097,7115,7136,7140,7159,7177,7185,7189,7197,7228],{"__ignoreMap":22},[27,7088,7089,7091,7093,7095],{"class":29,"line":30},[27,7090,184],{"class":183},[27,7092,188],{"class":187},[27,7094,207],{"class":191},[27,7096,210],{"class":183},[27,7098,7099,7101,7104,7106,7108,7111,7113],{"class":29,"line":47},[27,7100,1678],{"class":187},[27,7102,7103],{"class":191}," Rating",[27,7105,1684],{"class":187},[27,7107,1687],{"class":198},[27,7109,7110],{"class":37},".\u002FRating.vue",[27,7112,199],{"class":198},[27,7114,745],{"class":183},[27,7116,7117,7119,7121,7124,7126,7128,7130,7132,7134],{"class":29,"line":54},[27,7118,1678],{"class":187},[27,7120,2214],{"class":183},[27,7122,7123],{"class":191}," ref",[27,7125,2220],{"class":183},[27,7127,1684],{"class":187},[27,7129,1687],{"class":198},[27,7131,176],{"class":37},[27,7133,199],{"class":198},[27,7135,745],{"class":183},[27,7137,7138],{"class":29,"line":61},[27,7139,51],{"emptyLinePlaceholder":50},[27,7141,7142,7145,7148,7150,7152,7154,7157],{"class":29,"line":90},[27,7143,7144],{"class":215},"const",[27,7146,7147],{"class":191}," rating",[27,7149,222],{"class":183},[27,7151,7123],{"class":33},[27,7153,306],{"class":183},[27,7155,7156],{"class":738},"3",[27,7158,311],{"class":183},[27,7160,7161,7163,7166,7168,7170,7172,7175],{"class":29,"line":95},[27,7162,7144],{"class":215},[27,7164,7165],{"class":191}," totalRating",[27,7167,222],{"class":183},[27,7169,7123],{"class":33},[27,7171,306],{"class":183},[27,7173,7174],{"class":738},"5",[27,7176,311],{"class":183},[27,7178,7179,7181,7183],{"class":29,"line":101},[27,7180,337],{"class":183},[27,7182,188],{"class":187},[27,7184,210],{"class":183},[27,7186,7187],{"class":29,"line":110},[27,7188,51],{"emptyLinePlaceholder":50},[27,7190,7191,7193,7195],{"class":29,"line":115},[27,7192,184],{"class":183},[27,7194,352],{"class":187},[27,7196,210],{"class":183},[27,7198,7199,7201,7204,7207,7209,7211,7213,7215,7218,7220,7222,7224,7226],{"class":29,"line":121},[27,7200,359],{"class":183},[27,7202,7203],{"class":187},"Rating",[27,7205,7206],{"class":191}," :value",[27,7208,195],{"class":183},[27,7210,199],{"class":198},[27,7212,7078],{"class":37},[27,7214,199],{"class":198},[27,7216,7217],{"class":191}," :total-rating",[27,7219,195],{"class":183},[27,7221,199],{"class":198},[27,7223,6827],{"class":37},[27,7225,199],{"class":198},[27,7227,465],{"class":183},[27,7229,7230,7232,7234],{"class":29,"line":127},[27,7231,337],{"class":183},[27,7233,352],{"class":187},[27,7235,210],{"class":183},[10,7237,7238],{},"Indeed, it's important to note that, at this point, there haven't been any visible changes in our user interface. The reason is that we have yet to apply styles to the \"Rating\" component. Now, it's time to add the necessary styles to enhance the appearance and presentation of the \"Rating\" component:",[17,7240,7242],{"className":174,"code":7241,"language":176,"meta":22,"style":22},"\u003Cscript lang=\"ts\" setup>\nimport { defineProps, defineEmits } from \"vue\";\n\nconst props = defineProps\u003C{\n    value: number;\n    totalRating: number;\n}>();\n\nconst emit = defineEmits\u003C(event: \"update-rating\", value: number) => void>();\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n    \u003Cdiv class=\"wrapper\">\n        \u003Cspan v-for=\"star in totalRating\" :key=\"star\" class=\"star\">★\u003C\u002Fspan>\n    \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cstyle scoped>\n.wrapper {\n    display: flex;\n    align-items: center;\n    gap: 2px;\n}\n\n.star {\n    cursor: pointer;\n    color: #d8d8d8;\n}\n\n.star:hover,\n.star:active,\n.star.filled {\n    color: orange;\n}\n\u003C\u002Fstyle>\n",[24,7243,7244,7264,7288,7292,7304,7314,7324,7328,7332,7370,7378,7382,7390,7408,7454,7462,7470,7474,7484,7492,7502,7514,7526,7530,7534,7542,7554,7567,7571,7575,7588,7601,7614,7625,7629],{"__ignoreMap":22},[27,7245,7246,7248,7250,7252,7254,7256,7258,7260,7262],{"class":29,"line":30},[27,7247,184],{"class":183},[27,7249,188],{"class":187},[27,7251,192],{"class":191},[27,7253,195],{"class":183},[27,7255,199],{"class":198},[27,7257,202],{"class":37},[27,7259,199],{"class":198},[27,7261,207],{"class":191},[27,7263,210],{"class":183},[27,7265,7266,7268,7270,7272,7274,7276,7278,7280,7282,7284,7286],{"class":29,"line":47},[27,7267,1678],{"class":187},[27,7269,2214],{"class":183},[27,7271,6862],{"class":191},[27,7273,1387],{"class":183},[27,7275,6867],{"class":191},[27,7277,2220],{"class":183},[27,7279,1684],{"class":187},[27,7281,1687],{"class":198},[27,7283,176],{"class":37},[27,7285,199],{"class":198},[27,7287,745],{"class":183},[27,7289,7290],{"class":29,"line":54},[27,7291,51],{"emptyLinePlaceholder":50},[27,7293,7294,7296,7298,7300,7302],{"class":29,"line":61},[27,7295,216],{"class":215},[27,7297,3869],{"class":191},[27,7299,222],{"class":183},[27,7301,6862],{"class":33},[27,7303,6896],{"class":183},[27,7305,7306,7308,7310,7312],{"class":29,"line":90},[27,7307,6901],{"class":191},[27,7309,237],{"class":183},[27,7311,3508],{"class":3340},[27,7313,745],{"class":183},[27,7315,7316,7318,7320,7322],{"class":29,"line":95},[27,7317,6912],{"class":191},[27,7319,237],{"class":183},[27,7321,3508],{"class":3340},[27,7323,745],{"class":183},[27,7325,7326],{"class":29,"line":101},[27,7327,6923],{"class":183},[27,7329,7330],{"class":29,"line":110},[27,7331,51],{"emptyLinePlaceholder":50},[27,7333,7334,7336,7338,7340,7342,7344,7346,7348,7350,7352,7354,7356,7358,7360,7362,7364,7366,7368],{"class":29,"line":115},[27,7335,216],{"class":215},[27,7337,6934],{"class":191},[27,7339,222],{"class":183},[27,7341,6867],{"class":33},[27,7343,6941],{"class":183},[27,7345,1163],{"class":191},[27,7347,237],{"class":183},[27,7349,199],{"class":198},[27,7351,6785],{"class":37},[27,7353,199],{"class":198},[27,7355,1387],{"class":183},[27,7357,6956],{"class":191},[27,7359,237],{"class":183},[27,7361,3508],{"class":3340},[27,7363,1212],{"class":183},[27,7365,289],{"class":183},[27,7367,6967],{"class":3340},[27,7369,3349],{"class":183},[27,7371,7372,7374,7376],{"class":29,"line":121},[27,7373,337],{"class":183},[27,7375,188],{"class":187},[27,7377,210],{"class":183},[27,7379,7380],{"class":29,"line":127},[27,7381,51],{"emptyLinePlaceholder":50},[27,7383,7384,7386,7388],{"class":29,"line":132},[27,7385,184],{"class":183},[27,7387,352],{"class":187},[27,7389,210],{"class":183},[27,7391,7392,7394,7396,7398,7400,7402,7404,7406],{"class":29,"line":138},[27,7393,359],{"class":183},[27,7395,6559],{"class":187},[27,7397,6562],{"class":191},[27,7399,195],{"class":183},[27,7401,199],{"class":198},[27,7403,6569],{"class":37},[27,7405,199],{"class":198},[27,7407,210],{"class":183},[27,7409,7410,7412,7414,7416,7418,7420,7422,7424,7426,7428,7430,7432,7434,7436,7438,7440,7442,7444,7446,7448,7450,7452],{"class":29,"line":144},[27,7411,379],{"class":183},[27,7413,27],{"class":187},[27,7415,7016],{"class":191},[27,7417,195],{"class":183},[27,7419,199],{"class":198},[27,7421,7023],{"class":37},[27,7423,199],{"class":198},[27,7425,7028],{"class":191},[27,7427,195],{"class":183},[27,7429,199],{"class":198},[27,7431,6588],{"class":37},[27,7433,199],{"class":198},[27,7435,6562],{"class":191},[27,7437,195],{"class":183},[27,7439,199],{"class":198},[27,7441,6588],{"class":37},[27,7443,199],{"class":198},[27,7445,673],{"class":183},[27,7447,6595],{"class":402},[27,7449,337],{"class":183},[27,7451,27],{"class":187},[27,7453,210],{"class":183},[27,7455,7456,7458,7460],{"class":29,"line":149},[27,7457,688],{"class":183},[27,7459,6559],{"class":187},[27,7461,210],{"class":183},[27,7463,7464,7466,7468],{"class":29,"line":155},[27,7465,337],{"class":183},[27,7467,352],{"class":187},[27,7469,210],{"class":183},[27,7471,7472],{"class":29,"line":399},[27,7473,51],{"emptyLinePlaceholder":50},[27,7475,7476,7478,7480,7482],{"class":29,"line":406},[27,7477,184],{"class":183},[27,7479,714],{"class":187},[27,7481,717],{"class":191},[27,7483,210],{"class":183},[27,7485,7486,7488,7490],{"class":29,"line":468},[27,7487,300],{"class":183},[27,7489,6569],{"class":191},[27,7491,292],{"class":183},[27,7493,7494,7496,7498,7500],{"class":29,"line":478},[27,7495,751],{"class":233},[27,7497,735],{"class":183},[27,7499,757],{"class":756},[27,7501,745],{"class":183},[27,7503,7504,7507,7509,7512],{"class":29,"line":498},[27,7505,7506],{"class":233},"    align-items",[27,7508,735],{"class":183},[27,7510,7511],{"class":756}," center",[27,7513,745],{"class":183},[27,7515,7516,7518,7520,7522,7524],{"class":29,"line":504},[27,7517,778],{"class":233},[27,7519,735],{"class":183},[27,7521,4326],{"class":738},[27,7523,742],{"class":215},[27,7525,745],{"class":183},[27,7527,7528],{"class":29,"line":557},[27,7529,855],{"class":183},[27,7531,7532],{"class":29,"line":566},[27,7533,51],{"emptyLinePlaceholder":50},[27,7535,7536,7538,7540],{"class":29,"line":586},[27,7537,300],{"class":183},[27,7539,6588],{"class":191},[27,7541,292],{"class":183},[27,7543,7544,7547,7549,7552],{"class":29,"line":592},[27,7545,7546],{"class":233},"    cursor",[27,7548,735],{"class":183},[27,7550,7551],{"class":756}," pointer",[27,7553,745],{"class":183},[27,7555,7556,7558,7560,7562,7565],{"class":29,"line":645},[27,7557,1044],{"class":233},[27,7559,735],{"class":183},[27,7561,959],{"class":183},[27,7563,7564],{"class":756},"d8d8d8",[27,7566,745],{"class":183},[27,7568,7569],{"class":29,"line":654},[27,7570,855],{"class":183},[27,7572,7573],{"class":29,"line":685},[27,7574,51],{"emptyLinePlaceholder":50},[27,7576,7577,7579,7581,7583,7586],{"class":29,"line":695},[27,7578,300],{"class":183},[27,7580,6588],{"class":191},[27,7582,735],{"class":183},[27,7584,7585],{"class":191},"hover",[27,7587,243],{"class":183},[27,7589,7590,7592,7594,7596,7599],{"class":29,"line":704},[27,7591,300],{"class":183},[27,7593,6588],{"class":191},[27,7595,735],{"class":183},[27,7597,7598],{"class":191},"active",[27,7600,243],{"class":183},[27,7602,7603,7605,7607,7609,7612],{"class":29,"line":709},[27,7604,300],{"class":183},[27,7606,6588],{"class":191},[27,7608,300],{"class":183},[27,7610,7611],{"class":191},"filled",[27,7613,292],{"class":183},[27,7615,7616,7618,7620,7623],{"class":29,"line":722},[27,7617,1044],{"class":233},[27,7619,735],{"class":183},[27,7621,7622],{"class":756}," orange",[27,7624,745],{"class":183},[27,7626,7627],{"class":29,"line":729},[27,7628,855],{"class":183},[27,7630,7631,7633,7635],{"class":29,"line":748},[27,7632,337],{"class":183},[27,7634,714],{"class":187},[27,7636,210],{"class":183},[10,7638,7639,7640,7642,7643,7645,7646,7648],{},"Currently, there may not be any noticeable changes, but if you hover over the stars, you will observe a color change to orange. Our next step is to visually indicate the current rating value by conditionally applying a class to the ",[24,7641,27],{}," element. When the current star is less than or equal to the ",[24,7644,3417],{}," prop that we previously declared, we will add a ",[24,7647,7611],{}," class to the element.",[17,7650,7653],{"className":7651,"code":7652,"language":3058,"meta":22,"style":22},"language-html shiki shiki-themes vitesse-light vitesse-black","\u003Cspan v-for=\"star in totalRating\" :key=\"star\" class=\"star\" :class=\"{ filled: star \u003C= value }\">★\u003C\u002Fspan>\n",[24,7654,7655],{"__ignoreMap":22},[27,7656,7657,7659,7661,7663,7665,7667,7669,7671,7673,7675,7677,7679,7681,7683,7685,7687,7689,7691,7694,7696,7698,7701,7704,7707,7709,7711,7713,7715,7717],{"class":29,"line":30},[27,7658,184],{"class":183},[27,7660,27],{"class":187},[27,7662,7016],{"class":191},[27,7664,195],{"class":183},[27,7666,199],{"class":198},[27,7668,7023],{"class":37},[27,7670,199],{"class":198},[27,7672,7028],{"class":191},[27,7674,195],{"class":183},[27,7676,199],{"class":198},[27,7678,6588],{"class":37},[27,7680,199],{"class":198},[27,7682,6562],{"class":191},[27,7684,195],{"class":183},[27,7686,199],{"class":198},[27,7688,6588],{"class":37},[27,7690,199],{"class":198},[27,7692,7693],{"class":191}," :class",[27,7695,195],{"class":183},[27,7697,199],{"class":198},[27,7699,7700],{"class":37},"{ filled: star ",[27,7702,184],{"class":7703},"s7hpK",[27,7705,7706],{"class":37},"= value }",[27,7708,199],{"class":198},[27,7710,673],{"class":183},[27,7712,6595],{"class":402},[27,7714,337],{"class":183},[27,7716,27],{"class":187},[27,7718,210],{"class":183},[10,7720,7721],{},"With this modification, you can now observe the current rating value in the user interface, represented with varying colors to distinguish between filled and unfilled stars.",[10,7723,7724],{},[6728,7725],{"alt":22,"src":7726},"https:\u002F\u002Fcdn.hashnode.com\u002Fres\u002Fhashnode\u002Fimage\u002Fupload\u002Fv1697141096435\u002Fff56bece-c871-40a8-966f-d740123e2639.png",[10,7728,7729,7730,7733,7734,7736],{},"The final piece to complete our implementation is to introduce a functionality for setting new ratings. We will create a ",[24,7731,7732],{},"setRating"," function for updating the rating and emitting the new value to the parent component. This function will then be attached to the ",[24,7735,27],{}," element as a click event handler.",[17,7738,7740],{"className":174,"code":7739,"language":176,"meta":22,"style":22},"\u003Cscript lang=\"ts\" setup>\nimport { defineProps, defineEmits } from \"vue\";\n\nconst props = defineProps\u003C{\n    value: number;\n    totalRating: number;\n}>();\n\nconst emit = defineEmits\u003C(event: \"update-rating\", value: number) => void>();\n\nconst setRating = (star: number) => {\n    emit(\"update-rating\", star);\n};\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n    \u003Cdiv class=\"wrapper\">\n        \u003Cspan v-for=\"star in totalRating\" :key=\"star\" class=\"star\" :class=\"{ filled: star \u003C= value }\" @click=\"setRating(star)\">★\u003C\u002Fspan>\n    \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cstyle scoped>\n.wrapper {\n    display: flex;\n    align-items: center;\n    gap: 2px;\n}\n\n.star {\n    cursor: pointer;\n    color: #d8d8d8;\n}\n\n.star:hover,\n.star:active,\n.star.filled {\n    color: orange;\n}\n\u003C\u002Fstyle>\n",[24,7741,7742,7762,7786,7790,7802,7812,7822,7826,7830,7868,7872,7894,7914,7918,7926,7930,7938,7956,8025,8033,8041,8045,8055,8063,8073,8083,8095,8099,8103,8111,8121,8133,8137,8141,8153,8165,8177,8187,8191],{"__ignoreMap":22},[27,7743,7744,7746,7748,7750,7752,7754,7756,7758,7760],{"class":29,"line":30},[27,7745,184],{"class":183},[27,7747,188],{"class":187},[27,7749,192],{"class":191},[27,7751,195],{"class":183},[27,7753,199],{"class":198},[27,7755,202],{"class":37},[27,7757,199],{"class":198},[27,7759,207],{"class":191},[27,7761,210],{"class":183},[27,7763,7764,7766,7768,7770,7772,7774,7776,7778,7780,7782,7784],{"class":29,"line":47},[27,7765,1678],{"class":187},[27,7767,2214],{"class":183},[27,7769,6862],{"class":191},[27,7771,1387],{"class":183},[27,7773,6867],{"class":191},[27,7775,2220],{"class":183},[27,7777,1684],{"class":187},[27,7779,1687],{"class":198},[27,7781,176],{"class":37},[27,7783,199],{"class":198},[27,7785,745],{"class":183},[27,7787,7788],{"class":29,"line":54},[27,7789,51],{"emptyLinePlaceholder":50},[27,7791,7792,7794,7796,7798,7800],{"class":29,"line":61},[27,7793,216],{"class":215},[27,7795,3869],{"class":191},[27,7797,222],{"class":183},[27,7799,6862],{"class":33},[27,7801,6896],{"class":183},[27,7803,7804,7806,7808,7810],{"class":29,"line":90},[27,7805,6901],{"class":191},[27,7807,237],{"class":183},[27,7809,3508],{"class":3340},[27,7811,745],{"class":183},[27,7813,7814,7816,7818,7820],{"class":29,"line":95},[27,7815,6912],{"class":191},[27,7817,237],{"class":183},[27,7819,3508],{"class":3340},[27,7821,745],{"class":183},[27,7823,7824],{"class":29,"line":101},[27,7825,6923],{"class":183},[27,7827,7828],{"class":29,"line":110},[27,7829,51],{"emptyLinePlaceholder":50},[27,7831,7832,7834,7836,7838,7840,7842,7844,7846,7848,7850,7852,7854,7856,7858,7860,7862,7864,7866],{"class":29,"line":115},[27,7833,216],{"class":215},[27,7835,6934],{"class":191},[27,7837,222],{"class":183},[27,7839,6867],{"class":33},[27,7841,6941],{"class":183},[27,7843,1163],{"class":191},[27,7845,237],{"class":183},[27,7847,199],{"class":198},[27,7849,6785],{"class":37},[27,7851,199],{"class":198},[27,7853,1387],{"class":183},[27,7855,6956],{"class":191},[27,7857,237],{"class":183},[27,7859,3508],{"class":3340},[27,7861,1212],{"class":183},[27,7863,289],{"class":183},[27,7865,6967],{"class":3340},[27,7867,3349],{"class":183},[27,7869,7870],{"class":29,"line":121},[27,7871,51],{"emptyLinePlaceholder":50},[27,7873,7874,7876,7878,7880,7882,7884,7886,7888,7890,7892],{"class":29,"line":127},[27,7875,216],{"class":215},[27,7877,7732],{"class":33},[27,7879,222],{"class":183},[27,7881,1207],{"class":183},[27,7883,6588],{"class":191},[27,7885,237],{"class":183},[27,7887,3508],{"class":3340},[27,7889,1212],{"class":183},[27,7891,289],{"class":183},[27,7893,292],{"class":183},[27,7895,7896,7899,7901,7903,7905,7907,7909,7912],{"class":29,"line":132},[27,7897,7898],{"class":33},"    emit",[27,7900,306],{"class":183},[27,7902,199],{"class":198},[27,7904,6785],{"class":37},[27,7906,199],{"class":198},[27,7908,1387],{"class":183},[27,7910,7911],{"class":191}," star",[27,7913,311],{"class":183},[27,7915,7916],{"class":29,"line":138},[27,7917,332],{"class":183},[27,7919,7920,7922,7924],{"class":29,"line":144},[27,7921,337],{"class":183},[27,7923,188],{"class":187},[27,7925,210],{"class":183},[27,7927,7928],{"class":29,"line":149},[27,7929,51],{"emptyLinePlaceholder":50},[27,7931,7932,7934,7936],{"class":29,"line":155},[27,7933,184],{"class":183},[27,7935,352],{"class":187},[27,7937,210],{"class":183},[27,7939,7940,7942,7944,7946,7948,7950,7952,7954],{"class":29,"line":399},[27,7941,359],{"class":183},[27,7943,6559],{"class":187},[27,7945,6562],{"class":191},[27,7947,195],{"class":183},[27,7949,199],{"class":198},[27,7951,6569],{"class":37},[27,7953,199],{"class":198},[27,7955,210],{"class":183},[27,7957,7958,7960,7962,7964,7966,7968,7970,7972,7974,7976,7978,7980,7982,7984,7986,7988,7990,7992,7994,7996,7998,8001,8003,8006,8008,8010,8013,8015,8017,8019,8021,8023],{"class":29,"line":406},[27,7959,379],{"class":183},[27,7961,27],{"class":187},[27,7963,7016],{"class":191},[27,7965,195],{"class":183},[27,7967,199],{"class":198},[27,7969,7023],{"class":37},[27,7971,199],{"class":198},[27,7973,7028],{"class":191},[27,7975,195],{"class":183},[27,7977,199],{"class":198},[27,7979,6588],{"class":37},[27,7981,199],{"class":198},[27,7983,6562],{"class":191},[27,7985,195],{"class":183},[27,7987,199],{"class":198},[27,7989,6588],{"class":37},[27,7991,199],{"class":198},[27,7993,7693],{"class":191},[27,7995,195],{"class":183},[27,7997,199],{"class":198},[27,7999,8000],{"class":37},"{ filled: star \u003C= value }",[27,8002,199],{"class":198},[27,8004,8005],{"class":191}," @click",[27,8007,195],{"class":183},[27,8009,199],{"class":198},[27,8011,8012],{"class":37},"setRating(star)",[27,8014,199],{"class":198},[27,8016,673],{"class":183},[27,8018,6595],{"class":402},[27,8020,337],{"class":183},[27,8022,27],{"class":187},[27,8024,210],{"class":183},[27,8026,8027,8029,8031],{"class":29,"line":468},[27,8028,688],{"class":183},[27,8030,6559],{"class":187},[27,8032,210],{"class":183},[27,8034,8035,8037,8039],{"class":29,"line":478},[27,8036,337],{"class":183},[27,8038,352],{"class":187},[27,8040,210],{"class":183},[27,8042,8043],{"class":29,"line":498},[27,8044,51],{"emptyLinePlaceholder":50},[27,8046,8047,8049,8051,8053],{"class":29,"line":504},[27,8048,184],{"class":183},[27,8050,714],{"class":187},[27,8052,717],{"class":191},[27,8054,210],{"class":183},[27,8056,8057,8059,8061],{"class":29,"line":557},[27,8058,300],{"class":183},[27,8060,6569],{"class":191},[27,8062,292],{"class":183},[27,8064,8065,8067,8069,8071],{"class":29,"line":566},[27,8066,751],{"class":233},[27,8068,735],{"class":183},[27,8070,757],{"class":756},[27,8072,745],{"class":183},[27,8074,8075,8077,8079,8081],{"class":29,"line":586},[27,8076,7506],{"class":233},[27,8078,735],{"class":183},[27,8080,7511],{"class":756},[27,8082,745],{"class":183},[27,8084,8085,8087,8089,8091,8093],{"class":29,"line":592},[27,8086,778],{"class":233},[27,8088,735],{"class":183},[27,8090,4326],{"class":738},[27,8092,742],{"class":215},[27,8094,745],{"class":183},[27,8096,8097],{"class":29,"line":645},[27,8098,855],{"class":183},[27,8100,8101],{"class":29,"line":654},[27,8102,51],{"emptyLinePlaceholder":50},[27,8104,8105,8107,8109],{"class":29,"line":685},[27,8106,300],{"class":183},[27,8108,6588],{"class":191},[27,8110,292],{"class":183},[27,8112,8113,8115,8117,8119],{"class":29,"line":695},[27,8114,7546],{"class":233},[27,8116,735],{"class":183},[27,8118,7551],{"class":756},[27,8120,745],{"class":183},[27,8122,8123,8125,8127,8129,8131],{"class":29,"line":704},[27,8124,1044],{"class":233},[27,8126,735],{"class":183},[27,8128,959],{"class":183},[27,8130,7564],{"class":756},[27,8132,745],{"class":183},[27,8134,8135],{"class":29,"line":709},[27,8136,855],{"class":183},[27,8138,8139],{"class":29,"line":722},[27,8140,51],{"emptyLinePlaceholder":50},[27,8142,8143,8145,8147,8149,8151],{"class":29,"line":729},[27,8144,300],{"class":183},[27,8146,6588],{"class":191},[27,8148,735],{"class":183},[27,8150,7585],{"class":191},[27,8152,243],{"class":183},[27,8154,8155,8157,8159,8161,8163],{"class":29,"line":748},[27,8156,300],{"class":183},[27,8158,6588],{"class":191},[27,8160,735],{"class":183},[27,8162,7598],{"class":191},[27,8164,243],{"class":183},[27,8166,8167,8169,8171,8173,8175],{"class":29,"line":762},[27,8168,300],{"class":183},[27,8170,6588],{"class":191},[27,8172,300],{"class":183},[27,8174,7611],{"class":191},[27,8176,292],{"class":183},[27,8178,8179,8181,8183,8185],{"class":29,"line":775},[27,8180,1044],{"class":233},[27,8182,735],{"class":183},[27,8184,7622],{"class":756},[27,8186,745],{"class":183},[27,8188,8189],{"class":29,"line":790},[27,8190,855],{"class":183},[27,8192,8193,8195,8197],{"class":29,"line":806},[27,8194,337],{"class":183},[27,8196,714],{"class":187},[27,8198,210],{"class":183},[10,8200,8201],{},"In the parent component, we listen for the \"update-rating\" event allowing us to respond to changes in the rating value and update the rating state accordingly. This ensures that any changes made in the \"Rating\" component are reflected and managed in the parent component:",[17,8203,8205],{"className":174,"code":8204,"language":176,"meta":22,"style":22},"\u003Cscript setup>\nimport Rating from \".\u002FRating.vue\";\nimport { ref } from \"vue\";\n\nconst rating = ref(3);\nconst totalRating = ref(5);\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n    \u003CRating :value=\"rating\" :total-rating=\"totalRating\" @update-rating=\"rating = $event\" \u002F>\n\u003C\u002Ftemplate>\n",[24,8206,8207,8217,8233,8253,8257,8273,8289,8297,8301,8309,8349],{"__ignoreMap":22},[27,8208,8209,8211,8213,8215],{"class":29,"line":30},[27,8210,184],{"class":183},[27,8212,188],{"class":187},[27,8214,207],{"class":191},[27,8216,210],{"class":183},[27,8218,8219,8221,8223,8225,8227,8229,8231],{"class":29,"line":47},[27,8220,1678],{"class":187},[27,8222,7103],{"class":191},[27,8224,1684],{"class":187},[27,8226,1687],{"class":198},[27,8228,7110],{"class":37},[27,8230,199],{"class":198},[27,8232,745],{"class":183},[27,8234,8235,8237,8239,8241,8243,8245,8247,8249,8251],{"class":29,"line":54},[27,8236,1678],{"class":187},[27,8238,2214],{"class":183},[27,8240,7123],{"class":191},[27,8242,2220],{"class":183},[27,8244,1684],{"class":187},[27,8246,1687],{"class":198},[27,8248,176],{"class":37},[27,8250,199],{"class":198},[27,8252,745],{"class":183},[27,8254,8255],{"class":29,"line":61},[27,8256,51],{"emptyLinePlaceholder":50},[27,8258,8259,8261,8263,8265,8267,8269,8271],{"class":29,"line":90},[27,8260,7144],{"class":215},[27,8262,7147],{"class":191},[27,8264,222],{"class":183},[27,8266,7123],{"class":33},[27,8268,306],{"class":183},[27,8270,7156],{"class":738},[27,8272,311],{"class":183},[27,8274,8275,8277,8279,8281,8283,8285,8287],{"class":29,"line":95},[27,8276,7144],{"class":215},[27,8278,7165],{"class":191},[27,8280,222],{"class":183},[27,8282,7123],{"class":33},[27,8284,306],{"class":183},[27,8286,7174],{"class":738},[27,8288,311],{"class":183},[27,8290,8291,8293,8295],{"class":29,"line":101},[27,8292,337],{"class":183},[27,8294,188],{"class":187},[27,8296,210],{"class":183},[27,8298,8299],{"class":29,"line":110},[27,8300,51],{"emptyLinePlaceholder":50},[27,8302,8303,8305,8307],{"class":29,"line":115},[27,8304,184],{"class":183},[27,8306,352],{"class":187},[27,8308,210],{"class":183},[27,8310,8311,8313,8315,8317,8319,8321,8323,8325,8327,8329,8331,8333,8335,8338,8340,8342,8345,8347],{"class":29,"line":121},[27,8312,359],{"class":183},[27,8314,7203],{"class":187},[27,8316,7206],{"class":191},[27,8318,195],{"class":183},[27,8320,199],{"class":198},[27,8322,7078],{"class":37},[27,8324,199],{"class":198},[27,8326,7217],{"class":191},[27,8328,195],{"class":183},[27,8330,199],{"class":198},[27,8332,6827],{"class":37},[27,8334,199],{"class":198},[27,8336,8337],{"class":191}," @update-rating",[27,8339,195],{"class":183},[27,8341,199],{"class":198},[27,8343,8344],{"class":37},"rating = $event",[27,8346,199],{"class":198},[27,8348,465],{"class":183},[27,8350,8351,8353,8355],{"class":29,"line":127},[27,8352,337],{"class":183},[27,8354,352],{"class":187},[27,8356,210],{"class":183},[10,8358,8359,8360,8365],{},"Excellent! We've successfully created a reusable Rating component that enables users to review and rate products or any other items in your project. This component can be further extended to accommodate various icons or symbols by utilizing ",[1510,8361,8364],{"href":8362,"rel":8363},"https:\u002F\u002Fvuejs.org\u002Fguide\u002Fcomponents\u002Fslots.html",[1514],"slots",". Additionally, it can be customized to display the rating of a product or other items without allowing user interaction.",[714,8367,8368],{},"html pre.shiki code .stPdD, html code.shiki .stPdD{--shiki-default:#999999;--shiki-dark:#444444}html pre.shiki code .sTPum, html code.shiki .sTPum{--shiki-default:#1E754F;--shiki-dark:#4D9375}html pre.shiki code .s9nN2, html code.shiki .s9nN2{--shiki-default:#B07D48;--shiki-dark:#BD976A}html pre.shiki code .scnC2, html code.shiki .scnC2{--shiki-default:#B5695977;--shiki-dark:#C98A7D77}html pre.shiki code .spP0B, html code.shiki .spP0B{--shiki-default:#B56959;--shiki-dark:#C98A7D}html pre.shiki code .slSvp, html code.shiki .slSvp{--shiki-default:#393A34;--shiki-dark:#DBD7CACC}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sGGdt, html code.shiki .sGGdt{--shiki-default:#999999;--shiki-default-font-style:italic;--shiki-dark:#444444;--shiki-dark-font-style:italic}html pre.shiki code .s5TCs, html code.shiki .s5TCs{--shiki-default:#AB5959;--shiki-dark:#CB7676}html pre.shiki code .s_xSY, html code.shiki .s_xSY{--shiki-default:#59873A;--shiki-dark:#80A665}html pre.shiki code .s_NWU, html code.shiki .s_NWU{--shiki-default:#2E8F82;--shiki-dark:#5DA994}html pre.shiki code .sqbOQ, html code.shiki .sqbOQ{--shiki-default:#2F798A;--shiki-dark:#4C9A91}html pre.shiki code .sHLBJ, html code.shiki .sHLBJ{--shiki-default:#998418;--shiki-dark:#B8A965}html pre.shiki code .sfsYZ, html code.shiki .sfsYZ{--shiki-default:#A65E2B;--shiki-dark:#C99076}html pre.shiki code .s7hpK, html code.shiki .s7hpK{--shiki-default:#B31D28;--shiki-default-font-style:italic;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic}",{"title":22,"searchDepth":47,"depth":47,"links":8370},[],"A rating component which is sometimes referred to as a star rating component, is a user-facing element that allows users to provide feedback or rate something, typically on a scale, and just to display the rating of something sometimes. It's used in ...","2023-10-12T21:06:23.034Z",{},"\u002Fwritings\u002Fbuilding-a-simple-rating-component-with-vue-3","4",{"title":6522,"description":6527},"building-a-simple-rating-component-with-vue-3","writings\u002Fbuilding-a-simple-rating-component-with-vue-3","qoBHMUH8ny8gqOhXWeP9K8u83ZqfW3qw7xblZYM7LDM",1784370713572]