[{"data":1,"prerenderedAt":734},["ShallowReactive",2],{"content-\u002Fdocs\u002Fserver-and-ssr\u002Fssr-nuxt":3},{"id":4,"title":5,"body":6,"description":712,"extension":713,"meta":714,"metaRows":715,"navigation":728,"path":729,"seo":730,"source":731,"stem":732,"__hash__":733},"docs\u002Fdocs\u002Fserver-and-ssr\u002Fssr-nuxt.md","SSR hydration: Nuxt",{"type":7,"value":8,"toc":699},"minimark",[9,13,25,28,37,42,97,161,176,180,292,295,299,313,357,384,439,443,453,537,545,549,554,584,588,597,652,656,665,669,677,681,695],[10,11,5],"h1",{"id":12},"ssr-hydration-nuxt",[14,15,16],"blockquote",{},[17,18,19,20,24],"p",{},"The fastest path to server-rendered form values that don't flicker on hydrate. Install the module, write ",[21,22,23],"code",{},"useForm"," normally, and the round-trip wires itself.",[26,27],"docs-meta-table",{},[17,29,30,31,36],{},"This page is code-only; SSR happens at the server runtime, and the docs site can't demo a hydration round-trip without bootstrapping a separate server. The setup is small enough to verify in your own Nuxt project; see ",[32,33,35],"a",{"href":34},"\u002Fdocs\u002Fserver-and-ssr\u002Fssr-bare-vue","SSR hydration: bare Vue"," for the equivalent without the Nuxt convenience.",[38,39,41],"h2",{"id":40},"nothing-to-wire","Nothing to wire",[43,44,49],"pre",{"className":45,"code":46,"language":47,"meta":48,"style":48},"language-ts shiki shiki-themes github-light github-dark","\u002F\u002F nuxt.config.ts\nexport default defineNuxtConfig({\n  modules: ['attaform\u002Fnuxt'],\n})\n","ts","",[21,50,51,60,78,91],{"__ignoreMap":48},[52,53,56],"span",{"class":54,"line":55},"line",1,[52,57,59],{"class":58},"sJ8bj","\u002F\u002F nuxt.config.ts\n",[52,61,63,67,70,74],{"class":54,"line":62},2,[52,64,66],{"class":65},"szBVR","export",[52,68,69],{"class":65}," default",[52,71,73],{"class":72},"sScJk"," defineNuxtConfig",[52,75,77],{"class":76},"sVt8B","({\n",[52,79,81,84,88],{"class":54,"line":80},3,[52,82,83],{"class":76},"  modules: [",[52,85,87],{"class":86},"sZZnC","'attaform\u002Fnuxt'",[52,89,90],{"class":76},"],\n",[52,92,94],{"class":54,"line":93},4,[52,95,96],{"class":76},"})\n",[43,98,102],{"className":99,"code":100,"language":101,"meta":48,"style":48},"language-vue shiki shiki-themes github-light github-dark","\u003Cscript setup lang=\"ts\">\n  const form = useForm({ schema, key: 'signup' })\n\u003C\u002Fscript>\n","vue",[21,103,104,128,152],{"__ignoreMap":48},[52,105,106,109,113,116,119,122,125],{"class":54,"line":55},[52,107,108],{"class":76},"\u003C",[52,110,112],{"class":111},"s9eBZ","script",[52,114,115],{"class":72}," setup",[52,117,118],{"class":72}," lang",[52,120,121],{"class":76},"=",[52,123,124],{"class":86},"\"ts\"",[52,126,127],{"class":76},">\n",[52,129,130,133,137,140,143,146,149],{"class":54,"line":62},[52,131,132],{"class":65},"  const",[52,134,136],{"class":135},"sj4cs"," form",[52,138,139],{"class":65}," =",[52,141,142],{"class":72}," useForm",[52,144,145],{"class":76},"({ schema, key: ",[52,147,148],{"class":86},"'signup'",[52,150,151],{"class":76}," })\n",[52,153,154,157,159],{"class":54,"line":80},[52,155,156],{"class":76},"\u003C\u002F",[52,158,112],{"class":111},[52,160,127],{"class":76},[17,162,163,164,167,168,171,172,175],{},"That's the whole setup. Values, errors, and field interaction flags (touched \u002F focused \u002F blurred \u002F connected) survive the server → client round-trip through ",[21,165,166],{},"nuxtApp.payload",". Need to peek? Open the rendered HTML and look for the Nuxt payload ",[21,169,170],{},"\u003Cscript>"," block; ",[21,173,174],{},"attaform"," is a top-level key.",[38,177,179],{"id":178},"what-crosses-the-wire","What crosses the wire",[181,182,183,199],"table",{},[184,185,186],"thead",{},[187,188,189,193,196],"tr",{},[190,191,192],"th",{},"Surface",[190,194,195],{},"Round-trips?",[190,197,198],{},"Notes",[200,201,202,216,228,256,268,282],"tbody",{},[187,203,204,210,213],{},[205,206,207],"td",{},[21,208,209],{},"form.values",[205,211,212],{},"✅",[205,214,215],{},"Whole tree, including nested objects and arrays.",[187,217,218,223,225],{},[205,219,220],{},[21,221,222],{},"errors",[205,224,212],{},[205,226,227],{},"Every entry in the error map, keyed by path.",[187,229,230,235,237],{},[205,231,232],{},[21,233,234],{},"fields",[205,236,212],{},[205,238,239,242,243,242,246,242,249,242,252,255],{},[21,240,241],{},"touched"," \u002F ",[21,244,245],{},"focused",[21,247,248],{},"blurred",[21,250,251],{},"connected",[21,253,254],{},"updatedAt"," per path.",[187,257,258,263,265],{},[205,259,260],{},[21,261,262],{},"blankPaths",[205,264,212],{},[205,266,267],{},"Numeric-blank state survives the boundary.",[187,269,270,276,279],{},[205,271,272,275],{},[21,273,274],{},"history"," chain",[205,277,278],{},"❌",[205,280,281],{},"Each tab walks its own undo timeline.",[187,283,284,287,289],{},[205,285,286],{},"Validation in-flight state",[205,288,278],{},[205,290,291],{},"Re-runs locally on the client.",[17,293,294],{},"The client-side form is identical to the server one; no second round of validation kicks in unless the user interacts.",[38,296,298],{"id":297},"auto-imports","Auto-imports",[17,300,301,302,304,305,308,309,312],{},"The Nuxt module auto-imports ",[21,303,23],{}," from the unified entry point. No ",[21,306,307],{},"import"," statement needed in ",[21,310,311],{},"\u003Cscript setup>",":",[43,314,315],{"className":99,"code":100,"language":101,"meta":48,"style":48},[21,316,317,333,349],{"__ignoreMap":48},[52,318,319,321,323,325,327,329,331],{"class":54,"line":55},[52,320,108],{"class":76},[52,322,112],{"class":111},[52,324,115],{"class":72},[52,326,118],{"class":72},[52,328,121],{"class":76},[52,330,124],{"class":86},[52,332,127],{"class":76},[52,334,335,337,339,341,343,345,347],{"class":54,"line":62},[52,336,132],{"class":65},[52,338,136],{"class":135},[52,340,139],{"class":65},[52,342,142],{"class":72},[52,344,145],{"class":76},[52,346,148],{"class":86},[52,348,151],{"class":76},[52,350,351,353,355],{"class":54,"line":80},[52,352,156],{"class":76},[52,354,112],{"class":111},[52,356,127],{"class":76},[17,358,359,360,362,363,366,367,370,371,370,374,370,377,370,380,383],{},"For the Zod-typed wrapper (",[21,361,23],{}," from ",[21,364,365],{},"attaform\u002Fzod","), or for any other helper (",[21,368,369],{},"injectForm",", ",[21,372,373],{},"useWizard",[21,375,376],{},"useRegister",[21,378,379],{},"unset",[21,381,382],{},"defaultDisplayState","), import explicitly:",[43,385,387],{"className":99,"code":386,"language":101,"meta":48,"style":48},"\u003Cscript setup lang=\"ts\">\n  import { injectForm, useWizard } from 'attaform\u002Fzod'\n  import { defaultDisplayState, unset } from 'attaform'\n\u003C\u002Fscript>\n",[21,388,389,405,419,431],{"__ignoreMap":48},[52,390,391,393,395,397,399,401,403],{"class":54,"line":55},[52,392,108],{"class":76},[52,394,112],{"class":111},[52,396,115],{"class":72},[52,398,118],{"class":72},[52,400,121],{"class":76},[52,402,124],{"class":86},[52,404,127],{"class":76},[52,406,407,410,413,416],{"class":54,"line":62},[52,408,409],{"class":65},"  import",[52,411,412],{"class":76}," { injectForm, useWizard } ",[52,414,415],{"class":65},"from",[52,417,418],{"class":86}," 'attaform\u002Fzod'\n",[52,420,421,423,426,428],{"class":54,"line":80},[52,422,409],{"class":65},[52,424,425],{"class":76}," { defaultDisplayState, unset } ",[52,427,415],{"class":65},[52,429,430],{"class":86}," 'attaform'\n",[52,432,433,435,437],{"class":54,"line":93},[52,434,156],{"class":76},[52,436,112],{"class":111},[52,438,127],{"class":76},[38,440,442],{"id":441},"app-wide-defaults-under-nuxt","App-wide defaults under Nuxt",[17,444,445,446,449,450,312],{},"The Nuxt module surfaces the same ",[21,447,448],{},"AttaformDefaults"," you'd pass to ",[21,451,452],{},"createAttaform({ defaults })",[43,454,456],{"className":45,"code":455,"language":47,"meta":48,"style":48},"export default defineNuxtConfig({\n  modules: ['attaform\u002Fnuxt'],\n  attaform: {\n    defaults: {\n      validateOn: 'change',\n      debounceMs: 100,\n      onInvalidSubmit: 'focus-first-error',\n    },\n  },\n})\n",[21,457,458,468,476,481,486,498,509,520,526,532],{"__ignoreMap":48},[52,459,460,462,464,466],{"class":54,"line":55},[52,461,66],{"class":65},[52,463,69],{"class":65},[52,465,73],{"class":72},[52,467,77],{"class":76},[52,469,470,472,474],{"class":54,"line":62},[52,471,83],{"class":76},[52,473,87],{"class":86},[52,475,90],{"class":76},[52,477,478],{"class":54,"line":80},[52,479,480],{"class":76},"  attaform: {\n",[52,482,483],{"class":54,"line":93},[52,484,485],{"class":76},"    defaults: {\n",[52,487,489,492,495],{"class":54,"line":488},5,[52,490,491],{"class":76},"      validateOn: ",[52,493,494],{"class":86},"'change'",[52,496,497],{"class":76},",\n",[52,499,501,504,507],{"class":54,"line":500},6,[52,502,503],{"class":76},"      debounceMs: ",[52,505,506],{"class":135},"100",[52,508,497],{"class":76},[52,510,512,515,518],{"class":54,"line":511},7,[52,513,514],{"class":76},"      onInvalidSubmit: ",[52,516,517],{"class":86},"'focus-first-error'",[52,519,497],{"class":76},[52,521,523],{"class":54,"line":522},8,[52,524,525],{"class":76},"    },\n",[52,527,529],{"class":54,"line":528},9,[52,530,531],{"class":76},"  },\n",[52,533,535],{"class":54,"line":534},10,[52,536,96],{"class":76},[17,538,539,540,544],{},"See ",[32,541,543],{"href":542},"\u002Fdocs\u002Fcross-cutting-state\u002Fapp-defaults","App-wide defaults"," for the full option list and merge semantics.",[38,546,548],{"id":547},"common-issues","Common issues",[550,551,553],"h3",{"id":552},"the-form-is-empty-on-the-client-even-though-the-server-rendered-values","\"The form is empty on the client even though the server rendered values.\"",[555,556,557,573],"ul",{},[558,559,560,561,564,565,568,569,572],"li",{},"Does the form's ",[21,562,563],{},"key"," match between server and client? Hard-code it as a string literal; ",[21,566,567],{},"uuidv4()"," or ",[21,570,571],{},"Math.random()"," produces a fresh key per render and breaks the round-trip lookup.",[558,574,575,576,579,580,583],{},"Was the form created in ",[21,577,578],{},"setup","? Forms created in ",[21,581,582],{},"onMounted"," or event handlers aren't in the SSR snapshot.",[550,585,587],{"id":586},"field-errors-from-the-server-disappear-on-first-interaction","\"Field errors from the server disappear on first interaction.\"",[17,589,590,591,568,594,312],{},"By design. Any mutation re-runs validation, which can replace the errors. To keep server-provided errors around until the user dirties the field, gate the display on ",[21,592,593],{},"form.fields.\u003Cpath>.touched",[21,595,596],{},"form.meta.dirty",[43,598,600],{"className":99,"code":599,"language":101,"meta":48,"style":48},"\u003Csmall v-if=\"form.errors.email?.[0] && !form.fields.email.touched\">\n  {{ form.errors.email[0].message }}\n\u003C\u002Fsmall>\n",[21,601,602,639,644],{"__ignoreMap":48},[52,603,604,606,609,612,614,617,620,623,626,629,632,635,637],{"class":54,"line":55},[52,605,108],{"class":76},[52,607,608],{"class":111},"small",[52,610,611],{"class":65}," v-if",[52,613,121],{"class":76},[52,615,616],{"class":86},"\"",[52,618,619],{"class":76},"form.errors.email?.[",[52,621,622],{"class":135},"0",[52,624,625],{"class":76},"] ",[52,627,628],{"class":65},"&&",[52,630,631],{"class":65}," !",[52,633,634],{"class":76},"form.fields.email.touched",[52,636,616],{"class":86},[52,638,127],{"class":76},[52,640,641],{"class":54,"line":62},[52,642,643],{"class":76},"  {{ form.errors.email[0].message }}\n",[52,645,646,648,650],{"class":54,"line":80},[52,647,156],{"class":76},[52,649,608],{"class":111},[52,651,127],{"class":76},[550,653,655],{"id":654},"some-fields-look-right-others-dont","\"Some fields look right, others don't.\"",[17,657,658,659,661,662,664],{},"Forms created in ",[21,660,582],{}," or event handlers aren't in the SSR snapshot. Create forms during ",[21,663,578],{}," so the server sees them.",[38,666,668],{"id":667},"devtools-panel","DevTools panel",[17,670,671,672,676],{},"The Nuxt module auto-wires the ",[32,673,675],{"href":674},"\u002Fdocs\u002Fdevtools-and-debugging\u002Fdevtools-panel","Attaform DevTools panel"," into the Nuxt DevTools sidebar. The panel inspects every registered form including the SSR-rendered ones, useful for confirming the server-rendered shape matches your expectation before the user touches anything.",[38,678,680],{"id":679},"where-to-next","Where to next",[555,682,683,688],{},[558,684,685,687],{},[32,686,35],{"href":34},": the same round-trip without the Nuxt module, when you're not on Nuxt.",[558,689,690,694],{},[32,691,693],{"href":692},"\u002Fdocs\u002Fserver-and-ssr\u002Fperformance","Performance",": what SSR hydration costs in practice.",[696,697,698],"style",{},"html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}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 .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":48,"searchDepth":62,"depth":62,"links":700},[701,702,703,704,705,710,711],{"id":40,"depth":62,"text":41},{"id":178,"depth":62,"text":179},{"id":297,"depth":62,"text":298},{"id":441,"depth":62,"text":442},{"id":547,"depth":62,"text":548,"children":706},[707,708,709],{"id":552,"depth":80,"text":553},{"id":586,"depth":80,"text":587},{"id":654,"depth":80,"text":655},{"id":667,"depth":62,"text":668},{"id":679,"depth":62,"text":680},"Server-rendered Vue 3 forms round-trip values, errors, and field flags to the client automatically via attaform\u002Fnuxt. No hydration flicker, no manual wiring.","md",{},[716,719,722,725],{"label":717,"value":718},"Category","Integration",{"label":720,"value":721,"kind":21},"Setup","modules: [attaform\u002Fnuxt]",{"label":723,"value":724},"Transport","nuxtApp.payload (top-level key)",{"label":726,"value":727},"What rides","values · errors · field flags",true,"\u002Fdocs\u002Fserver-and-ssr\u002Fssr-nuxt",{"title":5,"description":712},null,"docs\u002Fserver-and-ssr\u002Fssr-nuxt","_0spiBcFc5fIjMxqiPgWELxt2m8wVMEJ_PftfN4sZrQ",1781745874653]