[{"data":1,"prerenderedAt":874},["ShallowReactive",2],{"blog-post-\u002Fblog\u002Fremote-config-for-nuxt-in-five-minutes":3},{"id":4,"title":5,"author":6,"body":7,"date":862,"description":863,"draft":864,"extension":865,"image":866,"meta":867,"navigation":337,"path":868,"seo":869,"sitemap":870,"stem":872,"__hash__":873},"blog\u002Fblog\u002Fremote-config-for-nuxt-in-five-minutes.md","Remote config for Nuxt in five minutes","Alejandro Beiderman",{"type":8,"value":9,"toc":852},"minimark",[10,14,17,22,39,48,55,59,88,91,211,229,240,243,247,254,407,410,420,423,427,437,440,443,447,454,543,546,550,553,556,679,685,788,807,810,814,823,836,840,848],[11,12,13],"p",{},"Nuxt renders a page twice, once on the server via SSR, and then again in the browser. That is good for users but can be tricky for feature flags. If the server renders the page with the flag off and the browser decides it is on, you get a flash of the wrong content and a hydration warning in the console. Most flag SDKs leave it up to you to wire the server and the browser yourself and to make sure they are synced up.",[11,15,16],{},"The ConfigDirector Nuxt module does it for you. In this post we will add a flag to a Nuxt app, have it render on the server, flip it live in the browser, read it from a server route, and update it to target a group of users. It takes about five minutes with a running Nuxt app.",[18,19,21],"h2",{"id":20},"_1-create-the-flag","1. Create the flag",[11,23,24,25,33,34,38],{},"Sign in to the ",[26,27,32],"a",{"href":28,"rel":29,"target":31},"https:\u002F\u002Fapp.configdirector.com\u002F",[30],"noopener","_blank","dashboard"," and create a config. For this post the key is ",[35,36,37],"code",{},"new-pricing-banner",", the type is Boolean, and the SDK availability is both client and server. The availability matters for Nuxt: the browser and the server read the same flag, so it has to be available to both.",[11,40,41,42,47],{},"The key is what your code reads, so pick the name you want to see in the code. If you change your mind later, ",[26,43,46],{"href":44,"rel":45,"target":31},"https:\u002F\u002Fwww.configdirector.com\u002Fblog\u002Fsafe-renaming-rename-a-feature-flag-thats-already-in-production",[30],"renaming it is safe",", even in production.",[11,49,50,51,54],{},"Leave the value ",[35,52,53],{},"false"," in every environment for now. You will turn it on from the dashboard once the app is reading it.",[18,56,58],{"id":57},"_2-install-the-module","2. Install the module",[60,61,66],"pre",{"className":62,"code":63,"language":64,"meta":65,"style":65},"language-bash shiki shiki-themes github-light-default github-light-default github-dark-default","npm install --save @configdirector\u002Fnuxt-sdk\n","bash","",[35,67,68],{"__ignoreMap":65},[69,70,73,77,81,85],"span",{"class":71,"line":72},"line",1,[69,74,76],{"class":75},"sQJ0q","npm",[69,78,80],{"class":79},"s-BGO"," install",[69,82,84],{"class":83},"sDkKp"," --save",[69,86,87],{"class":79}," @configdirector\u002Fnuxt-sdk\n",[11,89,90],{},"Register the module and give it the two SDK keys from the SDK Keys page of your project. Each environment has its own pair. The client key is public and ends up in the browser bundle. The server key is a secret and belongs in an environment variable, not in the code repository:",[60,92,97],{"className":93,"code":94,"filename":95,"language":96,"meta":65,"style":65},"language-ts shiki shiki-themes github-light-default github-light-default github-dark-default","export default defineNuxtConfig({\n  modules: [\"@configdirector\u002Fnuxt-sdk\"],\n  runtimeConfig: {\n    public: {\n      configdirector: {\n        clientSdkKey: \"YOUR-CLIENT-SDK-KEY\",\n      },\n    },\n    configdirector: {\n      serverSdkKey: \"\",\n    },\n  },\n});\n","nuxt.config.ts","ts",[35,98,99,115,131,137,143,149,160,166,172,178,189,194,202],{"__ignoreMap":65},[69,100,101,105,108,112],{"class":71,"line":72},[69,102,104],{"class":103},"s5nWI","export",[69,106,107],{"class":103}," default",[69,109,111],{"class":110},"sd69k"," defineNuxtConfig",[69,113,114],{"class":75},"({\n",[69,116,118,122,125,128],{"class":71,"line":117},2,[69,119,121],{"class":120},"s_bOF","  modules: [",[69,123,124],{"class":79},"\"@configdirector\u002Fnuxt-sdk\"",[69,126,127],{"class":120},"]",[69,129,130],{"class":75},",\n",[69,132,134],{"class":71,"line":133},3,[69,135,136],{"class":120},"  runtimeConfig: {\n",[69,138,140],{"class":71,"line":139},4,[69,141,142],{"class":120},"    public: {\n",[69,144,146],{"class":71,"line":145},5,[69,147,148],{"class":120},"      configdirector: {\n",[69,150,152,155,158],{"class":71,"line":151},6,[69,153,154],{"class":120},"        clientSdkKey: ",[69,156,157],{"class":79},"\"YOUR-CLIENT-SDK-KEY\"",[69,159,130],{"class":120},[69,161,163],{"class":71,"line":162},7,[69,164,165],{"class":120},"      },\n",[69,167,169],{"class":71,"line":168},8,[69,170,171],{"class":120},"    },\n",[69,173,175],{"class":71,"line":174},9,[69,176,177],{"class":120},"    configdirector: {\n",[69,179,181,184,187],{"class":71,"line":180},10,[69,182,183],{"class":120},"      serverSdkKey: ",[69,185,186],{"class":79},"\"\"",[69,188,130],{"class":120},[69,190,192],{"class":71,"line":191},11,[69,193,171],{"class":120},[69,195,197,200],{"class":71,"line":196},12,[69,198,199],{"class":120},"  }",[69,201,130],{"class":75},[69,203,205,208],{"class":71,"line":204},13,[69,206,207],{"class":75},"})",[69,209,210],{"class":120},";\n",[60,212,214],{"className":62,"code":213,"language":64,"meta":65,"style":65},"export NUXT_CONFIGDIRECTOR_SERVER_SDK_KEY=YOUR-SERVER-SDK-KEY\n",[35,215,216],{"__ignoreMap":65},[69,217,218,220,223,226],{"class":71,"line":72},[69,219,104],{"class":103},[69,221,222],{"class":120}," NUXT_CONFIGDIRECTOR_SERVER_SDK_KEY",[69,224,225],{"class":103},"=",[69,227,228],{"class":120},"YOUR-SERVER-SDK-KEY\n",[11,230,231,232,235,236,239],{},"Nuxt's runtime config fills ",[35,233,234],{},"serverSdkKey"," from that variable at startup, so the same build works in every environment with a different key. The client key can be overridden the same way with ",[35,237,238],{},"NUXT_PUBLIC_CONFIGDIRECTOR_CLIENT_SDK_KEY",".",[11,241,242],{},"That is the whole setup. There is no plugin to write and no provider component to wrap the app in.",[18,244,246],{"id":245},"_3-read-it-in-a-page","3. Read it in a page",[11,248,249,250,253],{},"The ",[35,251,252],{},"useConfigDirectorValue"," composable takes the key and the in-code default value, which is the value your code uses if the flag cannot be evaluated:",[60,255,260],{"className":256,"code":257,"filename":258,"language":259,"meta":65,"style":65},"language-vue shiki shiki-themes github-light-default github-light-default github-dark-default","\u003Cscript setup lang=\"ts\">\nconst { value: showNewBanner } = useConfigDirectorValue(\"new-pricing-banner\", false);\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Csection>\n    \u003CPricingBanner v-if=\"showNewBanner\" \u002F>\n    \u003CPricingTable \u002F>\n  \u003C\u002Fsection>\n\u003C\u002Ftemplate>\n","pages\u002Fpricing.vue","vue",[35,261,262,285,324,333,339,348,358,381,390,399],{"__ignoreMap":65},[69,263,264,267,271,274,277,279,282],{"class":71,"line":72},[69,265,266],{"class":120},"\u003C",[69,268,270],{"class":269},"sfEsd","script",[69,272,273],{"class":83}," setup",[69,275,276],{"class":83}," lang",[69,278,225],{"class":120},[69,280,281],{"class":79},"\"ts\"",[69,283,284],{"class":120},">\n",[69,286,287,290,293,296,299,302,305,307,310,313,316,319,321],{"class":71,"line":117},[69,288,289],{"class":103},"const",[69,291,292],{"class":120}," { ",[69,294,295],{"class":75},"value",[69,297,298],{"class":120},": ",[69,300,301],{"class":83},"showNewBanner",[69,303,304],{"class":120}," } ",[69,306,225],{"class":103},[69,308,309],{"class":110}," useConfigDirectorValue",[69,311,312],{"class":120},"(",[69,314,315],{"class":79},"\"new-pricing-banner\"",[69,317,318],{"class":120},", ",[69,320,53],{"class":83},[69,322,323],{"class":120},");\n",[69,325,326,329,331],{"class":71,"line":133},[69,327,328],{"class":120},"\u003C\u002F",[69,330,270],{"class":269},[69,332,284],{"class":120},[69,334,335],{"class":71,"line":139},[69,336,338],{"emptyLinePlaceholder":337},true,"\n",[69,340,341,343,346],{"class":71,"line":145},[69,342,266],{"class":120},[69,344,345],{"class":269},"template",[69,347,284],{"class":120},[69,349,350,353,356],{"class":71,"line":151},[69,351,352],{"class":120},"  \u003C",[69,354,355],{"class":269},"section",[69,357,284],{"class":120},[69,359,360,363,366,369,371,374,376,378],{"class":71,"line":162},[69,361,362],{"class":120},"    \u003C",[69,364,365],{"class":269},"PricingBanner",[69,367,368],{"class":103}," v-if",[69,370,225],{"class":120},[69,372,373],{"class":120},"\"",[69,375,301],{"class":120},[69,377,373],{"class":120},[69,379,380],{"class":120}," \u002F>\n",[69,382,383,385,388],{"class":71,"line":168},[69,384,362],{"class":120},[69,386,387],{"class":269},"PricingTable",[69,389,380],{"class":120},[69,391,392,395,397],{"class":71,"line":174},[69,393,394],{"class":120},"  \u003C\u002F",[69,396,355],{"class":269},[69,398,284],{"class":120},[69,400,401,403,405],{"class":71,"line":180},[69,402,328],{"class":120},[69,404,345],{"class":269},[69,406,284],{"class":120},[11,408,409],{},"Start the dev server and load the page. The server rendered it with the real value of the flag, and the browser picked up from there. There is no flash and no hydration mismatch, because both sides evaluated the same flag against the same context before anything was sent.",[11,411,412,413,415,416,419],{},"Two things make that work. On the server, the module keeps one long-lived client in Nitro that connects to ConfigDirector when the server starts (which requires the server SDK key). Requests that arrive before the first payload are held, for up to three seconds by default, so even the first request after a deploy renders real values rather than defaults. In the browser, the client connects on app creation, before any page component mounts, and the composable's ",[35,414,295],{}," is a ",[35,417,418],{},"ShallowRef"," that updates when the flag changes.",[11,421,422],{},"Back in the dashboard's Getting Started guide, the Connect SDK page of your project is listening for the first evaluation. It flips to \"Your app is connected\" within seconds of the page load, which is how you know everything is configured correctly before you go any further.",[18,424,426],{"id":425},"_4-flip-it-live","4. Flip it live",[11,428,429,430,432,433,436],{},"Set ",[35,431,37],{}," to ",[35,434,435],{},"true"," in the test\u002Fdev environment and save. Look at the browser tab. The banner appears without a reload.",[11,438,439],{},"The browser client holds a streaming connection, so a change in the dashboard reaches every open tab within moments of saving. The server SDK holds its own streaming connection, so the next server-rendered request shows the banner too. Nothing in the app had to poll or refresh.",[11,441,442],{},"If you prefer, either the client or the server SDK connection can poll instead of stream. The server defaults to every five minutes and the browser to every minute. Streaming is the default on both and the right choice unless your network setup rules it out.",[18,444,446],{"id":445},"_5-read-it-in-a-server-route","5. Read it in a server route",[11,448,449,450,453],{},"Server routes and middleware use the same Nitro client through the server-side ",[35,451,452],{},"useConfigDirectorClient"," composable:",[60,455,458],{"className":93,"code":456,"filename":457,"language":96,"meta":65,"style":65},"export default defineEventHandler(async () => {\n  const client = useConfigDirectorClient();\n  const showNewBanner = client.getValue(\"new-pricing-banner\", false);\n  return { showNewBanner };\n});\n","server\u002Fapi\u002Fpricing.get.ts",[35,459,460,483,500,525,533],{"__ignoreMap":65},[69,461,462,464,466,469,471,474,477,480],{"class":71,"line":72},[69,463,104],{"class":103},[69,465,107],{"class":103},[69,467,468],{"class":110}," defineEventHandler",[69,470,312],{"class":75},[69,472,473],{"class":103},"async",[69,475,476],{"class":75}," () ",[69,478,479],{"class":103},"=>",[69,481,482],{"class":120}," {\n",[69,484,485,488,491,494,497],{"class":71,"line":117},[69,486,487],{"class":103},"  const",[69,489,490],{"class":83}," client",[69,492,493],{"class":103}," =",[69,495,496],{"class":110}," useConfigDirectorClient",[69,498,499],{"class":120},"();\n",[69,501,502,504,507,509,512,515,517,519,521,523],{"class":71,"line":133},[69,503,487],{"class":103},[69,505,506],{"class":83}," showNewBanner",[69,508,493],{"class":103},[69,510,511],{"class":120}," client.",[69,513,514],{"class":110},"getValue",[69,516,312],{"class":120},[69,518,315],{"class":79},[69,520,318],{"class":120},[69,522,53],{"class":83},[69,524,323],{"class":120},[69,526,527,530],{"class":71,"line":139},[69,528,529],{"class":103},"  return",[69,531,532],{"class":120}," { showNewBanner };\n",[69,534,535,538,541],{"class":71,"line":145},[69,536,537],{"class":120},"}",[69,539,540],{"class":75},")",[69,542,210],{"class":120},[11,544,545],{},"The server SDK client evaluates targeting rules locally. Evaluating a flag in a route does not make a network request. It is a lookup against the payload the SDK already holds in memory, so the cost of evaluating flags in hot paths is an in-memory hash map lookup.",[18,547,549],{"id":548},"_6-target-it-to-some-users","6. Target it to some users",[11,551,552],{},"ConfigDirector evaluates the flag against a user context, which carries an ID, an optional name, and whatever traits you choose to send.",[11,554,555],{},"In the browser, give the client the signed-in user after login:",[60,557,560],{"className":256,"code":558,"filename":559,"language":259,"meta":65,"style":65},"\u003Cscript setup lang=\"ts\">\nconst { updateContext } = useConfigDirectorContext();\n\nasync function onSignedIn(user: { id: string; plan: string }) {\n  await updateContext({\n    id: user.id,\n    traits: { plan: user.plan },\n  });\n}\n\u003C\u002Fscript>\n","components\u002FSignIn.vue",[35,561,562,578,596,600,641,651,656,661,666,671],{"__ignoreMap":65},[69,563,564,566,568,570,572,574,576],{"class":71,"line":72},[69,565,266],{"class":120},[69,567,270],{"class":269},[69,569,273],{"class":83},[69,571,276],{"class":83},[69,573,225],{"class":120},[69,575,281],{"class":79},[69,577,284],{"class":120},[69,579,580,582,584,587,589,591,594],{"class":71,"line":117},[69,581,289],{"class":103},[69,583,292],{"class":120},[69,585,586],{"class":83},"updateContext",[69,588,304],{"class":120},[69,590,225],{"class":103},[69,592,593],{"class":110}," useConfigDirectorContext",[69,595,499],{"class":120},[69,597,598],{"class":71,"line":133},[69,599,338],{"emptyLinePlaceholder":337},[69,601,602,604,607,610,612,615,618,620,623,625,628,631,634,636,638],{"class":71,"line":139},[69,603,473],{"class":103},[69,605,606],{"class":103}," function",[69,608,609],{"class":110}," onSignedIn",[69,611,312],{"class":120},[69,613,614],{"class":75},"user",[69,616,617],{"class":103},":",[69,619,292],{"class":120},[69,621,622],{"class":75},"id",[69,624,617],{"class":103},[69,626,627],{"class":83}," string",[69,629,630],{"class":120},"; ",[69,632,633],{"class":75},"plan",[69,635,617],{"class":103},[69,637,627],{"class":83},[69,639,640],{"class":120}," }) {\n",[69,642,643,646,649],{"class":71,"line":145},[69,644,645],{"class":103},"  await",[69,647,648],{"class":110}," updateContext",[69,650,114],{"class":120},[69,652,653],{"class":71,"line":151},[69,654,655],{"class":120},"    id: user.id,\n",[69,657,658],{"class":71,"line":162},[69,659,660],{"class":120},"    traits: { plan: user.plan },\n",[69,662,663],{"class":71,"line":168},[69,664,665],{"class":120},"  });\n",[69,667,668],{"class":71,"line":174},[69,669,670],{"class":120},"}\n",[69,672,673,675,677],{"class":71,"line":180},[69,674,328],{"class":120},[69,676,270],{"class":269},[69,678,284],{"class":120},[11,680,681,682,684],{},"In a server route, pass the user context as the third argument to ",[35,683,514],{},", built from the request:",[60,686,688],{"className":93,"code":687,"filename":457,"language":96,"meta":65,"style":65},"export default defineEventHandler(async (event) => {\n  const user = await requireUser(event);\n  const client = useConfigDirectorClient();\n  const showNewBanner = client.getValue(\"new-pricing-banner\", false, {\n    id: user.id,\n    traits: { plan: user.plan },\n  });\n  return { showNewBanner };\n});\n",[35,689,690,709,727,739,762,766,770,774,780],{"__ignoreMap":65},[69,691,692,694,696,698,700,702,705,707],{"class":71,"line":72},[69,693,104],{"class":103},[69,695,107],{"class":103},[69,697,468],{"class":110},[69,699,312],{"class":75},[69,701,473],{"class":103},[69,703,704],{"class":75}," (event) ",[69,706,479],{"class":103},[69,708,482],{"class":120},[69,710,711,713,716,718,721,724],{"class":71,"line":117},[69,712,487],{"class":103},[69,714,715],{"class":83}," user",[69,717,493],{"class":103},[69,719,720],{"class":103}," await",[69,722,723],{"class":110}," requireUser",[69,725,726],{"class":120},"(event);\n",[69,728,729,731,733,735,737],{"class":71,"line":133},[69,730,487],{"class":103},[69,732,490],{"class":83},[69,734,493],{"class":103},[69,736,496],{"class":110},[69,738,499],{"class":120},[69,740,741,743,745,747,749,751,753,755,757,759],{"class":71,"line":139},[69,742,487],{"class":103},[69,744,506],{"class":83},[69,746,493],{"class":103},[69,748,511],{"class":120},[69,750,514],{"class":110},[69,752,312],{"class":120},[69,754,315],{"class":79},[69,756,318],{"class":120},[69,758,53],{"class":83},[69,760,761],{"class":120},", {\n",[69,763,764],{"class":71,"line":145},[69,765,655],{"class":120},[69,767,768],{"class":71,"line":151},[69,769,660],{"class":120},[69,771,772],{"class":71,"line":162},[69,773,665],{"class":120},[69,775,776,778],{"class":71,"line":168},[69,777,529],{"class":103},[69,779,532],{"class":120},[69,781,782,784,786],{"class":71,"line":174},[69,783,537],{"class":120},[69,785,540],{"class":75},[69,787,210],{"class":120},[11,789,790,791,793,794,796,797,799,800,803,804,806],{},"Now add a targeting rule in the dashboard: ",[35,792,37],{}," is ",[35,795,435],{}," when ",[35,798,633],{}," equals ",[35,801,802],{},"free",", and ",[35,805,53],{}," otherwise. Or roll it out to 20 percent of users by ID and raise the percentage over the week. The rule saves, and both the server and the browser start applying it on their next evaluation. The browser re-evaluates on its own as soon as the context changes, so a user who signs in sees their own version of the page without a reload.",[11,808,809],{},"Before saving a rule, the tester on the targeting rules tab shows which rule a given context matches and why, so you can check the free-plan rule against a real user ID before anyone else does.",[18,811,813],{"id":812},"beyond-a-boolean","Beyond a boolean",[11,815,816,817,822],{},"Nothing above is specific to feature flags. The same composable reads any config type: a number for a page size, a string for a headline, a JSON document for a whole section of the page. Those configs can carry constraints of your choosing, so a page size can be an integer between 10 and 100 and the dashboard refuses anything else. The ",[26,818,821],{"href":819,"rel":820,"target":31},"https:\u002F\u002Fwww.configdirector.com\u002Fblog\u002Fwhy-your-feature-flag-service-should-validate-values",[30],"earlier post on validation"," explains why that belongs in the service and not in your code.",[11,824,825,826,432,828,830,831,239],{},"When you test the components that read these configs, the SDK's testing entry point gives you the real client connected to an in-memory server your test controls, so a test can set ",[35,827,37],{},[35,829,435],{},", mount the page, and assert the banner rendered, with no network and no mocks of the composable. The ",[26,832,835],{"href":833,"rel":834,"target":31},"https:\u002F\u002Fdocs.configdirector.com\u002Fsdks\u002Fmeta\u002Fnuxt#test-your-components",[30],"docs cover the setup",[18,837,839],{"id":838},"try-it","Try it",[11,841,249,842,847],{},[26,843,846],{"href":844,"rel":845,"target":31},"https:\u002F\u002Fdocs.configdirector.com\u002Fsdks\u002Fmeta\u002Fnuxt",[30],"Nuxt SDK docs"," have every option: log levels, polling intervals, the startup hold, and client and server hooks. The module supports Nuxt 3.7 and later. The Free option needs no credit card and includes the SDK, streaming, targeting rules, and the alerts that tell you when code and configuration disagree.",[849,850,851],"style",{},"html pre.shiki code .sQJ0q, html code.shiki .sQJ0q{--shiki-light:#953800;--shiki-default:#953800;--shiki-dark:#FFA657}html pre.shiki code .s-BGO, html code.shiki .s-BGO{--shiki-light:#0A3069;--shiki-default:#0A3069;--shiki-dark:#A5D6FF}html pre.shiki code .sDkKp, html code.shiki .sDkKp{--shiki-light:#0550AE;--shiki-default:#0550AE;--shiki-dark:#79C0FF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}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 .s5nWI, html code.shiki .s5nWI{--shiki-light:#CF222E;--shiki-default:#CF222E;--shiki-dark:#FF7B72}html pre.shiki code .sd69k, html code.shiki .sd69k{--shiki-light:#8250DF;--shiki-default:#8250DF;--shiki-dark:#D2A8FF}html pre.shiki code .s_bOF, html code.shiki .s_bOF{--shiki-light:#1F2328;--shiki-default:#1F2328;--shiki-dark:#E6EDF3}html pre.shiki code .sfEsd, html code.shiki .sfEsd{--shiki-light:#116329;--shiki-default:#116329;--shiki-dark:#7EE787}",{"title":65,"searchDepth":117,"depth":117,"links":853},[854,855,856,857,858,859,860,861],{"id":20,"depth":117,"text":21},{"id":57,"depth":117,"text":58},{"id":245,"depth":117,"text":246},{"id":425,"depth":117,"text":426},{"id":445,"depth":117,"text":446},{"id":548,"depth":117,"text":549},{"id":812,"depth":117,"text":813},{"id":838,"depth":117,"text":839},"2026-10-07","Add a feature flag to a Nuxt app that evaluates during SSR, updates automatically in the browser, and is readable from server routes.",false,"md","\u002Fremote-config-for-nuxt-in-five-minutes-cover.webp",{},"\u002Fblog\u002Fremote-config-for-nuxt-in-five-minutes",{"title":5,"description":863},{"loc":868,"lastmod":871},"2026-10-07T20:02:43.000Z","blog\u002Fremote-config-for-nuxt-in-five-minutes","uT1hkhxqDeOL9oYRooA6fzEalGjGs3bL7N9E-HSPs80",1791403492345]