[{"data":1,"prerenderedAt":444},["ShallowReactive",2],{"content-\u002Fdocs\u002Fperf":3},{"id":4,"title":5,"body":6,"description":437,"extension":438,"meta":439,"navigation":346,"path":440,"seo":441,"stem":442,"__hash__":443},"docs\u002Fdocs\u002Fperf.md","Performance",{"type":7,"value":8,"toc":424},"minimark",[9,13,31,36,73,76,80,130,134,163,199,207,214,218,229,235,244,280,284,294,298,304,402,413,417,420],[10,11,5],"h1",{"id":12},"performance",[14,15,16,17,21,22,30],"p",{},"Notes on the hot paths — keystrokes, submits, validation, reset —\nand what to look at if a form starts feeling slow. CI runs the\nbenchmark suite under ",[18,19,20],"code",{},"bench\u002F"," on every PR with thresholds tracked\nin ",[23,24,28],"a",{"href":25,"rel":26},"https:\u002F\u002Fgithub.com\u002Fattaform\u002Fattaform\u002Ftree\u002Fmain\u002Fbench",[27],"nofollow",[18,29,20],{},".",[32,33,35],"h2",{"id":34},"hot-path-characteristics","Hot-path characteristics",[37,38,39,59,67],"ul",{},[40,41,42,46,47,50,51,58],"li",{},[43,44,45],"strong",{},"Keystrokes"," — the ",[18,48,49],{},"register"," → form-state path runs against a\nper-PR threshold; see ",[23,52,55],{"href":53,"rel":54},"https:\u002F\u002Fgithub.com\u002Fattaform\u002Fattaform\u002Fblob\u002Fmain\u002Fbench\u002Fkeystroke.bench.ts",[27],[18,56,57],{},"bench\u002Fkeystroke.bench.ts","\nfor the measured scenarios (100-leaf and 500-leaf forms,\nsingle-leaf mutation).",[40,60,61,66],{},[43,62,63],{},[18,64,65],{},"state.isDirty"," — iterates the tracked leaves with no\nper-leaf parse cost.",[40,68,69,72],{},[43,70,71],{},"Path resolution"," — dotted-string paths are LRU-cached (128\nentries), so repeat canonicalisation reduces to a map lookup.",[14,74,75],{},"For forms below a few hundred leaves, the hot paths typically\ndon't surface in profiling.",[32,77,79],{"id":78},"sizing-guidance","Sizing guidance",[81,82,83,96],"table",{},[84,85,86],"thead",{},[87,88,89,93],"tr",{},[90,91,92],"th",{},"Scale",[90,94,95],{},"Guidance",[97,98,99,108,118],"tbody",{},[87,100,101,105],{},[102,103,104],"td",{},"≤ 500 leaves",[102,106,107],{},"Default. No tuning needed.",[87,109,110,113],{},[102,111,112],{},"500 – 5,000 leaves",[102,114,115,116,30],{},"Still fine. Watch out for templates that render every leaf's ",[18,117,65],{},[87,119,120,123],{},[102,121,122],{},"5,000+ leaves",[102,124,125,126,129],{},"Consider splitting into sub-forms with distinct ",[18,127,128],{},"key","s. One giant schema is not what the library is optimised for.",[32,131,133],{"id":132},"array-helpers-are-on","Array helpers are O(N)",[14,135,136,139,140,139,143,139,146,139,149,139,152,155,156,162],{},[18,137,138],{},"append"," \u002F ",[18,141,142],{},"prepend",[18,144,145],{},"insert",[18,147,148],{},"remove",[18,150,151],{},"swap",[18,153,154],{},"move"," all\ncopy the target array before mutating. That's cheap in the common\ncase (dozens of items), fine at hundreds, but ",[43,157,158,159,161],{},"quadratic if you\nloop ",[18,160,138],{}," to seed a large list",". For a large seed, assign the\nwhole array in one shot:",[164,165,170],"pre",{"className":166,"code":167,"language":168,"meta":169,"style":169},"language-ts shiki shiki-themes github-light github-dark","form.setValue('items', preBuiltArray) \u002F\u002F O(N)\n","ts","",[18,171,172],{"__ignoreMap":169},[173,174,177,181,185,188,192,195],"span",{"class":175,"line":176},"line",1,[173,178,180],{"class":179},"sVt8B","form.",[173,182,184],{"class":183},"sScJk","setValue",[173,186,187],{"class":179},"(",[173,189,191],{"class":190},"sZZnC","'items'",[173,193,194],{"class":179},", preBuiltArray) ",[173,196,198],{"class":197},"sJ8bj","\u002F\u002F O(N)\n",[32,200,202,203,206],{"id":201},"keying-v-for-rows","Keying ",[18,204,205],{},"v-for"," rows",[14,208,209,210,213],{},"Use a stable per-row key — either an ID carried on the data or a\nclient-generated ",[18,211,212],{},"crypto.randomUUID()"," stored when you append.\nKeying by index re-renders more than necessary when rows move and\nflickers focus \u002F scroll state on reordered rows.",[32,215,217],{"id":216},"discriminated-unions-vs-plain-unions","Discriminated unions vs plain unions",[14,219,220,221,224,225,228],{},"Discriminated unions (",[18,222,223],{},"z.discriminatedUnion",") walk only the active\nbranch. Plain unions (",[18,226,227],{},"z.union",") walk every branch unconditionally\n— use a DU when you have a shared key.",[32,230,232,234],{"id":231},"stateisdirty-in-hot-templates",[18,233,65],{}," in hot templates",[14,236,237,239,240,243],{},[18,238,65],{}," is a whole-form aggregate — it invalidates whenever\nany tracked leaf's ",[18,241,242],{},"updatedAt"," ticks. If you render it in a hot\npath (e.g., a header that re-renders on every keystroke), derive a\nmore specific predicate instead:",[164,245,247],{"className":166,"code":246,"language":168,"meta":169,"style":169},"\u002F\u002F Faster than gating on the whole-form state.isDirty:\nconst isEmailDirty = computed(() => form.fields.email.dirty)\n",[18,248,249,254],{"__ignoreMap":169},[173,250,251],{"class":175,"line":176},[173,252,253],{"class":197},"\u002F\u002F Faster than gating on the whole-form state.isDirty:\n",[173,255,257,261,265,268,271,274,277],{"class":175,"line":256},2,[173,258,260],{"class":259},"szBVR","const",[173,262,264],{"class":263},"sj4cs"," isEmailDirty",[173,266,267],{"class":259}," =",[173,269,270],{"class":183}," computed",[173,272,273],{"class":179},"(() ",[173,275,276],{"class":259},"=>",[173,278,279],{"class":179}," form.fields.email.dirty)\n",[32,281,283],{"id":282},"reset-cost","Reset cost",[14,285,286,289,290,293],{},[18,287,288],{},"reset()"," is sub-millisecond on a 100-leaf form and a few\nmilliseconds at 500 leaves. ",[18,291,292],{},"resetField(path)"," scales with the\nsubtree — prefer it for localised reversions.",[32,295,297],{"id":296},"benching-your-own-form","Benching your own form",[14,299,300,301,303],{},"Clone the repo and drop a bench in ",[18,302,20],{},":",[164,305,307],{"className":166,"code":306,"language":168,"meta":169,"style":169},"import { bench, describe } from 'vitest'\nimport { z } from 'zod'\n\u002F\u002F import your form setup\n\ndescribe('my form — typical interaction', () => {\n  bench('the operation I care about', () => {\n    \u002F\u002F ...\n  })\n})\n",[18,308,309,323,335,341,348,367,384,390,396],{"__ignoreMap":169},[173,310,311,314,317,320],{"class":175,"line":176},[173,312,313],{"class":259},"import",[173,315,316],{"class":179}," { bench, describe } ",[173,318,319],{"class":259},"from",[173,321,322],{"class":190}," 'vitest'\n",[173,324,325,327,330,332],{"class":175,"line":256},[173,326,313],{"class":259},[173,328,329],{"class":179}," { z } ",[173,331,319],{"class":259},[173,333,334],{"class":190}," 'zod'\n",[173,336,338],{"class":175,"line":337},3,[173,339,340],{"class":197},"\u002F\u002F import your form setup\n",[173,342,344],{"class":175,"line":343},4,[173,345,347],{"emptyLinePlaceholder":346},true,"\n",[173,349,351,354,356,359,362,364],{"class":175,"line":350},5,[173,352,353],{"class":183},"describe",[173,355,187],{"class":179},[173,357,358],{"class":190},"'my form — typical interaction'",[173,360,361],{"class":179},", () ",[173,363,276],{"class":259},[173,365,366],{"class":179}," {\n",[173,368,370,373,375,378,380,382],{"class":175,"line":369},6,[173,371,372],{"class":183},"  bench",[173,374,187],{"class":179},[173,376,377],{"class":190},"'the operation I care about'",[173,379,361],{"class":179},[173,381,276],{"class":259},[173,383,366],{"class":179},[173,385,387],{"class":175,"line":386},7,[173,388,389],{"class":197},"    \u002F\u002F ...\n",[173,391,393],{"class":175,"line":392},8,[173,394,395],{"class":179},"  })\n",[173,397,399],{"class":175,"line":398},9,[173,400,401],{"class":179},"})\n",[14,403,404,405,408,409,412],{},"Run with ",[18,406,407],{},"pnpm bench",". The regression gate only fires on benches\nthat follow the ",[18,410,411],{},"old: \u002F new:"," pairing convention — informational\nbenches run without gating.",[32,414,416],{"id":415},"peer-dep-coverage","Peer-dep coverage",[14,418,419],{},"Per-PR CI covers Node 18 \u002F 20 \u002F 22 \u002F LTS against the devDep-pinned\npeer versions. A weekly workflow sweeps Vue 3.5 through 3.6, Vite\n5 \u002F 6, Nuxt 3.16 through Nuxt 4. Jobs fail independently — versions\nnot yet released surface as failed cells without blocking the main\nCI.",[421,422,423],"style",{},"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 .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 .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":169,"searchDepth":256,"depth":256,"links":425},[426,427,428,429,431,432,434,435,436],{"id":34,"depth":256,"text":35},{"id":78,"depth":256,"text":79},{"id":132,"depth":256,"text":133},{"id":201,"depth":256,"text":430},"Keying v-for rows",{"id":216,"depth":256,"text":217},{"id":231,"depth":256,"text":433},"state.isDirty in hot templates",{"id":282,"depth":256,"text":283},{"id":296,"depth":256,"text":297},{"id":415,"depth":256,"text":416},"Notes on the hot paths — keystrokes, submits, validation, reset —\nand what to look at if a form starts feeling slow. CI runs the\nbenchmark suite under bench\u002F on every PR with thresholds tracked\nin bench\u002F.","md",{},"\u002Fdocs\u002Fperf",{"title":5,"description":437},"docs\u002Fperf","sSHWAziNg9jMEwb6Uonh57-W7kdmdR-ILbywEouQli4",1777934135645]