[{"data":1,"prerenderedAt":676},["ShallowReactive",2],{"content-\u002Fdocs\u002Fpersistence\u002Fimperative":3},{"id":4,"title":5,"body":6,"description":655,"extension":656,"meta":657,"metaRows":658,"navigation":238,"path":671,"seo":672,"source":673,"stem":674,"__hash__":675},"docs\u002Fdocs\u002Fpersistence\u002Fimperative.md","Imperative persistence",{"type":7,"value":8,"toc":649},"minimark",[9,13,29,32,42,46,53,139,142,189,192,436,439,445,482,487,497,538,542,559,609,612,616,645],[10,11,5],"h1",{"id":12},"imperative-persistence",[14,15,16],"blockquote",{},[17,18,19,20,24,25,28],"p",{},"Two methods, one job each. ",[21,22,23],"code",{},"form.persist(path)"," flushes the current value at a path, ",[21,26,27],{},"form.clearPersistedDraft()"," wipes the backend entry. Both are async, both no-op cleanly when persistence isn't configured.",[30,31],"docs-meta-table",{},[17,33,34,35,38,39,41],{},"Neither field in this demo opts into persistence via ",[21,36,37],{},"register",", but ",[21,40,23],{}," writes them anyway. That's the bypass: the method ignores the per-field opt-in gate so a \"Save draft\" button can capture whatever's on screen, including fields that don't otherwise persist. The clear buttons demonstrate the per-path and whole-form variants.",[43,44],"docs-demo",{"label":45,"slug":12},"Imperative Demo",[47,48,50],"h2",{"id":49},"formpersistpath-options",[21,51,52],{},"form.persist(path, options?)",[54,55,60],"pre",{"className":56,"code":57,"language":58,"meta":59,"style":59},"language-ts shiki shiki-themes github-light github-dark","await form.persist('email') \u002F\u002F flush just one path's subtree\nawait form.persist('step1') \u002F\u002F works on object subtrees too\nawait form.persist('password', { acknowledgeSensitive: true }) \u002F\u002F sensitive paths\n","ts","",[21,61,62,93,112],{"__ignoreMap":59},[63,64,67,71,75,79,82,86,89],"span",{"class":65,"line":66},"line",1,[63,68,70],{"class":69},"szBVR","await",[63,72,74],{"class":73},"sVt8B"," form.",[63,76,78],{"class":77},"sScJk","persist",[63,80,81],{"class":73},"(",[63,83,85],{"class":84},"sZZnC","'email'",[63,87,88],{"class":73},") ",[63,90,92],{"class":91},"sJ8bj","\u002F\u002F flush just one path's subtree\n",[63,94,96,98,100,102,104,107,109],{"class":65,"line":95},2,[63,97,70],{"class":69},[63,99,74],{"class":73},[63,101,78],{"class":77},[63,103,81],{"class":73},[63,105,106],{"class":84},"'step1'",[63,108,88],{"class":73},[63,110,111],{"class":91},"\u002F\u002F works on object subtrees too\n",[63,113,115,117,119,121,123,126,129,133,136],{"class":65,"line":114},3,[63,116,70],{"class":69},[63,118,74],{"class":73},[63,120,78],{"class":77},[63,122,81],{"class":73},[63,124,125],{"class":84},"'password'",[63,127,128],{"class":73},", { acknowledgeSensitive: ",[63,130,132],{"class":131},"sj4cs","true",[63,134,135],{"class":73}," }) ",[63,137,138],{"class":91},"\u002F\u002F sensitive paths\n",[17,140,141],{},"A one-shot read-merge-write that:",[143,144,145,157,163,169,179],"ul",{},[146,147,148,152,153,156],"li",{},[149,150,151],"strong",{},"Bypasses the per-element opt-in gate."," Use it when an explicit user action (\"Save draft\", ",[21,154,155],{},"beforeunload",", wizard section boundary) means \"save what's on screen now.\"",[146,158,159,162],{},[149,160,161],{},"Bypasses the debouncer."," Pending writes flush first.",[146,164,165,168],{},[149,166,167],{},"Preserves untouched paths in storage."," A path-scoped call merges into the existing envelope; it does not overwrite siblings.",[146,170,171,174,175,178],{},[149,172,173],{},"Warns and no-ops on heuristic-matched paths"," unless ",[21,176,177],{},"{ acknowledgeSensitive: true }"," is passed. Sensitive paths without acknowledgement are simply not written; the call resolves cleanly so a misconfig can't take down the app.",[146,180,181,184,185,188],{},[149,182,183],{},"No-ops silently"," when ",[21,186,187],{},"persist:"," isn't configured on the form. Adding a \"Save draft\" button to a non-persisted form is a no-cost call.",[17,190,191],{},"For \"save the whole form,\" iterate the top-level paths:",[54,193,195],{"className":56,"code":194,"language":58,"meta":59,"style":59},"const paths = ['title', 'body', 'tags'] as const\n\nasync function onSaveDraft() {\n  for (const p of paths) await form.persist(p)\n}\n\n\u002F\u002F beforeunload guard for a long wizard\nwindow.addEventListener('beforeunload', () => {\n  for (const p of paths) void form.persist(p)\n})\n\n\u002F\u002F Wizard step transition: only the current step's subtree\nasync function goToStep(n: number) {\n  await form.persist(`step${currentStep.value}`)\n  currentStep.value = n\n}\n",[21,196,197,234,240,254,283,289,294,300,323,347,353,358,364,389,419,431],{"__ignoreMap":59},[63,198,199,202,205,208,211,214,217,220,222,225,228,231],{"class":65,"line":66},[63,200,201],{"class":69},"const",[63,203,204],{"class":131}," paths",[63,206,207],{"class":69}," =",[63,209,210],{"class":73}," [",[63,212,213],{"class":84},"'title'",[63,215,216],{"class":73},", ",[63,218,219],{"class":84},"'body'",[63,221,216],{"class":73},[63,223,224],{"class":84},"'tags'",[63,226,227],{"class":73},"] ",[63,229,230],{"class":69},"as",[63,232,233],{"class":69}," const\n",[63,235,236],{"class":65,"line":95},[63,237,239],{"emptyLinePlaceholder":238},true,"\n",[63,241,242,245,248,251],{"class":65,"line":114},[63,243,244],{"class":69},"async",[63,246,247],{"class":69}," function",[63,249,250],{"class":77}," onSaveDraft",[63,252,253],{"class":73},"() {\n",[63,255,257,260,263,265,268,271,274,276,278,280],{"class":65,"line":256},4,[63,258,259],{"class":69},"  for",[63,261,262],{"class":73}," (",[63,264,201],{"class":69},[63,266,267],{"class":131}," p",[63,269,270],{"class":69}," of",[63,272,273],{"class":73}," paths) ",[63,275,70],{"class":69},[63,277,74],{"class":73},[63,279,78],{"class":77},[63,281,282],{"class":73},"(p)\n",[63,284,286],{"class":65,"line":285},5,[63,287,288],{"class":73},"}\n",[63,290,292],{"class":65,"line":291},6,[63,293,239],{"emptyLinePlaceholder":238},[63,295,297],{"class":65,"line":296},7,[63,298,299],{"class":91},"\u002F\u002F beforeunload guard for a long wizard\n",[63,301,303,306,309,311,314,317,320],{"class":65,"line":302},8,[63,304,305],{"class":73},"window.",[63,307,308],{"class":77},"addEventListener",[63,310,81],{"class":73},[63,312,313],{"class":84},"'beforeunload'",[63,315,316],{"class":73},", () ",[63,318,319],{"class":69},"=>",[63,321,322],{"class":73}," {\n",[63,324,326,328,330,332,334,336,338,341,343,345],{"class":65,"line":325},9,[63,327,259],{"class":69},[63,329,262],{"class":73},[63,331,201],{"class":69},[63,333,267],{"class":131},[63,335,270],{"class":69},[63,337,273],{"class":73},[63,339,340],{"class":69},"void",[63,342,74],{"class":73},[63,344,78],{"class":77},[63,346,282],{"class":73},[63,348,350],{"class":65,"line":349},10,[63,351,352],{"class":73},"})\n",[63,354,356],{"class":65,"line":355},11,[63,357,239],{"emptyLinePlaceholder":238},[63,359,361],{"class":65,"line":360},12,[63,362,363],{"class":91},"\u002F\u002F Wizard step transition: only the current step's subtree\n",[63,365,367,369,371,374,376,380,383,386],{"class":65,"line":366},13,[63,368,244],{"class":69},[63,370,247],{"class":69},[63,372,373],{"class":77}," goToStep",[63,375,81],{"class":73},[63,377,379],{"class":378},"s4XuR","n",[63,381,382],{"class":69},":",[63,384,385],{"class":131}," number",[63,387,388],{"class":73},") {\n",[63,390,392,395,397,399,401,404,407,410,413,416],{"class":65,"line":391},14,[63,393,394],{"class":69},"  await",[63,396,74],{"class":73},[63,398,78],{"class":77},[63,400,81],{"class":73},[63,402,403],{"class":84},"`step${",[63,405,406],{"class":73},"currentStep",[63,408,409],{"class":84},".",[63,411,412],{"class":73},"value",[63,414,415],{"class":84},"}`",[63,417,418],{"class":73},")\n",[63,420,422,425,428],{"class":65,"line":421},15,[63,423,424],{"class":73},"  currentStep.value ",[63,426,427],{"class":69},"=",[63,429,430],{"class":73}," n\n",[63,432,434],{"class":65,"line":433},16,[63,435,288],{"class":73},[17,437,438],{},"The explicit-path signature is deliberate; \"save what's on screen\" is rarely literally the whole form. The path-scoped call gives you a precise checkpoint without accidentally promoting unfocused fields into storage.",[47,440,442],{"id":441},"formclearpersisteddraftpath",[21,443,444],{},"form.clearPersistedDraft(path?)",[54,446,448],{"className":56,"code":447,"language":58,"meta":59,"style":59},"await form.clearPersistedDraft() \u002F\u002F wipe the whole envelope\nawait form.clearPersistedDraft('email') \u002F\u002F wipe one path's slot\n",[21,449,450,465],{"__ignoreMap":59},[63,451,452,454,456,459,462],{"class":65,"line":66},[63,453,70],{"class":69},[63,455,74],{"class":73},[63,457,458],{"class":77},"clearPersistedDraft",[63,460,461],{"class":73},"() ",[63,463,464],{"class":91},"\u002F\u002F wipe the whole envelope\n",[63,466,467,469,471,473,475,477,479],{"class":65,"line":95},[63,468,70],{"class":69},[63,470,74],{"class":73},[63,472,458],{"class":77},[63,474,81],{"class":73},[63,476,85],{"class":84},[63,478,88],{"class":73},[63,480,481],{"class":91},"\u002F\u002F wipe one path's slot\n",[17,483,484,486],{},[21,485,458],{}," does NOT touch in-memory form state, and does NOT disable any active opt-ins; future writes from opted-in bindings will re-populate the storage entry.",[17,488,489,490,493,494,382],{},"For \"wipe both in-memory and on-disk,\" call ",[21,491,492],{},"reset()"," after ",[21,495,496],{},"clearPersistedDraft()",[54,498,500],{"className":56,"code":499,"language":58,"meta":59,"style":59},"async function startFresh() {\n  await form.clearPersistedDraft()\n  form.reset()\n}\n",[21,501,502,513,524,534],{"__ignoreMap":59},[63,503,504,506,508,511],{"class":65,"line":66},[63,505,244],{"class":69},[63,507,247],{"class":69},[63,509,510],{"class":77}," startFresh",[63,512,253],{"class":73},[63,514,515,517,519,521],{"class":65,"line":95},[63,516,394],{"class":69},[63,518,74],{"class":73},[63,520,458],{"class":77},[63,522,523],{"class":73},"()\n",[63,525,526,529,532],{"class":65,"line":114},[63,527,528],{"class":73},"  form.",[63,530,531],{"class":77},"reset",[63,533,523],{"class":73},[63,535,536],{"class":65,"line":256},[63,537,288],{"class":73},[47,539,541],{"id":540},"auto-clear-on-submit","Auto-clear on submit",[17,543,544,545,547,548,551,552,555,556,558],{},"By default, a successful submit fires ",[21,546,496],{}," automatically. ",[21,549,550],{},"handleSubmit","'s success callback resolving is the signal to drop the draft. Set ",[21,553,554],{},"clearOnSubmitSuccess: false"," on the form's ",[21,557,78],{}," config to opt out (review pages, retry-prone APIs that want to keep the draft until a confirmation lands):",[54,560,562],{"className":56,"code":561,"language":58,"meta":59,"style":59},"useForm({\n  schema,\n  key: 'signup',\n  persist: { storage: 'local', clearOnSubmitSuccess: false },\n})\n",[21,563,564,572,577,588,605],{"__ignoreMap":59},[63,565,566,569],{"class":65,"line":66},[63,567,568],{"class":77},"useForm",[63,570,571],{"class":73},"({\n",[63,573,574],{"class":65,"line":95},[63,575,576],{"class":73},"  schema,\n",[63,578,579,582,585],{"class":65,"line":114},[63,580,581],{"class":73},"  key: ",[63,583,584],{"class":84},"'signup'",[63,586,587],{"class":73},",\n",[63,589,590,593,596,599,602],{"class":65,"line":256},[63,591,592],{"class":73},"  persist: { storage: ",[63,594,595],{"class":84},"'local'",[63,597,598],{"class":73},", clearOnSubmitSuccess: ",[63,600,601],{"class":131},"false",[63,603,604],{"class":73}," },\n",[63,606,607],{"class":65,"line":285},[63,608,352],{"class":73},[17,610,611],{},"The default keeps the on-disk surface aligned with the user's mental model: \"I submitted, the draft is done.\" Override only when there's a concrete reason to keep it.",[47,613,615],{"id":614},"where-to-next","Where to next",[143,617,618,630,637],{},[146,619,620,625,626,629],{},[621,622,624],"a",{"href":623},"\u002Fdocs\u002Fpersistence\u002Fper-field-opt-in","Per-field opt-in",": the declarative opt-in ",[21,627,628],{},"form.persist()"," bypasses.",[146,631,632,636],{},[621,633,635],{"href":634},"\u002Fdocs\u002Fpersistence\u002Fedge-cases","Edge cases & hydration",": what happens when imperative writes race the debouncer, hydration timing, cross-tab.",[146,638,639,644],{},[621,640,642],{"href":641},"\u002Fdocs\u002Fsubmitting\u002Fhandle-submit",[21,643,550],{},": the success path that owns the auto-clear.",[646,647,648],"style",{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}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 .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}",{"title":59,"searchDepth":95,"depth":95,"links":650},[651,652,653,654],{"id":49,"depth":95,"text":52},{"id":441,"depth":95,"text":444},{"id":540,"depth":95,"text":541},{"id":614,"depth":95,"text":615},"form.persist() writes the current snapshot regardless of per-field opt-ins; form.clearPersistedDraft() wipes the backend entry. Use them for Save Draft buttons, beforeunload handlers, and explicit cleanup.","md",{},[659,662,665,668],{"label":660,"value":661},"Category","Return methods",{"label":663,"value":664,"kind":21},"form.persist","(path, options?) => Promise\u003Cvoid>",{"label":666,"value":667,"kind":21},"form.clearPersistedDraft","(path?) => Promise\u003Cvoid>",{"label":669,"value":670},"Bypass per-field gate?","form.persist yes; clearPersistedDraft N\u002FA.","\u002Fdocs\u002Fpersistence\u002Fimperative",{"title":5,"description":655},null,"docs\u002Fpersistence\u002Fimperative","tafTvkh_9T7kRON3D4juaq4b80ufSSp-hy28PTbUsYI",1780949761015]