[{"data":1,"prerenderedAt":1172},["ShallowReactive",2],{"articles":3},[4,646],{"id":5,"title":6,"body":7,"createdAt":635,"description":636,"extension":637,"meta":638,"navigation":106,"path":642,"seo":643,"stem":644,"__hash__":645},"articles\u002Farticles\u002F1.scroll-to-bottom-vue-with-hidden-elements.md","How to automatically scroll to bottom of an element after toggling its visibility in Vue 3.",{"type":8,"value":9,"toc":633},"minimark",[10,14,24,27,30,33,347,350,353,359,366,369,629],[11,12,6],"h1",{"id":13},"how-to-automatically-scroll-to-bottom-of-an-element-after-toggling-its-visibility-in-vue-3",[15,16,17],"p",{},[18,19,20],"strong",{},[21,22,23],"em",{},"Nov 22, 2024 10:26AM",[15,25,26],{},"When you need to automatically scroll an element to its bottom after toggling it into view in Vue 3, you would notice things doesn't actually work properly if you call the scroll function immediately after toggling the element's visibility.",[15,28,29],{},"Let's consider this scenario where you have a scrollable element that is hidden by default and a user has to click on a checkbox to make it visible.\nIf there is vital information at the bottom of the element, in most cases you want to automatically scroll to the botton as soon as the element is visible.",[15,31,32],{},"Below is a code snippet of how you might implement this.",[34,35,40],"pre",{"className":36,"code":37,"language":38,"meta":39,"style":39},"language-vue shiki shiki-themes dracula","\u003Cscript setup lang=\"ts\">\nimport { ref } from \"vue\";\n\nconst scrollEl = ref\u003CHTMLDivElement | null>(null);\nconst isVisible = ref(false);\n\nconst toggleVisibility = () => {\n  isVisible.value = !isVisible.value;\n\n  if (isVisible.value && scrollEl.value) {\n    scrollEl.value.scrollTop = scrollEl.value.scrollHeight;\n  }\n};\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Cbutton type=\"button\" @click=\"toggleVisibility\">Toggle Me\u003C\u002Fbutton>\n  \u003Cdiv ref=\"scrollEl\">Element with long scrollable content...\u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n","vue","",[41,42,43,79,101,108,146,166,171,191,205,210,225,236,242,248,258,263,273,312,338],"code",{"__ignoreMap":39},[44,45,48,52,56,60,63,66,70,74,76],"span",{"class":46,"line":47},"line",1,[44,49,51],{"class":50},"sCdxs","\u003C",[44,53,55],{"class":54},"s0Tla","script",[44,57,59],{"class":58},"sY_PY"," setup",[44,61,62],{"class":58}," lang",[44,64,65],{"class":54},"=",[44,67,69],{"class":68},"seVfx","\"",[44,71,73],{"class":72},"s-mGx","ts",[44,75,69],{"class":68},[44,77,78],{"class":50},">\n",[44,80,82,85,88,91,94,96,98],{"class":46,"line":81},2,[44,83,84],{"class":54},"import",[44,86,87],{"class":50}," { ref } ",[44,89,90],{"class":54},"from",[44,92,93],{"class":68}," \"",[44,95,38],{"class":72},[44,97,69],{"class":68},[44,99,100],{"class":50},";\n",[44,102,104],{"class":46,"line":103},3,[44,105,107],{"emptyLinePlaceholder":106},true,"\n",[44,109,111,114,117,119,123,125,129,132,136,139,143],{"class":46,"line":110},4,[44,112,113],{"class":54},"const",[44,115,116],{"class":50}," scrollEl ",[44,118,65],{"class":54},[44,120,122],{"class":121},"sAOxA"," ref",[44,124,51],{"class":50},[44,126,128],{"class":127},"sGEwX","HTMLDivElement",[44,130,131],{"class":54}," |",[44,133,135],{"class":134},"sCp4m"," null",[44,137,138],{"class":50},">(",[44,140,142],{"class":141},"sIQBb","null",[44,144,145],{"class":50},");\n",[44,147,149,151,154,156,158,161,164],{"class":46,"line":148},5,[44,150,113],{"class":54},[44,152,153],{"class":50}," isVisible ",[44,155,65],{"class":54},[44,157,122],{"class":121},[44,159,160],{"class":50},"(",[44,162,163],{"class":141},"false",[44,165,145],{"class":50},[44,167,169],{"class":46,"line":168},6,[44,170,107],{"emptyLinePlaceholder":106},[44,172,174,176,179,182,185,188],{"class":46,"line":173},7,[44,175,113],{"class":54},[44,177,178],{"class":121}," toggleVisibility",[44,180,181],{"class":54}," =",[44,183,184],{"class":50}," () ",[44,186,187],{"class":54},"=>",[44,189,190],{"class":50}," {\n",[44,192,194,197,199,202],{"class":46,"line":193},8,[44,195,196],{"class":50},"  isVisible.value ",[44,198,65],{"class":54},[44,200,201],{"class":54}," !",[44,203,204],{"class":50},"isVisible.value;\n",[44,206,208],{"class":46,"line":207},9,[44,209,107],{"emptyLinePlaceholder":106},[44,211,213,216,219,222],{"class":46,"line":212},10,[44,214,215],{"class":54},"  if",[44,217,218],{"class":50}," (isVisible.value ",[44,220,221],{"class":54},"&&",[44,223,224],{"class":50}," scrollEl.value) {\n",[44,226,228,231,233],{"class":46,"line":227},11,[44,229,230],{"class":50},"    scrollEl.value.scrollTop ",[44,232,65],{"class":54},[44,234,235],{"class":50}," scrollEl.value.scrollHeight;\n",[44,237,239],{"class":46,"line":238},12,[44,240,241],{"class":50},"  }\n",[44,243,245],{"class":46,"line":244},13,[44,246,247],{"class":50},"};\n",[44,249,251,254,256],{"class":46,"line":250},14,[44,252,253],{"class":50},"\u003C\u002F",[44,255,55],{"class":54},[44,257,78],{"class":50},[44,259,261],{"class":46,"line":260},15,[44,262,107],{"emptyLinePlaceholder":106},[44,264,266,268,271],{"class":46,"line":265},16,[44,267,51],{"class":50},[44,269,270],{"class":54},"template",[44,272,78],{"class":50},[44,274,276,279,282,285,287,289,291,293,296,298,300,303,305,308,310],{"class":46,"line":275},17,[44,277,278],{"class":50},"  \u003C",[44,280,281],{"class":54},"button",[44,283,284],{"class":58}," type",[44,286,65],{"class":54},[44,288,69],{"class":68},[44,290,281],{"class":72},[44,292,69],{"class":68},[44,294,295],{"class":58}," @click",[44,297,65],{"class":54},[44,299,69],{"class":68},[44,301,302],{"class":72},"toggleVisibility",[44,304,69],{"class":68},[44,306,307],{"class":50},">Toggle Me\u003C\u002F",[44,309,281],{"class":54},[44,311,78],{"class":50},[44,313,315,317,320,322,324,326,329,331,334,336],{"class":46,"line":314},18,[44,316,278],{"class":50},[44,318,319],{"class":54},"div",[44,321,122],{"class":58},[44,323,65],{"class":54},[44,325,69],{"class":68},[44,327,328],{"class":72},"scrollEl",[44,330,69],{"class":68},[44,332,333],{"class":50},">Element with long scrollable content...\u003C\u002F",[44,335,319],{"class":54},[44,337,78],{"class":50},[44,339,341,343,345],{"class":46,"line":340},19,[44,342,253],{"class":50},[44,344,270],{"class":54},[44,346,78],{"class":50},[15,348,349],{},"You would notice the above code doesn't produce the desired output.",[15,351,352],{},"As mentioned in the Vue documentation:",[354,355,356],"blockquote",{},[15,357,358],{},"When you mutate reactive state in Vue, the resulting DOM updates are not applied synchronously. Instead Vue buffers them until the \"next tick\" to ensure that each component updates only once no matter how many state changes you have made.",[15,360,361,362,365],{},"So a simple fix for the above issue is to call the ",[41,363,364],{},"nextTick"," utility function before the scrolling part of the code.",[15,367,368],{},"Here is an updated version of the code:",[34,370,372],{"className":36,"code":371,"language":38,"meta":39,"style":39},"\u003Cscript setup lang=\"ts\">\nimport { ref, nextTick } from \"vue\"; \u002F\u002F Updated import\n\nconst scrollEl = ref\u003CHTMLDivElement | null>(null);\nconst isVisible = ref(false);\n\n\u002F\u002F Function now marked as async\nconst toggleVisibility = async () => {\n  isVisible.value = !isVisible.value;\n\n  await nextTick(); \u002F\u002F Notice the new change here\n\n  if (isVisible.value && scrollEl.value) {\n    scrollEl.value.scrollTop = scrollEl.value.scrollHeight;\n  }\n};\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Cbutton type=\"button\" @click=\"toggleVisibility\">Toggle Me\u003C\u002Fbutton>\n  \u003Cdiv ref=\"scrollEl\">Element with long scrollable content...\u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n",[41,373,374,394,416,420,444,460,464,469,486,496,500,514,518,528,536,540,544,552,556,564,597,620],{"__ignoreMap":39},[44,375,376,378,380,382,384,386,388,390,392],{"class":46,"line":47},[44,377,51],{"class":50},[44,379,55],{"class":54},[44,381,59],{"class":58},[44,383,62],{"class":58},[44,385,65],{"class":54},[44,387,69],{"class":68},[44,389,73],{"class":72},[44,391,69],{"class":68},[44,393,78],{"class":50},[44,395,396,398,401,403,405,407,409,412],{"class":46,"line":81},[44,397,84],{"class":54},[44,399,400],{"class":50}," { ref, nextTick } ",[44,402,90],{"class":54},[44,404,93],{"class":68},[44,406,38],{"class":72},[44,408,69],{"class":68},[44,410,411],{"class":50},"; ",[44,413,415],{"class":414},"shSDL","\u002F\u002F Updated import\n",[44,417,418],{"class":46,"line":103},[44,419,107],{"emptyLinePlaceholder":106},[44,421,422,424,426,428,430,432,434,436,438,440,442],{"class":46,"line":110},[44,423,113],{"class":54},[44,425,116],{"class":50},[44,427,65],{"class":54},[44,429,122],{"class":121},[44,431,51],{"class":50},[44,433,128],{"class":127},[44,435,131],{"class":54},[44,437,135],{"class":134},[44,439,138],{"class":50},[44,441,142],{"class":141},[44,443,145],{"class":50},[44,445,446,448,450,452,454,456,458],{"class":46,"line":148},[44,447,113],{"class":54},[44,449,153],{"class":50},[44,451,65],{"class":54},[44,453,122],{"class":121},[44,455,160],{"class":50},[44,457,163],{"class":141},[44,459,145],{"class":50},[44,461,462],{"class":46,"line":168},[44,463,107],{"emptyLinePlaceholder":106},[44,465,466],{"class":46,"line":173},[44,467,468],{"class":414},"\u002F\u002F Function now marked as async\n",[44,470,471,473,475,477,480,482,484],{"class":46,"line":193},[44,472,113],{"class":54},[44,474,178],{"class":121},[44,476,181],{"class":54},[44,478,479],{"class":54}," async",[44,481,184],{"class":50},[44,483,187],{"class":54},[44,485,190],{"class":50},[44,487,488,490,492,494],{"class":46,"line":207},[44,489,196],{"class":50},[44,491,65],{"class":54},[44,493,201],{"class":54},[44,495,204],{"class":50},[44,497,498],{"class":46,"line":212},[44,499,107],{"emptyLinePlaceholder":106},[44,501,502,505,508,511],{"class":46,"line":227},[44,503,504],{"class":54},"  await",[44,506,507],{"class":121}," nextTick",[44,509,510],{"class":50},"(); ",[44,512,513],{"class":414},"\u002F\u002F Notice the new change here\n",[44,515,516],{"class":46,"line":238},[44,517,107],{"emptyLinePlaceholder":106},[44,519,520,522,524,526],{"class":46,"line":244},[44,521,215],{"class":54},[44,523,218],{"class":50},[44,525,221],{"class":54},[44,527,224],{"class":50},[44,529,530,532,534],{"class":46,"line":250},[44,531,230],{"class":50},[44,533,65],{"class":54},[44,535,235],{"class":50},[44,537,538],{"class":46,"line":260},[44,539,241],{"class":50},[44,541,542],{"class":46,"line":265},[44,543,247],{"class":50},[44,545,546,548,550],{"class":46,"line":275},[44,547,253],{"class":50},[44,549,55],{"class":54},[44,551,78],{"class":50},[44,553,554],{"class":46,"line":314},[44,555,107],{"emptyLinePlaceholder":106},[44,557,558,560,562],{"class":46,"line":340},[44,559,51],{"class":50},[44,561,270],{"class":54},[44,563,78],{"class":50},[44,565,567,569,571,573,575,577,579,581,583,585,587,589,591,593,595],{"class":46,"line":566},20,[44,568,278],{"class":50},[44,570,281],{"class":54},[44,572,284],{"class":58},[44,574,65],{"class":54},[44,576,69],{"class":68},[44,578,281],{"class":72},[44,580,69],{"class":68},[44,582,295],{"class":58},[44,584,65],{"class":54},[44,586,69],{"class":68},[44,588,302],{"class":72},[44,590,69],{"class":68},[44,592,307],{"class":50},[44,594,281],{"class":54},[44,596,78],{"class":50},[44,598,600,602,604,606,608,610,612,614,616,618],{"class":46,"line":599},21,[44,601,278],{"class":50},[44,603,319],{"class":54},[44,605,122],{"class":58},[44,607,65],{"class":54},[44,609,69],{"class":68},[44,611,328],{"class":72},[44,613,69],{"class":68},[44,615,333],{"class":50},[44,617,319],{"class":54},[44,619,78],{"class":50},[44,621,623,625,627],{"class":46,"line":622},22,[44,624,253],{"class":50},[44,626,270],{"class":54},[44,628,78],{"class":50},[630,631,632],"style",{},"html pre.shiki code .sCdxs, html code.shiki .sCdxs{--shiki-default:#F8F8F2}html pre.shiki code .s0Tla, html code.shiki .s0Tla{--shiki-default:#FF79C6}html pre.shiki code .sY_PY, html code.shiki .sY_PY{--shiki-default:#50FA7B;--shiki-default-font-style:italic}html pre.shiki code .seVfx, html code.shiki .seVfx{--shiki-default:#E9F284}html pre.shiki code .s-mGx, html code.shiki .s-mGx{--shiki-default:#F1FA8C}html pre.shiki code .sAOxA, html code.shiki .sAOxA{--shiki-default:#50FA7B}html pre.shiki code .sGEwX, html code.shiki .sGEwX{--shiki-default:#FFB86C;--shiki-default-font-style:italic}html pre.shiki code .sCp4m, html code.shiki .sCp4m{--shiki-default:#8BE9FD;--shiki-default-font-style:italic}html pre.shiki code .sIQBb, html code.shiki .sIQBb{--shiki-default:#BD93F9}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .shSDL, html code.shiki .shSDL{--shiki-default:#6272A4}",{"title":39,"searchDepth":81,"depth":81,"links":634},[],null,"When you need to automatically scroll an element to its bottom after toggling it into view in Vue 3, you would notice things doesn't actually work properly if you call the scroll function immediately after toggling the element's visibility. Let's consider this scenario where you have a scrollable element that is hidden by default and a user has to click on a checkbox to make it visible","md",{"If there is vital information at the bottom of the element, in most cases you want to automatically scroll to the botton as soon as the element is visible":639},[640],[641],{"\" createdAt":23},"\u002Farticles\u002Fscroll-to-bottom-vue-with-hidden-elements",{"title":6,"description":636},"articles\u002F1.scroll-to-bottom-vue-with-hidden-elements","L8Hntp8fRSV-PbA3QyNaUJs05XCHjrIHYFUhYDNHXRo",{"id":647,"title":648,"body":649,"createdAt":23,"description":1166,"extension":637,"meta":1167,"navigation":106,"path":1168,"seo":1169,"stem":1170,"__hash__":1171},"articles\u002Farticles\u002F2.laravel-sanctum-vue-spa-common-pitfalls.md","Common pitfalls to watch out for when integrating Laravel Sanctum with a Vue 3 Frontend",{"type":8,"value":650,"toc":1164},[651,654,661,664,676,679,682,689,692,729,1147,1151,1154,1161],[11,652,648],{"id":653},"common-pitfalls-to-watch-out-for-when-integrating-laravel-sanctum-with-a-vue-3-frontend",[15,655,656,657,660],{},"When implementing a cookie based authentication in your Vue 3 Single Page Application with Laravel Sanctum, it can sometimes be frustrating when you keep geting ",[18,658,659],{},"401"," unauthorized in your browser console even when you are fully convinced that you have followed all the instructions from the documentation.",[15,662,663],{},"In this article, I will be sharing with you some common pitfalls I have identified based on my experience while working with Laravel Sanctum and a separate Vue SPA.",[15,665,666,667,675],{},"Before we proceed, I must say the easiest way to integrate Laravel Sanctum into your Laravel application is by using the ",[668,669,674],"a",{"href":670,"rel":671,"target":673},"https:\u002F\u002Flaravel.com\u002Fdocs\u002F11.x\u002Fstarter-kits#laravel-breeze",[672],"nofollow","\\_blank","Laravel Breeze"," package.",[15,677,678],{},"Laravel has excellent documentation covering how to set up Sanctum in your Laravel application. Please go through the documentation as we won't be covering the exact steps in this post.",[15,680,681],{},"This tutorial is focusing on the common pitfalls you would likely face even after going through the documentation.",[15,683,684,685,688],{},"Be sure to define the following environment variables in your Laravel ",[41,686,687],{},".env"," file:",[15,690,691],{},"Make sure you add the port numbers.",[34,693,697],{"className":694,"code":695,"language":696,"meta":39,"style":39},"language-dotenv shiki shiki-themes dracula","APP_URL=http:\u002F\u002Flocalhost:80\nFRONTEND_URL=http:\u002F\u002Flocalhost:5173\nSESSION_DOMAIN=localhost\n","dotenv",[41,698,699,709,719],{"__ignoreMap":39},[44,700,701,704,706],{"class":46,"line":47},[44,702,703],{"class":50},"APP_URL",[44,705,65],{"class":54},[44,707,708],{"class":50},"http:\u002F\u002Flocalhost:80\n",[44,710,711,714,716],{"class":46,"line":81},[44,712,713],{"class":50},"FRONTEND_URL",[44,715,65],{"class":54},[44,717,718],{"class":50},"http:\u002F\u002Flocalhost:5173\n",[44,720,721,724,726],{"class":46,"line":103},[44,722,723],{"class":50},"SESSION_DOMAIN",[44,725,65],{"class":54},[44,727,728],{"class":50},"localhost\n",[730,731,732,790,1119,1130],"ul",{},[733,734,735,739,740,743,744,747,748,751,752,754,755,757,758],"li",{},[11,736,738],{"id":737},"_1-using-local-custom-domains","#1 Using local Custom domains","Let's assume you're using custom domains locally.\nSo your Frontend domain is ",[41,741,742],{},"demo.local"," and the Backend API is ",[41,745,746],{},"api.demo.local",".",[749,750],"br",{},"The updated .env variables will now look like below.\nNotice the leading ",[41,753,747],{}," in the ",[41,756,723],{}," variable",[34,759,761],{"className":694,"code":760,"language":696,"meta":39,"style":39},"APP_URL=http:\u002F\u002Fapi.demo.local:80\nFRONTEND_URL=http:\u002F\u002Fdemo.local:5173\nSESSION_DOMAIN=.demo.local\n\n",[41,762,763,772,781],{"__ignoreMap":39},[44,764,765,767,769],{"class":46,"line":47},[44,766,703],{"class":50},[44,768,65],{"class":54},[44,770,771],{"class":50},"http:\u002F\u002Fapi.demo.local:80\n",[44,773,774,776,778],{"class":46,"line":81},[44,775,713],{"class":50},[44,777,65],{"class":54},[44,779,780],{"class":50},"http:\u002F\u002Fdemo.local:5173\n",[44,782,783,785,787],{"class":46,"line":103},[44,784,723],{"class":50},[44,786,65],{"class":54},[44,788,789],{"class":50},".demo.local\n",[733,791,792,796,797,800,801,804,805,808,809,811,812,814,816,817,819,820,823,824,826,827],{},[11,793,795],{"id":794},"_2-misconfigured-cors-file","#2 Misconfigured CORS file","If you're integrating Sanctum into an existing laravel application that has a ",[41,798,799],{},"cors"," file already, check to make sure the ",[41,802,803],{},"paths"," and\n",[41,806,807],{},"allowed_origins"," values are set properly.\nMost importantly the ",[41,810,803],{}," value should allow requests from both the api and web routes.",[749,813],{},[41,815,807],{}," array should include your Frontend domain.",[749,818],{},"Also make sure ",[41,821,822],{},"support_credentials"," is set to true.",[749,825],{},"Below is a sample cors file",[34,828,832],{"className":829,"code":830,"language":831,"meta":39,"style":39},"language-php shiki shiki-themes dracula","  return [\n\n      \u002F*\n      |--------------------------------------------------------------------------\n      | Cross-Origin Resource Sharing (CORS) Configuration\n      |--------------------------------------------------------------------------\n      |\n      | Here you may configure your settings for cross-origin resource sharing\n      | or \"CORS\". This determines what cross-origin operations may execute\n      | in web browsers. You are free to adjust these settings as needed.\n      |\n      | To learn more: https:\u002F\u002Fdeveloper.mozilla.org\u002Fen-US\u002Fdocs\u002FWeb\u002FHTTP\u002FCORS\n      |\n      *\u002F\n\n      'paths' => ['*'],\n\n      'allowed_methods' => ['*'],\n\n      'allowed_origins' => [env('FRONTEND_URL', 'http:\u002F\u002Flocalhost:5173')],\n\n      'allowed_origins_patterns' => [],\n\n      'allowed_headers' => ['*'],\n\n      'exposed_headers' => [],\n\n      'max_age' => 0,\n\n      'supports_credentials' => true,\n\n  ];\n","php",[41,833,834,842,846,851,856,861,865,870,875,880,885,889,894,898,903,907,933,937,958,962,999,1003,1017,1022,1044,1049,1063,1068,1086,1091,1108,1113],{"__ignoreMap":39},[44,835,836,839],{"class":46,"line":47},[44,837,838],{"class":54},"  return",[44,840,841],{"class":50}," [\n",[44,843,844],{"class":46,"line":81},[44,845,107],{"emptyLinePlaceholder":106},[44,847,848],{"class":46,"line":103},[44,849,850],{"class":414},"      \u002F*\n",[44,852,853],{"class":46,"line":110},[44,854,855],{"class":414},"      |--------------------------------------------------------------------------\n",[44,857,858],{"class":46,"line":148},[44,859,860],{"class":414},"      | Cross-Origin Resource Sharing (CORS) Configuration\n",[44,862,863],{"class":46,"line":168},[44,864,855],{"class":414},[44,866,867],{"class":46,"line":173},[44,868,869],{"class":414},"      |\n",[44,871,872],{"class":46,"line":193},[44,873,874],{"class":414},"      | Here you may configure your settings for cross-origin resource sharing\n",[44,876,877],{"class":46,"line":207},[44,878,879],{"class":414},"      | or \"CORS\". This determines what cross-origin operations may execute\n",[44,881,882],{"class":46,"line":212},[44,883,884],{"class":414},"      | in web browsers. You are free to adjust these settings as needed.\n",[44,886,887],{"class":46,"line":227},[44,888,869],{"class":414},[44,890,891],{"class":46,"line":238},[44,892,893],{"class":414},"      | To learn more: https:\u002F\u002Fdeveloper.mozilla.org\u002Fen-US\u002Fdocs\u002FWeb\u002FHTTP\u002FCORS\n",[44,895,896],{"class":46,"line":244},[44,897,869],{"class":414},[44,899,900],{"class":46,"line":250},[44,901,902],{"class":414},"      *\u002F\n",[44,904,905],{"class":46,"line":260},[44,906,107],{"emptyLinePlaceholder":106},[44,908,909,912,914,917,920,923,925,928,930],{"class":46,"line":265},[44,910,911],{"class":68},"      '",[44,913,803],{"class":72},[44,915,916],{"class":68},"'",[44,918,919],{"class":54}," =>",[44,921,922],{"class":50}," [",[44,924,916],{"class":68},[44,926,927],{"class":72},"*",[44,929,916],{"class":68},[44,931,932],{"class":50},"],\n",[44,934,935],{"class":46,"line":275},[44,936,107],{"emptyLinePlaceholder":106},[44,938,939,941,944,946,948,950,952,954,956],{"class":46,"line":314},[44,940,911],{"class":68},[44,942,943],{"class":72},"allowed_methods",[44,945,916],{"class":68},[44,947,919],{"class":54},[44,949,922],{"class":50},[44,951,916],{"class":68},[44,953,927],{"class":72},[44,955,916],{"class":68},[44,957,932],{"class":50},[44,959,960],{"class":46,"line":340},[44,961,107],{"emptyLinePlaceholder":106},[44,963,964,966,968,970,972,974,977,979,981,983,985,988,991,994,996],{"class":46,"line":566},[44,965,911],{"class":68},[44,967,807],{"class":72},[44,969,916],{"class":68},[44,971,919],{"class":54},[44,973,922],{"class":50},[44,975,976],{"class":121},"env",[44,978,160],{"class":50},[44,980,916],{"class":68},[44,982,713],{"class":72},[44,984,916],{"class":68},[44,986,987],{"class":50},",",[44,989,990],{"class":68}," '",[44,992,993],{"class":72},"http:\u002F\u002Flocalhost:5173",[44,995,916],{"class":68},[44,997,998],{"class":50},")],\n",[44,1000,1001],{"class":46,"line":599},[44,1002,107],{"emptyLinePlaceholder":106},[44,1004,1005,1007,1010,1012,1014],{"class":46,"line":622},[44,1006,911],{"class":68},[44,1008,1009],{"class":72},"allowed_origins_patterns",[44,1011,916],{"class":68},[44,1013,919],{"class":54},[44,1015,1016],{"class":50}," [],\n",[44,1018,1020],{"class":46,"line":1019},23,[44,1021,107],{"emptyLinePlaceholder":106},[44,1023,1025,1027,1030,1032,1034,1036,1038,1040,1042],{"class":46,"line":1024},24,[44,1026,911],{"class":68},[44,1028,1029],{"class":72},"allowed_headers",[44,1031,916],{"class":68},[44,1033,919],{"class":54},[44,1035,922],{"class":50},[44,1037,916],{"class":68},[44,1039,927],{"class":72},[44,1041,916],{"class":68},[44,1043,932],{"class":50},[44,1045,1047],{"class":46,"line":1046},25,[44,1048,107],{"emptyLinePlaceholder":106},[44,1050,1052,1054,1057,1059,1061],{"class":46,"line":1051},26,[44,1053,911],{"class":68},[44,1055,1056],{"class":72},"exposed_headers",[44,1058,916],{"class":68},[44,1060,919],{"class":54},[44,1062,1016],{"class":50},[44,1064,1066],{"class":46,"line":1065},27,[44,1067,107],{"emptyLinePlaceholder":106},[44,1069,1071,1073,1076,1078,1080,1083],{"class":46,"line":1070},28,[44,1072,911],{"class":68},[44,1074,1075],{"class":72},"max_age",[44,1077,916],{"class":68},[44,1079,919],{"class":54},[44,1081,1082],{"class":141}," 0",[44,1084,1085],{"class":50},",\n",[44,1087,1089],{"class":46,"line":1088},29,[44,1090,107],{"emptyLinePlaceholder":106},[44,1092,1094,1096,1099,1101,1103,1106],{"class":46,"line":1093},30,[44,1095,911],{"class":68},[44,1097,1098],{"class":72},"supports_credentials",[44,1100,916],{"class":68},[44,1102,919],{"class":54},[44,1104,1105],{"class":141}," true",[44,1107,1085],{"class":50},[44,1109,1111],{"class":46,"line":1110},31,[44,1112,107],{"emptyLinePlaceholder":106},[44,1114,1116],{"class":46,"line":1115},32,[44,1117,1118],{"class":50},"  ];\n",[733,1120,1121,1125,1126,1129],{},[11,1122,1124],{"id":1123},"_3-sanctum-config-file","#3 Sanctum Config file","The generated sanctum file should be fine to work with, but you can review it to make sure your frontend application url is included or referenced in the ",[41,1127,1128],{},"stateful"," key.",[733,1131,1132,1136,1137,747,1140,1142,1143,1146],{},[11,1133,1135],{"id":1134},"_4-use-web-route-for-authentication","#4 Use web route for authentication","Laravel will automatically place the authentication routes in the web.php file or include a reference of the auth.php if you use an authentication scafolding package like ",[668,1138,674],{"href":670,"rel":1139,"target":673},[672],[749,1141],{},"In a case where you're defining the authentication routes and controllers manually, you should make sure the routes are placed in the ",[41,1144,1145],{},"web.php"," file.",[11,1148,1150],{"id":1149},"conclusion","Conclusion",[15,1152,1153],{},"In conclusion Laravel Sanctum makes authenticating your Vue Single Page Application with a Laravel backend using cookies and sessions.",[15,1155,1156,1157,1160],{},"Laravel goes one step forward by providing ",[668,1158,674],{"href":670,"rel":1159},[672]," which makes it super easier to generate all your authentication boilerplate code.\nThere are however some common mistakes developers make when working with Laravel Sanctum and a Single Page Application and this post highlighted some of these mistakes and the suggested solutions.",[630,1162,1163],{},"html pre.shiki code .sCdxs, html code.shiki .sCdxs{--shiki-default:#F8F8F2}html pre.shiki code .s0Tla, html code.shiki .s0Tla{--shiki-default:#FF79C6}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .shSDL, html code.shiki .shSDL{--shiki-default:#6272A4}html pre.shiki code .seVfx, html code.shiki .seVfx{--shiki-default:#E9F284}html pre.shiki code .s-mGx, html code.shiki .s-mGx{--shiki-default:#F1FA8C}html pre.shiki code .sAOxA, html code.shiki .sAOxA{--shiki-default:#50FA7B}html pre.shiki code .sIQBb, html code.shiki .sIQBb{--shiki-default:#BD93F9}",{"title":39,"searchDepth":81,"depth":81,"links":1165},[],"When implementing a cookie based authentication in your Vue 3 Single Page Application with Laravel Sanctum, it can sometimes be frustrating when you keep geting **401** unauthorized in your browser console even when you are fully convinced that you have followed all the instructions from the documentation. In this article, I will be sharing with you some common pitfalls I have identified based on my experience while working with Laravel Sanctum and a separate Vue SPA...",{},"\u002Farticles\u002Flaravel-sanctum-vue-spa-common-pitfalls",{"title":648,"description":1166},"articles\u002F2.laravel-sanctum-vue-spa-common-pitfalls","bOyn-NNQXTaIJHhwyiyS2tBbB-k7rc1MBhFTi7sgssQ",1785226969796]